summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c3510
1 files changed, 2181 insertions, 1329 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 5273ade71117..44029ceb8f77 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2022 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
#include <linux/kernel.h>
+#include <linux/fips.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
@@ -22,28 +23,36 @@
#include "mvm.h"
#include "sta.h"
#include "time-event.h"
-#include "iwl-eeprom-parse.h"
+#include "iwl-nvm-utils.h"
#include "iwl-phy-db.h"
#include "testmode.h"
#include "fw/error-dump.h"
#include "iwl-prph.h"
#include "iwl-nvm-parse.h"
+#include "time-sync.h"
+
+#define IWL_MVM_LIMITS(ap) \
+ { \
+ .max = 1, \
+ .types = BIT(NL80211_IFTYPE_STATION), \
+ }, \
+ { \
+ .max = 1, \
+ .types = ap | \
+ BIT(NL80211_IFTYPE_P2P_CLIENT) | \
+ BIT(NL80211_IFTYPE_P2P_GO), \
+ }, \
+ { \
+ .max = 1, \
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE), \
+ }
static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
- },
+ IWL_MVM_LIMITS(0)
+};
+
+static const struct ieee80211_iface_limit iwl_mvm_limits_ap[] = {
+ IWL_MVM_LIMITS(BIT(NL80211_IFTYPE_AP))
};
static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
@@ -53,10 +62,16 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
.limits = iwl_mvm_limits,
.n_limits = ARRAY_SIZE(iwl_mvm_limits),
},
+ {
+ .num_different_channels = 1,
+ .max_interfaces = 3,
+ .limits = iwl_mvm_limits_ap,
+ .n_limits = ARRAY_SIZE(iwl_mvm_limits_ap),
+ },
};
static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = {
- .max_peers = IWL_MVM_TOF_MAX_APS,
+ .max_peers = IWL_TOF_MAX_APS,
.report_ap_tsf = 1,
.randomize_mac_addr = 1,
@@ -107,7 +122,7 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
struct ieee80211_regdomain *regd = NULL;
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mcc_update_resp *resp;
+ struct iwl_mcc_update_resp_v8 *resp;
u8 resp_ver;
IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
@@ -132,12 +147,12 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
MCC_UPDATE_CMD, 0);
IWL_DEBUG_LAR(mvm, "MCC update response version: %d\n", resp_ver);
- regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
+ regd = iwl_parse_nvm_mcc_info(mvm->trans,
__le32_to_cpu(resp->n_channels),
resp->channels,
__le16_to_cpu(resp->mcc),
__le16_to_cpu(resp->geo_info),
- __le16_to_cpu(resp->cap), resp_ver);
+ le32_to_cpu(resp->cap), resp_ver);
/* Store the return source id */
src_id = resp->source_id;
if (IS_ERR_OR_NULL(regd)) {
@@ -185,7 +200,7 @@ struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
MCC_SOURCE_OLD_FW, changed);
}
-int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm, bool force_regd_sync)
{
enum iwl_mcc_source used_src;
struct ieee80211_regdomain *regd;
@@ -212,8 +227,10 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
if (IS_ERR_OR_NULL(regd))
return -EIO;
- /* update cfg80211 if the regdomain was changed */
- if (changed)
+ /* update cfg80211 if the regdomain was changed or the caller explicitly
+ * asked to update regdomain
+ */
+ if (changed || force_regd_sync)
ret = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd);
else
ret = 0;
@@ -222,23 +239,62 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
return ret;
}
+/* Each capability added here should also be add to tm_if_types_ext_capa_sta */
static const u8 he_if_types_ext_capa_sta[] = {
[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
- [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
+ WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
+ [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
+};
+
+static const u8 tm_if_types_ext_capa_sta[] = {
+ [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+ [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT |
+ WLAN_EXT_CAPA3_TIMING_MEASUREMENT_SUPPORT,
+ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
+ WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
+ [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
+ [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT,
};
-static const struct wiphy_iftype_ext_capab he_iftypes_ext_capa[] = {
+/* Additional interface types for which extended capabilities are
+ * specified separately
+ */
+
+#define IWL_MVM_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
+ IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \
+ __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
+ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
+ __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
+#define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \
+ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
+ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
+ IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT)
+
+static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = {
{
.iftype = NL80211_IFTYPE_STATION,
.extended_capabilities = he_if_types_ext_capa_sta,
.extended_capabilities_mask = he_if_types_ext_capa_sta,
.extended_capabilities_len = sizeof(he_if_types_ext_capa_sta),
+ /* relevant only if EHT is supported */
+ .eml_capabilities = IWL_MVM_EMLSR_CAPA,
+ .mld_capa_and_ops = IWL_MVM_MLD_CAPA_OPS,
+ },
+ {
+ .iftype = NL80211_IFTYPE_STATION,
+ .extended_capabilities = tm_if_types_ext_capa_sta,
+ .extended_capabilities_mask = tm_if_types_ext_capa_sta,
+ .extended_capabilities_len = sizeof(tm_if_types_ext_capa_sta),
+ /* relevant only if EHT is supported */
+ .eml_capabilities = IWL_MVM_EMLSR_CAPA,
+ .mld_capa_and_ops = IWL_MVM_MLD_CAPA_OPS,
},
};
-static int
-iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+int iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, int radio_idx,
+ u32 *tx_ant, u32 *rx_ant)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
*tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
@@ -246,6 +302,32 @@ 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, int radio_idx, u32 tx_ant,
+ u32 rx_ant)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /* This has been tested on those devices only */
+ if (mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_9000 &&
+ mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_22000 &&
+ mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_AX210)
+ return -EOPNOTSUPP;
+
+ if (!mvm->nvm_data)
+ return -EBUSY;
+
+ /* mac80211 ensures the device is not started,
+ * so the firmware cannot be running
+ */
+
+ mvm->set_tx_ant = tx_ant;
+ mvm->set_rx_ant = rx_ant;
+
+ iwl_reinit_cab(mvm->trans, mvm->nvm_data, tx_ant, rx_ant, mvm->fw);
+
+ return 0;
+}
+
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
{
struct ieee80211_hw *hw = mvm->hw;
@@ -260,6 +342,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
bool unified = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
#endif
+ u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
+ u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0);
/* Tell mac80211 our characteristics */
ieee80211_hw_set(hw, SIGNAL_DBM);
@@ -269,17 +353,31 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, SUPPORTS_PS);
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
- ieee80211_hw_set(hw, TIMING_BEACON_ONLY);
ieee80211_hw_set(hw, CONNECTION_MONITOR);
ieee80211_hw_set(hw, CHANCTX_STA_CSA);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
- ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
ieee80211_hw_set(hw, STA_MMPDU_TXQ);
+
+ /* Set this early since we need to have it for the check below */
+ if (mvm->mld_api_is_used && mvm->nvm_data->sku_cap_11be_enable &&
+ !iwlwifi_mod_params.disable_11ax &&
+ !iwlwifi_mod_params.disable_11be) {
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
+ /* we handle this already earlier, but need it for MLO */
+ ieee80211_hw_set(hw, HANDLES_QUIET_CSA);
+ }
+
+ /* With MLD FW API, it tracks timing by itself,
+ * no need for any timing from the host
+ */
+ if (!mvm->mld_api_is_used)
+ ieee80211_hw_set(hw, TIMING_BEACON_ONLY);
+
/*
* On older devices, enabling TX A-MSDU occasionally leads to
* something getting messed up, the command read from the FIFO
@@ -291,7 +389,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
* for older devices. We also don't see this issue on any newer
* devices.
*/
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000)
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000)
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, TX_FRAG_LIST);
@@ -300,7 +398,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
}
- if (iwl_mvm_has_new_rx_api(mvm))
+ /* We want to use the mac80211's reorder buffer for 9000 */
+ if (iwl_mvm_has_new_rx_api(mvm) &&
+ mvm->trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_9000)
ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
if (fw_has_capa(&mvm->fw->ucode_capa,
@@ -315,10 +415,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
return -EINVAL;
}
- if (mvm->trans->num_rx_queues > 1)
+ if (mvm->trans->info.num_rxqs > 1)
ieee80211_hw_set(hw, USES_RSS);
- if (mvm->trans->max_skb_frags)
+ if (mvm->trans->info.max_skb_frags)
hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
hw->queues = IEEE80211_NUM_ACS;
@@ -339,7 +439,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
- hw->max_tx_fragments = mvm->trans->max_skb_frags;
+ hw->max_tx_fragments = mvm->trans->info.max_skb_frags;
BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6);
memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
@@ -362,7 +462,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IWL_ERR(mvm,
"iwlmvm doesn't allow to disable BT Coex, check bt_coex_active module parameter\n");
- ieee80211_hw_set(hw, MFP_CAPABLE);
+ if (!fips_enabled)
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+
mvm->ciphers[hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
hw->wiphy->n_cipher_suites++;
if (iwl_mvm_has_new_rx_api(mvm)) {
@@ -376,6 +478,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);
if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_FTM_CALIBRATED)) {
@@ -384,11 +488,30 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->pmsr_capa = &iwl_mvm_pmsr_capa;
}
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT))
+ /*
+ * beacon protection must be handled by firmware,
+ * so cannot be done with fips_enabled
+ */
+ if (!fips_enabled && sec_key_ver &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BIGTK_TX_SUPPORT))
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION);
+ else if (!fips_enabled &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT))
wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT);
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM))
+ hw->wiphy->hw_timestamp_max_peers = 1;
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT))
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT);
+
ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
hw->wiphy->features |=
NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
@@ -426,6 +549,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS;
+ if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_DFS_CONCURRENT);
+
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->flags |= WIPHY_FLAG_SPLIT_SCAN_6GHZ;
@@ -435,7 +562,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ARRAY_SIZE(iwl_mvm_iface_combinations);
hw->wiphy->max_remain_on_channel_duration = 10000;
- hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+ hw->max_listen_interval = IWL_MVM_CONN_LISTEN_INTERVAL;
/* Extract MAC address */
memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
@@ -460,13 +587,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK);
- BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
- IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
+ BUILD_BUG_ON(IWL_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
+ IWL_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
- mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS;
+ mvm->max_scans = IWL_MAX_UMAC_SCANS;
else
- mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS;
+ mvm->max_scans = IWL_MAX_LMAC_SCANS;
if (mvm->nvm_data->bands[NL80211_BAND_2GHZ].n_channels)
hw->wiphy->bands[NL80211_BAND_2GHZ] =
@@ -488,7 +615,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->bands[NL80211_BAND_6GHZ] =
&mvm->nvm_data->bands[NL80211_BAND_6GHZ];
- hw->wiphy->hw_version = mvm->trans->hw_id;
+ hw->wiphy->hw_version = mvm->trans->info.hw_id;
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -514,10 +641,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
NL80211_FEATURE_LOW_PRIORITY_SCAN |
NL80211_FEATURE_P2P_GO_OPPPS |
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
- NL80211_FEATURE_DYNAMIC_SMPS |
- NL80211_FEATURE_STATIC_SMPS |
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
+ /* when firmware supports RLC/SMPS offload, do not set these
+ * driver features, since it's no longer supported by driver.
+ */
+ if (!iwl_mvm_has_rlc_offload(mvm))
+ hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS |
+ NL80211_FEATURE_DYNAMIC_SMPS;
+
if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT))
hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
@@ -535,7 +667,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES;
if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_KEK_KCK_MATERIAL,
- IWL_FW_CMD_VER_UNKNOWN) == 3)
+ IWL_FW_CMD_VER_UNKNOWN) >= 3)
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK;
if (fw_has_api(&mvm->fw->ucode_capa,
@@ -562,23 +694,51 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION);
}
+ hw->wiphy->iftype_ext_capab = NULL;
+ hw->wiphy->num_iftype_ext_capab = 0;
+
if (mvm->nvm_data->sku_cap_11ax_enable &&
!iwlwifi_mod_params.disable_11ax) {
- hw->wiphy->iftype_ext_capab = he_iftypes_ext_capa;
+ hw->wiphy->iftype_ext_capab = add_iftypes_ext_capa;
hw->wiphy->num_iftype_ext_capab =
- ARRAY_SIZE(he_iftypes_ext_capa);
+ ARRAY_SIZE(add_iftypes_ext_capa) - 1;
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
}
+ if (iwl_fw_lookup_cmd_ver(mvm->fw,
+ WIDE_ID(DATA_PATH_GROUP,
+ WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),
+ IWL_FW_CMD_VER_UNKNOWN) >= 1) {
+ IWL_DEBUG_INFO(mvm->trans, "Timing measurement supported\n");
+
+ if (!hw->wiphy->iftype_ext_capab) {
+ hw->wiphy->num_iftype_ext_capab = 1;
+ hw->wiphy->iftype_ext_capab = add_iftypes_ext_capa +
+ ARRAY_SIZE(add_iftypes_ext_capa) - 1;
+ } else {
+ hw->wiphy->iftype_ext_capab = add_iftypes_ext_capa + 1;
+ }
+ }
+
+ if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(LOCATION_GROUP,
+ TOF_RANGE_REQ_CMD),
+ IWL_FW_CMD_VER_UNKNOWN) >= 11) {
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE);
+
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT))
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_SECURE_LTF);
+ }
+
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
#ifdef CONFIG_PM_SLEEP
if ((unified || mvm->fw->img[IWL_UCODE_WOWLAN].num_sec) &&
- mvm->trans->ops->d3_suspend &&
- mvm->trans->ops->d3_resume &&
- device_can_wakeup(mvm->trans->dev)) {
+ device_can_wakeup(mvm->trans->dev) && !fips_enabled) {
mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_EAP_IDENTITY_REQ |
@@ -614,7 +774,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
}
- hw->netdev_features |= mvm->cfg->features;
+ hw->netdev_features |= mvm->trans->mac_cfg->base->features;
if (!iwl_mvm_is_csum_supported(mvm))
hw->netdev_features &= ~IWL_CSUM_NETIF_FLAGS_MASK;
@@ -653,9 +813,8 @@ static void iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
ieee80211_free_txskb(mvm->hw, skb);
}
-static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
+void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control, struct sk_buff *skb)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_sta *sta = control->sta;
@@ -663,6 +822,9 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr = (void *)skb->data;
bool offchannel = IEEE80211_SKB_CB(skb)->flags &
IEEE80211_TX_CTL_TX_OFFCHAN;
+ u32 link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+ struct ieee80211_sta *tmp_sta = sta;
if (iwl_mvm_is_radio_killed(mvm)) {
IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
@@ -670,7 +832,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
}
if (offchannel &&
- !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
+ !test_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status) &&
!test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
goto drop;
@@ -681,19 +843,28 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
if (ieee80211_is_mgmt(hdr->frame_control))
sta = NULL;
- /* If there is no sta, and it's not offchannel - send through AP */
+ /* this shouldn't even happen: just drop */
if (!sta && info->control.vif->type == NL80211_IFTYPE_STATION &&
- !offchannel) {
- struct iwl_mvm_vif *mvmvif =
- iwl_mvm_vif_from_mac80211(info->control.vif);
- u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
-
- if (ap_sta_id < mvm->fw->ucode_capa.num_stations) {
- /* mac80211 holds rcu read lock */
- sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]);
- if (IS_ERR_OR_NULL(sta))
- goto drop;
- }
+ !offchannel)
+ goto drop;
+
+ if (tmp_sta && !sta && link_id != IEEE80211_LINK_UNSPECIFIED &&
+ !ieee80211_is_probe_resp(hdr->frame_control)) {
+ /* translate MLD addresses to LINK addresses */
+ struct ieee80211_link_sta *link_sta =
+ rcu_dereference(tmp_sta->link[link_id]);
+ struct ieee80211_bss_conf *link_conf =
+ rcu_dereference(info->control.vif->link_conf[link_id]);
+ struct ieee80211_mgmt *mgmt;
+
+ if (WARN_ON(!link_sta || !link_conf))
+ goto drop;
+
+ /* if sta is NULL, the frame is a management frame */
+ mgmt = (void *)hdr;
+ memcpy(mgmt->da, link_sta->addr, ETH_ALEN);
+ memcpy(mgmt->sa, link_conf->addr, ETH_ALEN);
+ memcpy(mgmt->bssid, link_conf->bssid, ETH_ALEN);
}
iwl_mvm_tx_skb(mvm, skb, sta);
@@ -732,7 +903,12 @@ void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
rcu_read_lock();
do {
- while (likely(!mvmtxq->stopped &&
+ while (likely(!test_bit(IWL_MVM_TXQ_STATE_STOP_FULL,
+ &mvmtxq->state) &&
+ !test_bit(IWL_MVM_TXQ_STATE_STOP_REDIRECT,
+ &mvmtxq->state) &&
+ !test_bit(IWL_MVM_TXQ_STATE_STOP_AP_CSA,
+ &mvmtxq->state) &&
!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))) {
skb = ieee80211_tx_dequeue(hw, txq);
@@ -751,48 +927,31 @@ void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
rcu_read_unlock();
}
-static void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
+void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
- /*
- * Please note that racing is handled very carefully here:
- * mvmtxq->txq_id is updated during allocation, and mvmtxq->list is
- * deleted afterwards.
- * This means that if:
- * mvmtxq->txq_id != INVALID_QUEUE && list_empty(&mvmtxq->list):
- * queue is allocated and we can TX.
- * mvmtxq->txq_id != INVALID_QUEUE && !list_empty(&mvmtxq->list):
- * a race, should defer the frame.
- * mvmtxq->txq_id == INVALID_QUEUE && list_empty(&mvmtxq->list):
- * need to allocate the queue and defer the frame.
- * mvmtxq->txq_id == INVALID_QUEUE && !list_empty(&mvmtxq->list):
- * queue is already scheduled for allocation, no need to allocate,
- * should defer the frame.
- */
-
- /* If the queue is allocated TX and return. */
- if (!txq->sta || mvmtxq->txq_id != IWL_MVM_INVALID_QUEUE) {
- /*
- * Check that list is empty to avoid a race where txq_id is
- * already updated, but the queue allocation work wasn't
- * finished
- */
- if (unlikely(txq->sta && !list_empty(&mvmtxq->list)))
- return;
-
+ if (likely(test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) ||
+ !txq->sta) {
iwl_mvm_mac_itxq_xmit(hw, txq);
return;
}
- /* The list is being deleted only after the queue is fully allocated. */
- if (!list_empty(&mvmtxq->list))
- return;
+ /* iwl_mvm_mac_itxq_xmit() will later be called by the worker
+ * to handle any packets we leave on the txq now
+ */
- list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
- schedule_work(&mvm->add_stream_wk);
+ spin_lock_bh(&mvm->add_stream_lock);
+ /* The list is being deleted only after the queue is fully allocated. */
+ if (list_empty(&mvmtxq->list) &&
+ /* recheck under lock */
+ !test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) {
+ list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
+ schedule_work(&mvm->add_stream_wk);
+ }
+ spin_unlock_bh(&mvm->add_stream_lock);
}
#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
@@ -847,9 +1006,9 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
}
}
-static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_ampdu_params *params)
+int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
@@ -871,8 +1030,8 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
switch (action) {
case IEEE80211_AMPDU_RX_START:
- if (iwl_mvm_vif_from_mac80211(vif)->ap_sta_id ==
- iwl_mvm_sta_from_mac80211(sta)->sta_id) {
+ if (iwl_mvm_vif_from_mac80211(vif)->deflink.ap_sta_id ==
+ iwl_mvm_sta_from_mac80211(sta)->deflink.sta_id) {
struct iwl_mvm_vif *mvmvif;
u16 macid = iwl_mvm_vif_from_mac80211(vif)->id;
struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[macid];
@@ -935,17 +1094,72 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
{
struct iwl_mvm *mvm = data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_probe_resp_data *probe_data;
+ unsigned int link_id;
mvmvif->uploaded = false;
- mvmvif->ap_sta_id = IWL_MVM_INVALID_STA;
spin_lock_bh(&mvm->time_event_lock);
iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
spin_unlock_bh(&mvm->time_event_lock);
- mvmvif->phy_ctxt = NULL;
- memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
- memset(&mvmvif->probe_resp_data, 0, sizeof(mvmvif->probe_resp_data));
+ mvmvif->roc_activity = ROC_NUM_ACTIVITIES;
+
+ mvmvif->bf_enabled = false;
+ mvmvif->ba_enabled = false;
+ mvmvif->ap_sta = NULL;
+
+ mvmvif->esr_active = false;
+ vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE;
+
+ for_each_mvm_vif_valid_link(mvmvif, link_id) {
+ mvmvif->link[link_id]->ap_sta_id = IWL_INVALID_STA;
+ mvmvif->link[link_id]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
+ mvmvif->link[link_id]->phy_ctxt = NULL;
+ mvmvif->link[link_id]->active = 0;
+ mvmvif->link[link_id]->igtk = NULL;
+ memset(&mvmvif->link[link_id]->bf_data, 0,
+ sizeof(mvmvif->link[link_id]->bf_data));
+ }
+
+ probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data,
+ lockdep_is_held(&mvm->mutex));
+ if (probe_data)
+ kfree_rcu(probe_data, rcu_head);
+ RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL);
+}
+
+static void iwl_mvm_cleanup_sta_iterator(void *data, struct ieee80211_sta *sta)
+{
+ struct iwl_mvm *mvm = data;
+ struct iwl_mvm_sta *mvm_sta;
+ struct ieee80211_vif *vif;
+ int link_id;
+
+ mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ vif = mvm_sta->vif;
+
+ if (!sta->valid_links)
+ return;
+
+ for (link_id = 0; link_id < ARRAY_SIZE((sta)->link); link_id++) {
+ struct iwl_mvm_link_sta *mvm_link_sta;
+
+ mvm_link_sta =
+ rcu_dereference_check(mvm_sta->link[link_id],
+ lockdep_is_held(&mvm->mutex));
+ if (mvm_link_sta && !(vif->active_links & BIT(link_id))) {
+ /*
+ * We have a link STA but the link is inactive in
+ * mac80211. This will happen if we failed to
+ * deactivate the link but mac80211 roll back the
+ * deactivation of the link.
+ * Delete the stale data to avoid issues later on.
+ */
+ iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta,
+ link_id);
+ }
+ }
}
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
@@ -970,6 +1184,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
*/
ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm);
+ /* cleanup stations as links may be gone after restart */
+ ieee80211_iterate_stations_atomic(mvm->hw,
+ iwl_mvm_cleanup_sta_iterator, mvm);
+
mvm->p2p_device_vif = NULL;
iwl_mvm_reset_phy_ctxts(mvm);
@@ -982,6 +1200,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->rx_ba_sessions = 0;
mvm->fwrt.dump.conf = FW_DBG_INVALID;
mvm->monitor_on = false;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ mvm->beacon_inject_active = false;
+#endif
/* keep statistics ticking */
iwl_mvm_accu_radio_stats(mvm);
@@ -989,6 +1210,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
{
+ bool fast_resume = false;
int ret;
lockdep_assert_held(&mvm->mutex);
@@ -1014,6 +1236,31 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
mvm->nvm_data = NULL;
}
+#ifdef CONFIG_PM_SLEEP
+ /* fast_resume will be cleared by iwl_mvm_fast_resume */
+ fast_resume = mvm->fast_resume;
+
+ if (fast_resume) {
+ iwl_mvm_mei_device_state(mvm, true);
+ ret = iwl_mvm_fast_resume(mvm);
+ if (ret) {
+ iwl_mvm_stop_device(mvm);
+ /* iwl_mvm_up() will be called further down */
+ } else {
+ /*
+ * We clear IWL_MVM_STATUS_FIRMWARE_RUNNING upon
+ * mac_down() so that debugfs will stop honoring
+ * requests after we flush all the workers.
+ * Set the IWL_MVM_STATUS_FIRMWARE_RUNNING bit again
+ * now that we are back. This is a bit abusing the
+ * flag since the firmware wasn't really ever stopped,
+ * but this still serves the purpose.
+ */
+ set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
+ }
+ }
+#endif /* CONFIG_PM_SLEEP */
+
if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status)) {
/*
* Now convert the HW_RESTART_REQUESTED flag to IN_HW_RESTART
@@ -1024,7 +1271,10 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
/* Clean up some internal and mac80211 state on restart */
iwl_mvm_restart_cleanup(mvm);
}
- ret = iwl_mvm_up(mvm);
+
+ /* we also want to load the firmware if fast_resume failed */
+ if (!fast_resume || ret)
+ ret = iwl_mvm_up(mvm);
iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT,
NULL);
@@ -1044,7 +1294,7 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
return ret;
}
-static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
+int iwl_mvm_mac_start(struct ieee80211_hw *hw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
@@ -1054,33 +1304,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
/* we are starting the mac not in error flow, and restart is enabled */
if (!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) &&
- iwlwifi_mod_params.fw_restart) {
+ iwlwifi_mod_params.fw_restart)
max_retry = IWL_MAX_INIT_RETRY;
- /*
- * This will prevent mac80211 recovery flows to trigger during
- * init failures
- */
- set_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
- }
for (retry = 0; retry <= max_retry; retry++) {
ret = __iwl_mvm_mac_start(mvm);
- if (!ret)
+ if (ret != -ETIMEDOUT)
break;
- /*
- * In PLDR sync PCI re-enumeration is needed. no point to retry
- * mac start before that.
- */
- if (mvm->pldr_sync) {
- iwl_mei_alive_notif(false);
- iwl_trans_pcie_remove(mvm->trans, true);
- break;
- }
-
IWL_ERR(mvm, "mac start retry %d\n", retry);
}
- clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
mutex_unlock(&mvm->mutex);
@@ -1093,7 +1326,7 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
{
int ret;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
@@ -1110,12 +1343,16 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
*/
iwl_mvm_teardown_tdls_peers(mvm);
- mutex_unlock(&mvm->mutex);
+ IWL_INFO(mvm, "restart completed\n");
+ iwl_trans_finish_sw_reset(mvm->trans);
+
+ /* no need to lock, adding in parallel would schedule too */
+ if (!list_empty(&mvm->add_stream_txqs))
+ schedule_work(&mvm->add_stream_wk);
}
-static void
-iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
- enum ieee80211_reconfig_type reconfig_type)
+void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -1128,7 +1365,7 @@ iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
}
}
-void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
+void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend)
{
lockdep_assert_held(&mvm->mutex);
@@ -1141,10 +1378,17 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
/* async_handlers_wk is now blocked */
- if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12)
+ if (!iwl_mvm_has_new_station_api(mvm->fw))
iwl_mvm_rm_aux_sta(mvm);
- iwl_mvm_stop_device(mvm);
+ if (suspend &&
+ mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
+ iwl_mvm_fast_suspend(mvm);
+ /* From this point on, we won't touch the device */
+ iwl_mvm_mei_device_state(mvm, false);
+ } else {
+ iwl_mvm_stop_device(mvm);
+ }
iwl_mvm_async_handlers_purge(mvm);
/* async_handlers_list is empty and will stay empty: HW is stopped */
@@ -1177,10 +1421,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
}
}
-static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
+void iwl_mvm_mac_stop(struct ieee80211_hw *hw, bool suspend)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ wiphy_work_flush(mvm->hw->wiphy, &mvm->async_handlers_wiphy_wk);
flush_work(&mvm->async_handlers_wk);
flush_work(&mvm->add_stream_wk);
@@ -1206,7 +1451,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
iwl_mvm_mei_set_sw_rfkill_state(mvm);
mutex_lock(&mvm->mutex);
- __iwl_mvm_mac_stop(mvm);
+ __iwl_mvm_mac_stop(mvm, suspend);
mutex_unlock(&mvm->mutex);
/*
@@ -1214,9 +1459,10 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
* discover that its list is now empty.
*/
cancel_work_sync(&mvm->async_handlers_wk);
+ wiphy_work_cancel(hw->wiphy, &mvm->async_handlers_wiphy_wk);
}
-static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
+struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
{
u16 i;
@@ -1230,27 +1476,47 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
return NULL;
}
-static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- s16 tx_power)
+int iwl_mvm_set_tx_power(struct iwl_mvm *mvm,
+ struct ieee80211_bss_conf *link_conf,
+ s16 tx_power)
{
u32 cmd_id = REDUCE_TX_POWER_CMD;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(link_conf->vif);
+ u32 mac_id = mvmvif->id;
int len;
- struct iwl_dev_tx_power_cmd cmd = {
- .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC),
- .common.mac_context_id =
- cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id),
- .common.pwr_restriction = cpu_to_le16(8 * tx_power),
+ struct iwl_dev_tx_power_cmd_v3_v8 cmd = {
+ .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
+ .common.link_id = cpu_to_le32(mac_id),
};
- u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
- IWL_FW_CMD_VER_UNKNOWN);
+ struct iwl_dev_tx_power_cmd cmd_v9_v10;
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 3);
+ u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ?
+ IWL_DEV_MAX_TX_POWER : 8 * tx_power;
+ void *cmd_data = &cmd;
+
+ cmd.common.pwr_restriction = cpu_to_le16(u_tx_power);
+
+ if (cmd_ver > 8) {
+ u32 link_id;
- if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
- cmd.common.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
+ if (WARN_ON(!mvmvif->link[link_conf->link_id]))
+ return -ENODEV;
- if (cmd_ver == 7)
- len = sizeof(cmd.v7);
- else if (cmd_ver == 6)
- len = sizeof(cmd.v6);
+ link_id = mvmvif->link[link_conf->link_id]->fw_link_id;
+
+ /* Those fields sit on the same place for v9 and v10 */
+ cmd_v9_v10.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK);
+ cmd_v9_v10.common.link_id = cpu_to_le32(link_id);
+ cmd_v9_v10.common.pwr_restriction = cpu_to_le16(u_tx_power);
+ cmd_data = &cmd_v9_v10;
+ }
+
+ if (cmd_ver == 10)
+ len = sizeof(cmd_v9_v10.v10);
+ else if (cmd_ver == 9)
+ len = sizeof(cmd_v9_v10.v9);
+ else if (cmd_ver == 8)
+ len = sizeof(cmd.v8);
else if (fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_REDUCE_TX_POWER))
len = sizeof(cmd.v5);
@@ -1260,14 +1526,33 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
else
len = sizeof(cmd.v3);
- /* all structs have the same common part, add it */
+ /* all structs have the same common part, add its length */
len += sizeof(cmd.common);
- return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd);
+ if (cmd_ver < 9)
+ len += sizeof(cmd.per_band);
+
+ return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, cmd_data);
+
}
-static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+static void iwl_mvm_post_csa_tx(void *data, struct ieee80211_sta *sta)
+{
+ struct ieee80211_hw *hw = data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct iwl_mvm_txq *mvmtxq =
+ iwl_mvm_txq_from_mac80211(sta->txq[i]);
+
+ clear_bit(IWL_MVM_TXQ_STATE_STOP_AP_CSA, &mvmtxq->state);
+ iwl_mvm_mac_itxq_xmit(hw, sta->txq[i]);
+ }
+}
+
+int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -1277,10 +1562,12 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_STATION) {
struct iwl_mvm_sta *mvmsta;
+ unsigned int link_id = link_conf->link_id;
+ u8 ap_sta_id = mvmvif->link[link_id]->ap_sta_id;
mvmvif->csa_bcn_pending = false;
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
- mvmvif->ap_sta_id);
+ mvmvif->csa_blocks_tx = false;
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, ap_sta_id);
if (WARN_ON(!mvmsta)) {
ret = -EIO;
@@ -1288,17 +1575,31 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
}
iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
-
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ if (mvm->mld_api_is_used)
+ iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false);
+ else
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
if (!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
- ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif);
if (ret)
goto out_unlock;
iwl_mvm_stop_session_protection(mvm, vif);
}
+ } else if (vif->type == NL80211_IFTYPE_AP && mvmvif->csa_blocks_tx) {
+ struct iwl_mvm_txq *mvmtxq =
+ iwl_mvm_txq_from_mac80211(vif->txq);
+
+ clear_bit(IWL_MVM_TXQ_STATE_STOP_AP_CSA, &mvmtxq->state);
+
+ local_bh_disable();
+ iwl_mvm_mac_itxq_xmit(hw, vif->txq);
+ ieee80211_iterate_stations_atomic(hw, iwl_mvm_post_csa_tx, hw);
+ local_bh_enable();
+
+ mvmvif->csa_blocks_tx = false;
}
mvmvif->ps_disabled = false;
@@ -1313,8 +1614,9 @@ out_unlock:
return ret;
}
-static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -1347,10 +1649,11 @@ static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
mvmvif->csa_failed = true;
mutex_unlock(&mvm->mutex);
- iwl_mvm_post_channel_switch(hw, vif);
+ /* If we're here, we can't support MLD */
+ iwl_mvm_post_channel_switch(hw, vif, &vif->bss_conf);
}
-static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)
+void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)
{
struct iwl_mvm_vif *mvmvif;
struct ieee80211_vif *vif;
@@ -1359,7 +1662,64 @@ static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)
vif = container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
/* Trigger disconnect (should clear the CSA state) */
- ieee80211_chswitch_done(vif, false);
+ ieee80211_chswitch_done(vif, false, 0);
+}
+
+static u8
+iwl_mvm_chandef_get_primary_80(struct cfg80211_chan_def *chandef)
+{
+ int data_start;
+ int control_start;
+ int bw;
+
+ if (chandef->width == NL80211_CHAN_WIDTH_320)
+ bw = 320;
+ else if (chandef->width == NL80211_CHAN_WIDTH_160)
+ bw = 160;
+ else
+ return 0;
+
+ /* data is bw wide so the start is half the width */
+ data_start = chandef->center_freq1 - bw / 2;
+ /* control is 20Mhz width */
+ control_start = chandef->chan->center_freq - 10;
+
+ return (control_start - data_start) / 80;
+}
+
+static int iwl_mvm_alloc_bcast_mcast_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to allocate bcast sta\n");
+ return ret;
+ }
+
+ /* Only queue for this station is the mcast queue,
+ * which shouldn't be in TFD mask anyway
+ */
+ return iwl_mvm_allocate_int_sta(mvm, &mvmvif->deflink.mcast_sta, 0,
+ vif->type,
+ IWL_STA_MULTICAST);
+}
+
+void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ return;
+
+ mvmvif->deflink.average_beacon_energy = 0;
+
+ INIT_DELAYED_WORK(&mvmvif->csa_work,
+ iwl_mvm_channel_switch_disconnect_wk);
}
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
@@ -1368,9 +1728,22 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
+ int i;
+
+ mutex_lock(&mvm->mutex);
+
+ iwl_mvm_mac_init_mvmvif(mvm, mvmvif);
mvmvif->mvm = mvm;
- RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL);
+
+ /* the first link always points to the default one */
+ mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
+ mvmvif->deflink.active = 0;
+ mvmvif->link[0] = &mvmvif->deflink;
+
+ vif->driver_flags = IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC;
+
+ iwl_mvm_set_link_fw_id(mvm, vif, &vif->bss_conf);
/*
* Not much to do here. The stack will not allow interface
@@ -1378,17 +1751,16 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
* don't really have to check the types.
*/
- mutex_lock(&mvm->mutex);
-
/* make sure that beacon statistics don't go backwards with FW reset */
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- mvmvif->beacon_stats.accu_num_beacons +=
- mvmvif->beacon_stats.num_beacons;
+ for_each_mvm_vif_valid_link(mvmvif, i)
+ mvmvif->link[i]->beacon_stats.accu_num_beacons +=
+ mvmvif->link[i]->beacon_stats.num_beacons;
/* Allocate resources for the MAC context, and add it to the fw */
ret = iwl_mvm_mac_ctxt_init(mvm, vif);
if (ret)
- goto out_unlock;
+ goto out;
rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif);
@@ -1405,24 +1777,10 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
*/
if (vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_ADHOC) {
- ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
- if (ret) {
- IWL_ERR(mvm, "Failed to allocate bcast sta\n");
- goto out_unlock;
- }
-
- /*
- * Only queue for this station is the mcast queue,
- * which shouldn't be in TFD mask anyway
- */
- ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta,
- 0, vif->type,
- IWL_STA_MULTICAST);
- if (ret)
- goto out_unlock;
-
- iwl_mvm_vif_dbgfs_register(mvm, vif);
- goto out_unlock;
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ iwl_mvm_vif_dbgfs_add_link(mvm, vif);
+ ret = 0;
+ goto out;
}
mvmvif->features |= hw->netdev_features;
@@ -1436,7 +1794,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
goto out_remove_mac;
/* beacon filtering */
- ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif);
if (ret)
goto out_remove_mac;
@@ -1447,41 +1805,19 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
IEEE80211_VIF_SUPPORTS_CQM_RSSI;
}
- /*
- * P2P_DEVICE interface does not have a channel context assigned to it,
- * so a dedicated PHY context is allocated to it and the corresponding
- * MAC context is bound to it at this stage.
- */
- if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
-
- mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
- if (!mvmvif->phy_ctxt) {
- ret = -ENOSPC;
- goto out_free_bf;
- }
-
- iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (ret)
- goto out_unref_phy;
-
- ret = iwl_mvm_add_p2p_bcast_sta(mvm, vif);
- if (ret)
- goto out_unbind;
-
- /* Save a pointer to p2p device vif, so it can later be used to
- * update the p2p device MAC when a GO is started/stopped */
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
mvm->p2p_device_vif = vif;
- }
iwl_mvm_tcm_add_vif(mvm, vif);
- INIT_DELAYED_WORK(&mvmvif->csa_work,
- iwl_mvm_channel_switch_disconnect_wk);
- if (vif->type == NL80211_IFTYPE_MONITOR)
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
mvm->monitor_on = true;
+ mvm->monitor_p80 =
+ iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chanreq.oper);
+ }
- iwl_mvm_vif_dbgfs_register(mvm, vif);
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ iwl_mvm_vif_dbgfs_add_link(mvm, vif);
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
@@ -1491,20 +1827,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
mvm->csme_vif = vif;
}
+out:
+ if (!ret && (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_ADHOC))
+ ret = iwl_mvm_alloc_bcast_mcast_sta(mvm, vif);
+
goto out_unlock;
- out_unbind:
- iwl_mvm_binding_remove_vif(mvm, vif);
- out_unref_phy:
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
- out_free_bf:
- if (mvm->bf_allowed_vif == mvmvif) {
- mvm->bf_allowed_vif = NULL;
- vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
- IEEE80211_VIF_SUPPORTS_CQM_RSSI);
- }
out_remove_mac:
- mvmvif->phy_ctxt = NULL;
+ mvmvif->deflink.phy_ctxt = NULL;
iwl_mvm_mac_ctxt_remove(mvm, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
@@ -1512,9 +1843,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
return ret;
}
-static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
/*
* Flush the ROC worker which will flush the OFFCHANNEL queue.
@@ -1523,6 +1856,8 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
*/
flush_work(&mvm->roc_done_wk);
}
+
+ cancel_delayed_work_sync(&mvmvif->csa_work);
}
static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
@@ -1545,9 +1880,9 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
mvm->csme_vif = NULL;
}
- probe_data = rcu_dereference_protected(mvmvif->probe_resp_data,
+ probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data,
lockdep_is_held(&mvm->mutex));
- RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL);
+ RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL);
if (probe_data)
kfree_rcu(probe_data, rcu_head);
@@ -1560,34 +1895,31 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
if (vif->bss_conf.ftm_responder)
memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats));
- iwl_mvm_vif_dbgfs_clean(mvm, vif);
+ iwl_mvm_vif_dbgfs_rm_link(mvm, vif);
/*
* For AP/GO interface, the tear down of the resources allocated to the
* interface is be handled as part of the stop_ap flow.
*/
if (vif->type == NL80211_IFTYPE_AP ||
- vif->type == NL80211_IFTYPE_ADHOC) {
-#ifdef CONFIG_NL80211_TESTMODE
- if (vif == mvm->noa_vif) {
- mvm->noa_vif = NULL;
- mvm->noa_duration = 0;
- }
-#endif
- iwl_mvm_dealloc_int_sta(mvm, &mvmvif->mcast_sta);
- iwl_mvm_dealloc_bcast_sta(mvm, vif);
- goto out_release;
- }
+ vif->type == NL80211_IFTYPE_ADHOC)
+ goto out;
+ iwl_mvm_power_update_mac(mvm);
+
+ /* Before the interface removal, mac80211 would cancel the ROC, and the
+ * ROC worker would be scheduled if needed. The worker would be flushed
+ * in iwl_mvm_prepare_mac_removal() and thus at this point there is no
+ * binding etc. so nothing needs to be done here.
+ */
if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ if (mvmvif->deflink.phy_ctxt) {
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt);
+ mvmvif->deflink.phy_ctxt = NULL;
+ }
mvm->p2p_device_vif = NULL;
- iwl_mvm_rm_p2p_bcast_sta(mvm, vif);
- iwl_mvm_binding_remove_vif(mvm, vif);
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
- mvmvif->phy_ctxt = NULL;
}
- iwl_mvm_power_update_mac(mvm);
iwl_mvm_mac_ctxt_remove(mvm, vif);
RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL);
@@ -1595,13 +1927,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_MONITOR)
mvm->monitor_on = false;
-out_release:
- mutex_unlock(&mvm->mutex);
-}
+out:
+ if (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_ADHOC) {
+ iwl_mvm_dealloc_int_sta(mvm, &mvmvif->deflink.mcast_sta);
+ iwl_mvm_dealloc_bcast_sta(mvm, vif);
+ }
-static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
-{
- return 0;
+ mutex_unlock(&mvm->mutex);
}
struct iwl_mvm_mc_iter_data {
@@ -1675,8 +2008,8 @@ static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
IWL_ERR(mvm, "Failed to synchronize multicast groups update\n");
}
-static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
- struct netdev_hw_addr_list *mc_list)
+u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mcast_filter_cmd *cmd;
@@ -1712,15 +2045,14 @@ static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
return (u64)(unsigned long)cmd;
}
-static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *total_flags,
- u64 multicast)
+void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
/* replace previous configuration */
kfree(mvm->mcast_filter_cmd);
@@ -1737,7 +2069,6 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
iwl_mvm_recalc_multicast(mvm);
out:
- mutex_unlock(&mvm->mutex);
*total_flags = 0;
}
@@ -1757,13 +2088,11 @@ static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw,
!vif->p2p)
return;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- mutex_unlock(&mvm->mutex);
}
-static int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mu_group_mgmt_cmd cmd = {};
@@ -1888,12 +2217,13 @@ set_thresholds:
}
static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
+ struct ieee80211_link_sta *link_sta,
struct iwl_he_pkt_ext_v2 *pkt_ext,
bool inheritance)
{
- u8 nss = (sta->deflink.he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1;
- u8 *ppe = &sta->deflink.he_cap.ppe_thres[0];
+ u8 nss = (link_sta->he_cap.ppe_thres[0] &
+ IEEE80211_PPE_THRES_NSS_MASK) + 1;
+ u8 *ppe = &link_sta->he_cap.ppe_thres[0];
u8 ru_index_bitmap =
u8_get_bits(*ppe,
IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
@@ -1904,9 +2234,9 @@ static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm,
inheritance);
}
-static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext,
- u8 nominal_padding,
- u32 *flags)
+static int
+iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext,
+ u8 nominal_padding)
{
int low_th = -1;
int high_th = -1;
@@ -1929,21 +2259,22 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p
break;
}
+ if (low_th < 0 || high_th < 0)
+ return -EINVAL;
+
/* Set the PPE thresholds accordingly */
- if (low_th >= 0 && high_th >= 0) {
- for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
- u8 bw;
+ for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ u8 bw;
- for (bw = 0;
- bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
- bw++) {
- pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
- pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
- }
+ for (bw = 0;
+ bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
+ bw++) {
+ pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
+ pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
}
-
- *flags |= STA_CTXT_HE_PACKET_EXT;
}
+
+ return 0;
}
static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,
@@ -1971,6 +2302,189 @@ static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,
}
}
+/* Set the pkt_ext field according to PPE Thresholds element */
+int iwl_mvm_set_sta_pkt_ext(struct iwl_mvm *mvm,
+ struct ieee80211_link_sta *link_sta,
+ struct iwl_he_pkt_ext_v2 *pkt_ext)
+{
+ u8 nominal_padding;
+ int i, ret = 0;
+
+ if (WARN_ON(!link_sta))
+ return -EINVAL;
+
+ /* Initialize the PPE thresholds to "None" (7), as described in Table
+ * 9-262ac of 80211.ax/D3.0.
+ */
+ memset(pkt_ext, IWL_HE_PKT_EXT_NONE,
+ sizeof(struct iwl_he_pkt_ext_v2));
+
+ if (link_sta->eht_cap.has_eht) {
+ nominal_padding =
+ u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5],
+ IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK);
+
+ /* If PPE Thresholds exists, parse them into a FW-familiar
+ * format.
+ */
+ if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
+ IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
+ u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] &
+ IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1;
+ u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0];
+ u8 ru_index_bitmap =
+ u16_get_bits(*ppe,
+ IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK);
+ /* Starting after PPE header */
+ u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE;
+
+ iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap,
+ ppe, ppe_pos_bit, true);
+ /* EHT PPE Thresholds doesn't exist - set the API according to
+ * HE PPE Tresholds
+ */
+ } else if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ /* Even though HE Capabilities IE doesn't contain PPE
+ * Thresholds for BW 320Mhz, thresholds for this BW will
+ * be filled in with the same values as 160Mhz, due to
+ * the inheritance, as required.
+ */
+ iwl_mvm_set_pkt_ext_from_he_ppe(mvm, link_sta, pkt_ext,
+ true);
+
+ /* According to the requirements, for MCSs 12-13 the
+ * maximum value between HE PPE Threshold and Common
+ * Nominal Packet Padding needs to be taken
+ */
+ iwl_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding);
+
+ /* if PPE Thresholds doesn't present in both EHT IE and HE IE -
+ * take the Thresholds from Common Nominal Packet Padding field
+ */
+ } else {
+ ret = iwl_mvm_set_pkt_ext_from_nominal_padding(pkt_ext,
+ nominal_padding);
+ }
+ } else if (link_sta->he_cap.has_he) {
+ /* If PPE Thresholds exist, parse them into a FW-familiar format. */
+ if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ iwl_mvm_set_pkt_ext_from_he_ppe(mvm, link_sta, pkt_ext,
+ false);
+ /* PPE Thresholds doesn't exist - set the API PPE values
+ * according to Common Nominal Packet Padding field.
+ */
+ } else {
+ nominal_padding =
+ u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9],
+ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
+ if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED)
+ ret = iwl_mvm_set_pkt_ext_from_nominal_padding(pkt_ext,
+ nominal_padding);
+ }
+ }
+
+ for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ int bw;
+
+ for (bw = 0;
+ bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]);
+ bw++) {
+ u8 *qam_th =
+ &pkt_ext->pkt_ext_qam_th[i][bw][0];
+
+ IWL_DEBUG_HT(mvm,
+ "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n",
+ i, bw, qam_th[0], qam_th[1]);
+ }
+ }
+ return ret;
+}
+
+/*
+ * This function sets the MU EDCA parameters ans returns whether MU EDCA
+ * is enabled or not
+ */
+bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm,
+ const struct iwl_mvm_vif_link_info *link_info,
+ struct iwl_he_backoff_conf *trig_based_txf)
+{
+ int i;
+ /* Mark MU EDCA as enabled, unless none detected on some AC */
+ bool mu_edca_enabled = true;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ const struct ieee80211_he_mu_edca_param_ac_rec *mu_edca =
+ &link_info->queue_params[i].mu_edca_param_rec;
+ u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i);
+
+ if (!link_info->queue_params[i].mu_edca) {
+ mu_edca_enabled = false;
+ break;
+ }
+
+ trig_based_txf[ac].cwmin =
+ cpu_to_le16(mu_edca->ecw_min_max & 0xf);
+ trig_based_txf[ac].cwmax =
+ cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4);
+ trig_based_txf[ac].aifsn =
+ cpu_to_le16(mu_edca->aifsn & 0xf);
+ trig_based_txf[ac].mu_time =
+ cpu_to_le16(mu_edca->mu_edca_timer);
+ }
+
+ return mu_edca_enabled;
+}
+
+bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ const struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_he_cap *own_he_cap = NULL;
+
+ /* This capability is the same for all bands,
+ * so take it from one of them.
+ */
+ sband = mvm->hw->wiphy->bands[NL80211_BAND_2GHZ];
+ own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
+
+ return (own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] &
+ IEEE80211_HE_MAC_CAP2_ACK_EN));
+}
+
+__le32 iwl_mvm_get_sta_htc_flags(struct ieee80211_sta *sta,
+ struct ieee80211_link_sta *link_sta)
+{
+ u8 *mac_cap_info =
+ &link_sta->he_cap.he_cap_elem.mac_cap_info[0];
+ __le32 htc_flags = 0;
+
+ if (mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE)
+ htc_flags |= cpu_to_le32(IWL_HE_HTC_SUPPORT);
+ if ((mac_cap_info[1] & IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) ||
+ (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) {
+ u8 link_adap =
+ ((mac_cap_info[2] &
+ IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) +
+ (mac_cap_info[1] &
+ IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION);
+
+ if (link_adap == 2)
+ htc_flags |=
+ cpu_to_le32(IWL_HE_HTC_LINK_ADAP_UNSOLICITED);
+ else if (link_adap == 3)
+ htc_flags |= cpu_to_le32(IWL_HE_HTC_LINK_ADAP_BOTH);
+ }
+ if (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR)
+ htc_flags |= cpu_to_le32(IWL_HE_HTC_BSR_SUPP);
+ if (mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL)
+ htc_flags |= cpu_to_le32(IWL_HE_HTC_OMI_SUPP);
+ if (mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
+ htc_flags |= cpu_to_le32(IWL_HE_HTC_BQR_SUPP);
+
+ return htc_flags;
+}
+
static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, u8 sta_id)
{
@@ -1990,11 +2504,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
struct ieee80211_sta *sta;
u32 flags;
int i;
- const struct ieee80211_sta_he_cap *own_he_cap = NULL;
- struct ieee80211_chanctx_conf *chanctx_conf;
- const struct ieee80211_supported_band *sband;
void *cmd;
- u8 nominal_padding;
if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_MBSSID_HE))
ver = 1;
@@ -2020,16 +2530,6 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
rcu_read_lock();
- chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf);
- if (WARN_ON(!chanctx_conf)) {
- rcu_read_unlock();
- return;
- }
-
- sband = mvm->hw->wiphy->bands[chanctx_conf->def.chan->band];
- own_he_cap = ieee80211_get_he_iftype_cap(sband,
- ieee80211_vif_type_p2p(vif));
-
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_ctxt_cmd.sta_id]);
if (IS_ERR_OR_NULL(sta)) {
rcu_read_unlock();
@@ -2045,136 +2545,15 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
flags = 0;
/* Block 26-tone RU OFDMA transmissions */
- if (mvmvif->he_ru_2mhz_block)
+ if (mvmvif->deflink.he_ru_2mhz_block)
flags |= STA_CTXT_HE_RU_2MHZ_BLOCK;
/* HTC flags */
- if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[0] &
- IEEE80211_HE_MAC_CAP0_HTC_HE)
- sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_SUPPORT);
- if ((sta->deflink.he_cap.he_cap_elem.mac_cap_info[1] &
- IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) ||
- (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] &
- IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) {
- u8 link_adap =
- ((sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] &
- IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) +
- (sta->deflink.he_cap.he_cap_elem.mac_cap_info[1] &
- IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION);
-
- if (link_adap == 2)
- sta_ctxt_cmd.htc_flags |=
- cpu_to_le32(IWL_HE_HTC_LINK_ADAP_UNSOLICITED);
- else if (link_adap == 3)
- sta_ctxt_cmd.htc_flags |=
- cpu_to_le32(IWL_HE_HTC_LINK_ADAP_BOTH);
- }
- if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR)
- sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BSR_SUPP);
- if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[3] &
- IEEE80211_HE_MAC_CAP3_OMI_CONTROL)
- sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_OMI_SUPP);
- if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
- sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BQR_SUPP);
-
- /*
- * Initialize the PPE thresholds to "None" (7), as described in Table
- * 9-262ac of 80211.ax/D3.0.
- */
- memset(&sta_ctxt_cmd.pkt_ext, IWL_HE_PKT_EXT_NONE,
- sizeof(sta_ctxt_cmd.pkt_ext));
+ sta_ctxt_cmd.htc_flags = iwl_mvm_get_sta_htc_flags(sta, &sta->deflink);
- if (sta->deflink.eht_cap.has_eht) {
- nominal_padding =
- u8_get_bits(sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5],
- IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK);
-
- /* If PPE Thresholds exists, parse them into a FW-familiar format. */
- if (sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5] &
- IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
- u8 nss = (sta->deflink.eht_cap.eht_ppe_thres[0] &
- IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1;
- u8 *ppe = &sta->deflink.eht_cap.eht_ppe_thres[0];
- u8 ru_index_bitmap =
- u16_get_bits(*ppe,
- IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK);
- /* Starting after PPE header */
- u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE;
-
- iwl_mvm_parse_ppe(mvm,
- &sta_ctxt_cmd.pkt_ext,
- nss, ru_index_bitmap, ppe,
- ppe_pos_bit, true);
- flags |= STA_CTXT_HE_PACKET_EXT;
- /* EHT PPE Thresholds doesn't exist - set the API according to HE PPE Tresholds*/
- } else if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] &
- IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
- struct iwl_he_pkt_ext_v2 *pkt_ext =
- &sta_ctxt_cmd.pkt_ext;
-
- /*
- * Even though HE Capabilities IE doesn't contain PPE
- * Thresholds for BW 320Mhz, thresholds for this BW will
- * be filled in with the same values as 160Mhz, due to
- * the inheritance, as required.
- */
- iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, pkt_ext,
- true);
-
- /*
- * According to the requirements, for MCSs 12-13 the maximum value between
- * HE PPE Threshold and Common Nominal Packet Padding needs to be taken
- */
- iwl_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding);
-
- flags |= STA_CTXT_HE_PACKET_EXT;
-
- /*
- * if PPE Thresholds doesn't present in both EHT IE and HE IE -
- * take the Thresholds from Common Nominal Packet Padding field
- */
- } else {
- iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext,
- nominal_padding,
- &flags);
- }
- } else if (sta->deflink.he_cap.has_he) {
- /* If PPE Thresholds exist, parse them into a FW-familiar format. */
- if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] &
- IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
- iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta,
- &sta_ctxt_cmd.pkt_ext,
- false);
- flags |= STA_CTXT_HE_PACKET_EXT;
- /*
- * PPE Thresholds doesn't exist - set the API PPE values
- * according to Common Nominal Packet Padding field.
- */
- } else {
- nominal_padding =
- u8_get_bits(sta->deflink.he_cap.he_cap_elem.phy_cap_info[9],
- IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
- if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED)
- iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext,
- nominal_padding,
- &flags);
- }
- }
-
- for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
- int bw;
-
- for (bw = 0;
- bw < ARRAY_SIZE(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i]);
- bw++) {
- u8 *qam_th =
- &sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0];
-
- IWL_DEBUG_HT(mvm,
- "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n",
- i, bw, qam_th[0], qam_th[1]);
- }
- }
+ /* PPE Thresholds */
+ if (!iwl_mvm_set_sta_pkt_ext(mvm, &sta->deflink, &sta_ctxt_cmd.pkt_ext))
+ flags |= STA_CTXT_HE_PACKET_EXT;
if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] &
IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP)
@@ -2186,28 +2565,9 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
rcu_read_unlock();
- /* Mark MU EDCA as enabled, unless none detected on some AC */
- flags |= STA_CTXT_HE_MU_EDCA_CW;
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- struct ieee80211_he_mu_edca_param_ac_rec *mu_edca =
- &mvmvif->queue_params[i].mu_edca_param_rec;
- u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i);
-
- if (!mvmvif->queue_params[i].mu_edca) {
- flags &= ~STA_CTXT_HE_MU_EDCA_CW;
- break;
- }
-
- sta_ctxt_cmd.trig_based_txf[ac].cwmin =
- cpu_to_le16(mu_edca->ecw_min_max & 0xf);
- sta_ctxt_cmd.trig_based_txf[ac].cwmax =
- cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4);
- sta_ctxt_cmd.trig_based_txf[ac].aifsn =
- cpu_to_le16(mu_edca->aifsn);
- sta_ctxt_cmd.trig_based_txf[ac].mu_time =
- cpu_to_le16(mu_edca->mu_edca_timer);
- }
-
+ if (iwl_mvm_set_fw_mu_edca_params(mvm, &mvmvif->deflink,
+ &sta_ctxt_cmd.trig_based_txf[0]))
+ flags |= STA_CTXT_HE_MU_EDCA_CW;
if (vif->bss_conf.uora_exists) {
flags |= STA_CTXT_HE_TRIG_RND_ALLOC;
@@ -2218,8 +2578,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
(vif->bss_conf.uora_ocw_range >> 3) & 0x7;
}
- if (own_he_cap && !(own_he_cap->he_cap_elem.mac_cap_info[2] &
- IEEE80211_HE_MAC_CAP2_ACK_EN))
+ if (!iwl_mvm_is_nic_ack_enabled(mvm, vif))
flags |= STA_CTXT_HE_NIC_NOT_ACK_ENABLED;
if (vif->bss_conf.nontransmitted) {
@@ -2279,9 +2638,8 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
IWL_ERR(mvm, "Failed to config FW to work HE!\n");
}
-static void iwl_mvm_protect_assoc(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- u32 duration_override)
+void iwl_mvm_protect_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ u32 duration_override, unsigned int link_id)
{
u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS;
@@ -2301,12 +2659,107 @@ static void iwl_mvm_protect_assoc(struct iwl_mvm *mvm,
if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
iwl_mvm_schedule_session_protection(mvm, vif, 900,
- min_duration, false);
+ min_duration, false,
+ link_id);
else
iwl_mvm_protect_session(mvm, vif, duration,
min_duration, 500, false);
}
+/* Handle association common part to MLD and non-MLD modes */
+void iwl_mvm_bss_info_changed_station_assoc(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u64 changes)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+ int link_id;
+
+ /* The firmware tracks the MU-MIMO group on its own.
+ * However, on HW restart we should restore this data.
+ */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ (changes & BSS_CHANGED_MU_GROUPS) && vif->bss_conf.mu_mimo_owner) {
+ ret = iwl_mvm_update_mu_groups(mvm, vif);
+ if (ret)
+ IWL_ERR(mvm,
+ "failed to update VHT MU_MIMO groups\n");
+ }
+
+ iwl_mvm_recalc_multicast(mvm);
+
+ /* reset rssi values */
+ for_each_mvm_vif_valid_link(mvmvif, link_id)
+ mvmvif->link[link_id]->bf_data.ave_beacon_signal = 0;
+
+ iwl_mvm_bt_coex_vif_change(mvm);
+ iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_TT,
+ IEEE80211_SMPS_AUTOMATIC);
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ iwl_mvm_config_scan(mvm);
+}
+
+/* Execute the common part for MLD and non-MLD modes */
+void
+iwl_mvm_bss_info_changed_station_common(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ u64 changes)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (changes & BSS_CHANGED_BEACON_INFO) {
+ /* We received a beacon from the associated AP so
+ * remove the session protection.
+ */
+ iwl_mvm_stop_session_protection(mvm, vif);
+
+ iwl_mvm_sf_update(mvm, vif, false);
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
+ }
+
+ if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS |
+ /* Send power command on every beacon change,
+ * because we may have not enabled beacon abort yet.
+ */
+ BSS_CHANGED_BEACON_INFO)) {
+ ret = iwl_mvm_power_update_mac(mvm);
+ if (ret)
+ IWL_ERR(mvm, "failed to update power mode\n");
+ }
+
+ if (changes & BSS_CHANGED_CQM) {
+ struct iwl_mvm_vif_link_info *link_info =
+ mvmvif->link[link_conf->link_id];
+
+ IWL_DEBUG_MAC80211(mvm, "CQM info_changed\n");
+ if (link_info)
+ link_info->bf_data.last_cqm_event = 0;
+
+ if (mvmvif->bf_enabled) {
+ /* FIXME: need to update per link when FW API will
+ * support it
+ */
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+ if (ret)
+ IWL_ERR(mvm,
+ "failed to update CQM thresholds\n");
+ }
+ }
+
+ if (changes & BSS_CHANGED_BANDWIDTH)
+ iwl_mvm_update_link_smps(vif, link_conf);
+
+ if (changes & BSS_CHANGED_TPE) {
+ IWL_DEBUG_CALIB(mvm, "Changing TPE\n");
+ iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif,
+ link_conf,
+ false);
+ }
+}
+
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -2314,6 +2767,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
+ int i;
/*
* Re-calculate the tsf id, as the leader-follower relations depend
@@ -2322,10 +2776,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
*/
if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
if ((vif->bss_conf.he_support &&
- !iwlwifi_mod_params.disable_11ax) ||
- (vif->bss_conf.eht_support &&
- !iwlwifi_mod_params.disable_11be))
- iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
+ !iwlwifi_mod_params.disable_11ax))
+ iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->deflink.ap_sta_id);
iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
}
@@ -2333,11 +2785,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
/* Update MU EDCA params */
if (changes & BSS_CHANGED_QOS && mvmvif->associated &&
vif->cfg.assoc &&
- ((vif->bss_conf.he_support &&
- !iwlwifi_mod_params.disable_11ax) ||
- (vif->bss_conf.eht_support &&
- !iwlwifi_mod_params.disable_11be)))
- iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
+ (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax))
+ iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->deflink.ap_sta_id);
/*
* If we're not associated yet, take the (new) BSSID before associating
@@ -2346,22 +2795,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
* branch for disassociation below.
*/
if (changes & BSS_CHANGED_BSSID && !mvmvif->associated)
- memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
+ memcpy(mvmvif->deflink.bssid, bss_conf->bssid, ETH_ALEN);
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid);
+ ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->deflink.bssid);
if (ret)
IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
/* after sending it once, adopt mac80211 data */
- memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
+ memcpy(mvmvif->deflink.bssid, bss_conf->bssid, ETH_ALEN);
mvmvif->associated = vif->cfg.assoc;
if (changes & BSS_CHANGED_ASSOC) {
if (vif->cfg.assoc) {
+ mvmvif->session_prot_connection_loss = false;
+
/* clear statistics to get clean beacon counter */
iwl_mvm_request_statistics(mvm, true);
- memset(&mvmvif->beacon_stats, 0,
- sizeof(mvmvif->beacon_stats));
+ for_each_mvm_vif_valid_link(mvmvif, i)
+ memset(&mvmvif->link[i]->beacon_stats, 0,
+ sizeof(mvmvif->link[i]->beacon_stats));
/* add quota for this interface */
ret = iwl_mvm_update_quotas(mvm, true, NULL);
@@ -2408,7 +2860,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
* time could be small without us having heard
* a beacon yet.
*/
- iwl_mvm_protect_assoc(mvm, vif, 0);
+ iwl_mvm_protect_assoc(mvm, vif, 0, 0);
}
iwl_mvm_sf_update(mvm, vif, false);
@@ -2416,9 +2868,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
if (vif->p2p) {
iwl_mvm_update_smps(mvm, vif,
IWL_MVM_SMPS_REQ_PROT,
- IEEE80211_SMPS_DYNAMIC);
+ IEEE80211_SMPS_DYNAMIC, 0);
}
- } else if (mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) {
+ } else if (mvmvif->deflink.ap_sta_id != IWL_INVALID_STA) {
iwl_mvm_mei_host_disassociated(mvm);
/*
* If update fails - SF might be running in associated
@@ -2430,32 +2882,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
&mvm->status),
"Failed to update SF upon disassociation\n");
- /*
- * If we get an assert during the connection (after the
- * station has been added, but before the vif is set
- * to associated), mac80211 will re-add the station and
- * then configure the vif. Since the vif is not
- * associated, we would remove the station here and
- * this would fail the recovery.
- */
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status)) {
- /* first remove remaining keys */
- iwl_mvm_sec_key_remove_ap(mvm, vif);
-
- /*
- * Remove AP station now that
- * the MAC is unassoc
- */
- ret = iwl_mvm_rm_sta_id(mvm, vif,
- mvmvif->ap_sta_id);
- if (ret)
- IWL_ERR(mvm,
- "failed to remove AP station\n");
-
- mvmvif->ap_sta_id = IWL_MVM_INVALID_STA;
- }
-
/* remove quota for this interface */
ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
@@ -2469,67 +2895,52 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
vif->addr);
}
- /*
- * The firmware tracks the MU-MIMO group on its own.
- * However, on HW restart we should restore this data.
- */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
- (changes & BSS_CHANGED_MU_GROUPS) && vif->bss_conf.mu_mimo_owner) {
- ret = iwl_mvm_update_mu_groups(mvm, vif);
- if (ret)
- IWL_ERR(mvm,
- "failed to update VHT MU_MIMO groups\n");
- }
+ iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes);
+ }
- iwl_mvm_recalc_multicast(mvm);
+ iwl_mvm_bss_info_changed_station_common(mvm, vif, &vif->bss_conf,
+ changes);
+}
- /* reset rssi values */
- mvmvif->bf_data.ave_beacon_signal = 0;
+bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ int *ret)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int i;
- iwl_mvm_bt_coex_vif_change(mvm);
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
- IEEE80211_SMPS_AUTOMATIC);
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_UMAC_SCAN))
- iwl_mvm_config_scan(mvm);
- }
+ lockdep_assert_held(&mvm->mutex);
- if (changes & BSS_CHANGED_BEACON_INFO) {
- /*
- * We received a beacon from the associated AP so
- * remove the session protection.
- */
- iwl_mvm_stop_session_protection(mvm, vif);
+ mvmvif->ap_assoc_sta_count = 0;
- iwl_mvm_sf_update(mvm, vif, false);
- WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
- }
+ /* must be set before quota calculations */
+ mvmvif->ap_ibss_active = true;
- if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS |
- /*
- * Send power command on every beacon change,
- * because we may have not enabled beacon abort yet.
- */
- BSS_CHANGED_BEACON_INFO)) {
- ret = iwl_mvm_power_update_mac(mvm);
- if (ret)
- IWL_ERR(mvm, "failed to update power mode\n");
+ /* send all the early keys to the device now */
+ for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) {
+ struct ieee80211_key_conf *key = mvmvif->ap_early_keys[i];
+
+ if (!key)
+ continue;
+
+ mvmvif->ap_early_keys[i] = NULL;
+
+ *ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key);
+ if (*ret)
+ return true;
}
- if (changes & BSS_CHANGED_CQM) {
- IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");
- /* reset cqm events tracking */
- mvmvif->bf_data.last_cqm_event = 0;
- if (mvmvif->bf_data.bf_enabled) {
- ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
- if (ret)
- IWL_ERR(mvm,
- "failed to update CQM thresholds\n");
- }
+ if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
+ iwl_mvm_vif_set_low_latency(mvmvif, true,
+ LOW_LATENCY_VIF_TYPE);
+ iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id);
}
- if (changes & BSS_CHANGED_BANDWIDTH)
- iwl_mvm_apply_fw_smps_request(vif);
+ /* power updated needs to be done before quotas */
+ iwl_mvm_power_update_mac(mvm);
+
+ return false;
}
static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
@@ -2538,15 +2949,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret, i;
+ int ret;
mutex_lock(&mvm->mutex);
- /* Send the beacon template */
- ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif);
- if (ret)
- goto out_unlock;
-
/*
* Re-calculate the tsf id, as the leader-follower relations depend on
* the beacon interval, which was not known when the AP interface
@@ -2555,12 +2961,31 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_AP)
iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
- mvmvif->ap_assoc_sta_count = 0;
+ /* For older devices need to send beacon template before adding mac
+ * context. For the newer, the beacon is a resource that belongs to a
+ * MAC, so need to send beacon template after adding the mac.
+ */
+ if (mvm->trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_22000) {
+ /* Add the mac context */
+ ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+ if (ret)
+ goto out_unlock;
- /* Add the mac context */
- ret = iwl_mvm_mac_ctxt_add(mvm, vif);
- if (ret)
- goto out_unlock;
+ /* Send the beacon template */
+ ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf);
+ if (ret)
+ goto out_unlock;
+ } else {
+ /* Send the beacon template */
+ ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf);
+ if (ret)
+ goto out_unlock;
+
+ /* Add the mac context */
+ ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+ if (ret)
+ goto out_unlock;
+ }
/* Perform the binding */
ret = iwl_mvm_binding_add_vif(mvm, vif);
@@ -2602,35 +3027,12 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
}
}
- /* must be set before quota calculations */
- mvmvif->ap_ibss_active = true;
-
- /* send all the early keys to the device now */
- for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) {
- struct ieee80211_key_conf *key = mvmvif->ap_early_keys[i];
-
- if (!key)
- continue;
-
- mvmvif->ap_early_keys[i] = NULL;
-
- ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key);
- if (ret)
- goto out_quota_failed;
- }
-
- if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
- iwl_mvm_vif_set_low_latency(mvmvif, true,
- LOW_LATENCY_VIF_TYPE);
- iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id);
- }
-
- /* power updated needs to be done before quotas */
- iwl_mvm_power_update_mac(mvm);
+ if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret))
+ goto out_failed;
ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
- goto out_quota_failed;
+ goto out_failed;
/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
if (vif->p2p && mvm->p2p_device_vif)
@@ -2642,11 +3044,11 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (iwl_mvm_phy_ctx_count(mvm) > 1)
iwl_mvm_teardown_tdls_peers(mvm);
- iwl_mvm_ftm_restart_responder(mvm, vif);
+ iwl_mvm_ftm_restart_responder(mvm, vif, &vif->bss_conf);
goto out_unlock;
-out_quota_failed:
+out_failed:
iwl_mvm_power_update_mac(mvm);
mvmvif->ap_ibss_active = false;
iwl_mvm_send_rm_bcast_sta(mvm, vif);
@@ -2673,16 +3075,15 @@ static int iwl_mvm_start_ibss(struct ieee80211_hw *hw,
return iwl_mvm_start_ap_ibss(hw, vif, &vif->bss_conf);
}
-static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *link_conf)
+/* Common part for MLD and non-MLD ops */
+void iwl_mvm_stop_ap_ibss_common(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- iwl_mvm_prepare_mac_removal(mvm, vif);
+ lockdep_assert_held(&mvm->mutex);
- mutex_lock(&mvm->mutex);
+ iwl_mvm_prepare_mac_removal(mvm, vif);
/* Handle AP stop while in CSA */
if (rcu_access_pointer(mvm->csa_vif) == vif) {
@@ -2707,6 +3108,17 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
}
iwl_mvm_bt_coex_vif_change(mvm);
+}
+
+static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ guard(mvm)(mvm);
+
+ iwl_mvm_stop_ap_ibss_common(mvm, vif);
/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
if (vif->p2p && mvm->p2p_device_vif)
@@ -2734,8 +3146,6 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
iwl_mvm_power_update_mac(mvm);
iwl_mvm_mac_ctxt_remove(mvm, vif);
-
- mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_stop_ap(struct ieee80211_hw *hw,
@@ -2770,11 +3180,11 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
/* Need to send a new beacon template to the FW */
if (changes & BSS_CHANGED_BEACON &&
- iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
+ iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, &vif->bss_conf))
IWL_WARN(mvm, "Failed updating beacon data\n");
if (changes & BSS_CHANGED_FTM_RESPONDER) {
- int ret = iwl_mvm_ftm_start_responder(mvm, vif);
+ int ret = iwl_mvm_ftm_start_responder(mvm, vif, &vif->bss_conf);
if (ret)
IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n",
@@ -2790,7 +3200,7 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
if (changes & BSS_CHANGED_IDLE && !vif->cfg.idle)
iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
@@ -2815,51 +3225,42 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
if (changes & BSS_CHANGED_TXPOWER) {
IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n",
bss_conf->txpower);
- iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+ iwl_mvm_set_tx_power(mvm, bss_conf, bss_conf->txpower);
}
-
- mutex_unlock(&mvm->mutex);
}
-static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_scan_request *hw_req)
+int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
if (hw_req->req.n_channels == 0 ||
hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels)
return -EINVAL;
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies);
- mutex_unlock(&mvm->mutex);
-
- return ret;
+ guard(mvm)(mvm);
+ return iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies);
}
-static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
/* Due to a race condition, it's possible that mac80211 asks
* us to stop a hw_scan when it's already stopped. This can
* happen, for instance, if we stopped the scan ourselves,
* called ieee80211_scan_completed() and the userspace called
- * cancel scan scan before ieee80211_scan_work() could run.
+ * cancel scan before ieee80211_scan_work() could run.
* To handle that, simply return if the scan is not running.
*/
if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
-
- mutex_unlock(&mvm->mutex);
}
-static void
+void
iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tids,
int num_frames,
@@ -2874,7 +3275,7 @@ iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
tids, more_data, false);
}
-static void
+void
iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tids,
int num_frames,
@@ -2935,7 +3336,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
*/
break;
case STA_NOTIFY_AWAKE:
- if (WARN_ON(mvmsta->sta_id == IWL_MVM_INVALID_STA))
+ if (WARN_ON(mvmsta->deflink.sta_id == IWL_INVALID_STA))
break;
if (txqs)
@@ -2948,10 +3349,8 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
spin_unlock_bh(&mvmsta->lock);
}
-static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- enum sta_notify_cmd cmd,
- struct ieee80211_sta *sta)
+void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
{
__iwl_mvm_mac_sta_notify(hw, cmd, sta);
}
@@ -3009,12 +3408,15 @@ void iwl_mvm_sta_pm_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
rcu_read_unlock();
}
-static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ unsigned int link_id;
+
+ lockdep_assert_wiphy(mvm->hw->wiphy);
/*
* This is called before mac80211 does RCU synchronisation,
@@ -3023,13 +3425,26 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
* be able to find the station this way, and we don't rely
* on further RCU synchronisation after the sta_state()
* callback deleted the station.
+ * Since there's mvm->mutex here, no need to have RCU lock for
+ * mvm_sta->link access.
*/
- mutex_lock(&mvm->mutex);
- if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id]))
- rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
- ERR_PTR(-ENOENT));
+ guard(mvm)(mvm);
+ for (link_id = 0; link_id < ARRAY_SIZE(mvm_sta->link); link_id++) {
+ struct iwl_mvm_link_sta *link_sta;
+ u32 sta_id;
- mutex_unlock(&mvm->mutex);
+ if (!mvm_sta->link[link_id])
+ continue;
+
+ link_sta = rcu_dereference_protected(mvm_sta->link[link_id],
+ lockdep_is_held(&mvm->mutex));
+ sta_id = link_sta->sta_id;
+ if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[sta_id])) {
+ RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id],
+ ERR_PTR(-ENOENT));
+ RCU_INIT_POINTER(mvm->fw_id_to_link_sta[sta_id], NULL);
+ }
+ }
}
static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -3121,20 +3536,27 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
rcu_read_unlock();
}
-static void iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+static void
+iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id,
+ struct ieee80211_bss_conf *link_conf)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_he_obss_narrow_bw_ru_data iter_data = {
.tolerated = true,
};
- if (!(vif->bss_conf.chandef.chan->flags & IEEE80211_CHAN_RADAR)) {
- mvmvif->he_ru_2mhz_block = false;
+ if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan ||
+ !mvmvif->link[link_id]))
+ return;
+
+ if (!(link_conf->chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) {
+ mvmvif->link[link_id]->he_ru_2mhz_block = false;
return;
}
- cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chandef,
+ cfg80211_bss_iter(hw->wiphy, &link_conf->chanreq.oper,
iwl_mvm_check_he_obss_narrow_bw_ru_iter,
&iter_data);
@@ -3142,7 +3564,7 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
* If there is at least one AP on radar channel that cannot
* tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU.
*/
- mvmvif->he_ru_2mhz_block = !iter_data.tolerated;
+ mvmvif->link[link_id]->he_ru_2mhz_block = !iter_data.tolerated;
}
static void iwl_mvm_reset_cca_40mhz_workaround(struct iwl_mvm *mvm,
@@ -3166,8 +3588,7 @@ static void iwl_mvm_reset_cca_40mhz_workaround(struct iwl_mvm *mvm,
sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- he_cap = ieee80211_get_he_iftype_cap(sband,
- ieee80211_vif_type_p2p(vif));
+ he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
if (he_cap) {
/* we know that ours is writable */
@@ -3186,7 +3607,6 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mei_conn_info conn_info = {
.ssid_len = vif->cfg.ssid_len,
- .channel = vif->bss_conf.chandef.chan->hw_value,
};
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
@@ -3195,6 +3615,12 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm,
if (!mvm->mei_registered)
return;
+ /* FIXME: MEI needs to be updated for MLO */
+ if (!vif->bss_conf.chanreq.oper.chan)
+ return;
+
+ conn_info.channel = vif->bss_conf.chanreq.oper.chan->hw_value;
+
switch (mvm_sta->pairwise_cipher) {
case WLAN_CIPHER_SUITE_TKIP:
conn_info.pairwise_cipher = IWL_MEI_CIPHER_TKIP;
@@ -3244,24 +3670,350 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm,
#endif
}
+static int iwl_mvm_mac_ctxt_changed_wrapper(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool force_assoc_off)
+{
+ return iwl_mvm_mac_ctxt_changed(mvm, vif, force_assoc_off, NULL);
+}
+
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state)
{
+ static const struct iwl_mvm_sta_state_ops callbacks = {
+ .add_sta = iwl_mvm_add_sta,
+ .update_sta = iwl_mvm_update_sta,
+ .rm_sta = iwl_mvm_rm_sta,
+ .mac_ctxt_changed = iwl_mvm_mac_ctxt_changed_wrapper,
+ };
+
+ return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state,
+ &callbacks);
+}
+
+/* FIXME: temporary making two assumptions in all sta handling functions:
+ * (1) when setting sta state, the link exists and protected
+ * (2) if a link is valid in sta then it's valid in vif (can
+ * use same index in the link array)
+ */
+static void iwl_mvm_rs_rate_init_all_links(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ unsigned int link_id;
+
+ for_each_mvm_vif_valid_link(mvmvif, link_id) {
+ struct ieee80211_bss_conf *conf =
+ link_conf_dereference_check(vif, link_id);
+ struct ieee80211_link_sta *link_sta =
+ link_sta_dereference_check(sta, link_id);
+
+ if (!conf || !link_sta || !mvmvif->link[link_id]->phy_ctxt)
+ continue;
+
+ iwl_mvm_rs_rate_init(mvm, vif, sta, conf, link_sta,
+ mvmvif->link[link_id]->phy_ctxt->channel->band);
+ }
+}
+
+static bool iwl_mvm_vif_conf_from_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ /* Beacon interval check - firmware will crash if the beacon
+ * interval is less than 16. We can't avoid connecting at all,
+ * so refuse the station state change, this will cause mac80211
+ * to abandon attempts to connect to this AP, and eventually
+ * wpa_s will blocklist the AP...
+ */
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+
+ if (!link_conf)
+ continue;
+
+ if (link_conf->beacon_int < IWL_MVM_MIN_BEACON_INTERVAL_TU) {
+ IWL_ERR(mvm,
+ "Beacon interval %d for AP %pM is too small\n",
+ link_conf->beacon_int, link_sta->addr);
+ return false;
+ }
+
+ link_conf->he_support = link_sta->he_cap.has_he;
+ }
+
+ return true;
+}
+
+static void iwl_mvm_vif_set_he_support(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ bool is_sta)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+
+ if (!link_conf || !mvmvif->link[link_id])
+ continue;
+
+ link_conf->he_support = link_sta->he_cap.has_he;
+
+ if (is_sta) {
+ mvmvif->link[link_id]->he_ru_2mhz_block = false;
+ if (link_sta->he_cap.has_he)
+ iwl_mvm_check_he_obss_narrow_bw_ru(hw, vif,
+ link_id,
+ link_conf);
+ }
+ }
+}
+
+static int
+iwl_mvm_sta_state_notexist_to_none(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ const struct iwl_mvm_sta_state_ops *callbacks)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_link_sta *link_sta;
+ unsigned int i;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ !iwl_mvm_vif_conf_from_sta(mvm, vif, sta))
+ return -EINVAL;
+
+ if (sta->tdls &&
+ (vif->p2p ||
+ iwl_mvm_tdls_sta_count(mvm, NULL) == IWL_TDLS_STA_COUNT ||
+ iwl_mvm_phy_ctx_count(mvm) > 1)) {
+ IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
+ return -EBUSY;
+ }
+
+ ret = callbacks->add_sta(mvm, vif, sta);
+ if (sta->tdls && ret == 0) {
+ iwl_mvm_recalc_tdls_state(mvm, vif, true);
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_SETUP);
+ }
+
+ if (ret)
+ return ret;
+
+ for_each_sta_active_link(vif, sta, link_sta, i)
+ link_sta->agg.max_rc_amsdu_len = 1;
+
+ ieee80211_sta_recalc_aggregates(sta);
+
+ if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+ mvmvif->ap_sta = sta;
+
+ /*
+ * Initialize the rates here already - this really tells
+ * the firmware only what the supported legacy rates are
+ * (may be) since it's initialized already from what the
+ * AP advertised in the beacon/probe response. This will
+ * allow the firmware to send auth/assoc frames with one
+ * of the supported rates already, rather than having to
+ * use a mandatory rate.
+ * If we're the AP, we'll just assume mandatory rates at
+ * this point, but we know nothing about the STA anyway.
+ */
+ iwl_mvm_rs_rate_init_all_links(mvm, vif, sta);
+
+ return 0;
+}
+
+static int
+iwl_mvm_sta_state_auth_to_assoc(struct ieee80211_hw *hw,
+ struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ const struct iwl_mvm_sta_state_ops *callbacks)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ iwl_mvm_vif_set_he_support(hw, vif, sta, false);
+ mvmvif->ap_assoc_sta_count++;
+ callbacks->mac_ctxt_changed(mvm, vif, false);
+
+ /* since the below is not for MLD API, it's ok to use
+ * the default bss_conf
+ */
+ if (!mvm->mld_api_is_used &&
+ (vif->bss_conf.he_support &&
+ !iwlwifi_mod_params.disable_11ax))
+ iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->deflink.sta_id);
+ } else if (vif->type == NL80211_IFTYPE_STATION) {
+ iwl_mvm_vif_set_he_support(hw, vif, sta, true);
+
+ callbacks->mac_ctxt_changed(mvm, vif, false);
+
+ if (!mvm->mld_api_is_used)
+ goto out;
+
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ struct ieee80211_bss_conf *link_conf =
+ link_conf_dereference_protected(vif, link_id);
+
+ if (WARN_ON(!link_conf))
+ return -EINVAL;
+ if (!mvmvif->link[link_id])
+ continue;
+
+ iwl_mvm_link_changed(mvm, vif, link_conf,
+ LINK_CONTEXT_MODIFY_ALL &
+ ~LINK_CONTEXT_MODIFY_ACTIVE,
+ true);
+ }
+ }
+
+out:
+ iwl_mvm_rs_rate_init_all_links(mvm, vif, sta);
+
+ return callbacks->update_sta(mvm, vif, sta);
+}
+
+static int
+iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ const struct iwl_mvm_sta_state_ops *callbacks)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* we don't support TDLS during DCM */
+ if (iwl_mvm_phy_ctx_count(mvm) > 1)
+ iwl_mvm_teardown_tdls_peers(mvm);
+
+ if (sta->tdls) {
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_ENABLE_LINK);
+ } else {
+ /* enable beacon filtering */
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
+
+ mvmvif->authorized = 1;
+
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ mvmvif->link_selection_res = vif->active_links;
+ mvmvif->link_selection_primary =
+ vif->active_links ? __ffs(vif->active_links) : 0;
+ }
+
+ callbacks->mac_ctxt_changed(mvm, vif, false);
+ iwl_mvm_mei_host_associated(mvm, vif, mvm_sta);
+ }
+
+ mvm_sta->authorized = true;
+
+ /* MFP is set by default before the station is authorized.
+ * Clear it here in case it's not used.
+ */
+ if (!sta->mfp) {
+ int ret = callbacks->update_sta(mvm, vif, sta);
+
+ if (ret)
+ return ret;
+ }
+
+ iwl_mvm_rs_rate_init_all_links(mvm, vif, sta);
+
+ return 0;
+}
+
+static int
+iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ const struct iwl_mvm_sta_state_ops *callbacks)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ mvmsta->authorized = false;
+
+ /* once we move into assoc state, need to update rate scale to
+ * disable using wide bandwidth
+ */
+ iwl_mvm_rs_rate_init_all_links(mvm, vif, sta);
+
+ if (!sta->tdls) {
+ /* Set this but don't call iwl_mvm_mac_ctxt_changed()
+ * yet to avoid sending high prio again for a little
+ * time.
+ */
+ mvmvif->authorized = 0;
+ mvmvif->link_selection_res = 0;
+
+ /* disable beacon filtering */
+ iwl_mvm_disable_beacon_filter(mvm, vif);
+ }
+
+ return 0;
+}
+
+void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool update)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!iwl_mvm_has_rlc_offload(mvm) ||
+ iwl_fw_lookup_cmd_ver(mvm->fw, MAC_PM_POWER_TABLE, 0) >= 2)
+ return;
+
+ mvmvif->ps_disabled = !vif->cfg.ps;
+
+ if (update)
+ iwl_mvm_power_update_mac(mvm);
+}
+
+/* Common part for MLD and non-MLD modes */
+int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state,
+ const struct iwl_mvm_sta_state_ops *callbacks)
+{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
int ret;
IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",
sta->addr, old_state, new_state);
- /* this would be a mac80211 bug ... but don't crash */
- if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
- return test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) ? 0 : -EINVAL;
-
/*
* If we are in a STA removal flow and in DQA mode:
*
@@ -3289,51 +4041,36 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
* from the AP now.
*/
iwl_mvm_reset_cca_40mhz_workaround(mvm, vif);
+
+ /* Also free dup data just in case any assertions below fail */
+ kfree(mvm_sta->dup_data);
}
mutex_lock(&mvm->mutex);
+
+ /* this would be a mac80211 bug ... but don't crash, unless we had a
+ * firmware crash while we were activating a link, in which case it is
+ * legit to have phy_ctxt = NULL. Don't bother not to WARN if we are in
+ * recovery flow since we spit tons of error messages anyway.
+ */
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ if (WARN_ON_ONCE(!mvmvif->link[link_id] ||
+ !mvmvif->link[link_id]->phy_ctxt)) {
+ mutex_unlock(&mvm->mutex);
+ return test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+ &mvm->status) ? 0 : -EINVAL;
+ }
+ }
+
/* track whether or not the station is associated */
mvm_sta->sta_state = new_state;
if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
- /*
- * Firmware bug - it'll crash if the beacon interval is less
- * than 16. We can't avoid connecting at all, so refuse the
- * station state change, this will cause mac80211 to abandon
- * attempts to connect to this AP, and eventually wpa_s will
- * blocklist the AP...
- */
- if (vif->type == NL80211_IFTYPE_STATION &&
- vif->bss_conf.beacon_int < 16) {
- IWL_ERR(mvm,
- "AP %pM beacon interval is %d, refusing due to firmware bug!\n",
- sta->addr, vif->bss_conf.beacon_int);
- ret = -EINVAL;
- goto out_unlock;
- }
-
- if (vif->type == NL80211_IFTYPE_STATION)
- vif->bss_conf.he_support = sta->deflink.he_cap.has_he;
-
- if (sta->tdls &&
- (vif->p2p ||
- iwl_mvm_tdls_sta_count(mvm, NULL) ==
- IWL_MVM_TDLS_STA_COUNT ||
- iwl_mvm_phy_ctx_count(mvm) > 1)) {
- IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
- ret = -EBUSY;
+ ret = iwl_mvm_sta_state_notexist_to_none(mvm, vif, sta,
+ callbacks);
+ if (ret < 0)
goto out_unlock;
- }
-
- ret = iwl_mvm_add_sta(mvm, vif, sta);
- if (sta->tdls && ret == 0) {
- iwl_mvm_recalc_tdls_state(mvm, vif, true);
- iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
- NL80211_TDLS_SETUP);
- }
-
- sta->deflink.agg.max_rc_amsdu_len = 1;
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_AUTH) {
/*
@@ -3345,85 +4082,22 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = 0;
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC) {
- if (vif->type == NL80211_IFTYPE_AP) {
- vif->bss_conf.he_support = sta->deflink.he_cap.has_he;
- mvmvif->ap_assoc_sta_count++;
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- if ((vif->bss_conf.he_support &&
- !iwlwifi_mod_params.disable_11ax) ||
- (vif->bss_conf.eht_support &&
- !iwlwifi_mod_params.disable_11be))
- iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
- } else if (vif->type == NL80211_IFTYPE_STATION) {
- vif->bss_conf.he_support = sta->deflink.he_cap.has_he;
-
- mvmvif->he_ru_2mhz_block = false;
- if (sta->deflink.he_cap.has_he)
- iwl_mvm_check_he_obss_narrow_bw_ru(hw, vif);
-
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- }
-
- iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
- false);
- ret = iwl_mvm_update_sta(mvm, vif, sta);
+ ret = iwl_mvm_sta_state_auth_to_assoc(hw, mvm, vif, sta,
+ callbacks);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
- ret = 0;
-
- /* we don't support TDLS during DCM */
- if (iwl_mvm_phy_ctx_count(mvm) > 1)
- iwl_mvm_teardown_tdls_peers(mvm);
-
- if (sta->tdls) {
- iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
- NL80211_TDLS_ENABLE_LINK);
- } else {
- /* enable beacon filtering */
- WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
-
- mvmvif->authorized = 1;
-
- /*
- * Now that the station is authorized, i.e., keys were already
- * installed, need to indicate to the FW that
- * multicast data frames can be forwarded to the driver
- */
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- iwl_mvm_mei_host_associated(mvm, vif, mvm_sta);
- }
-
- iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
- true);
+ ret = iwl_mvm_sta_state_assoc_to_authorized(mvm, vif, sta,
+ callbacks);
+ iwl_mvm_smps_workaround(mvm, vif, true);
} else if (old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
- /* once we move into assoc state, need to update rate scale to
- * disable using wide bandwidth
- */
- iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
- false);
- if (!sta->tdls) {
- /* Multicast data frames are no longer allowed */
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
-
- /*
- * Set this after the above iwl_mvm_mac_ctxt_changed()
- * to avoid sending high prio again for a little time.
- */
- mvmvif->authorized = 0;
-
- /* disable beacon filtering */
- ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
- WARN_ON(ret &&
- !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
- &mvm->status));
- }
- ret = 0;
+ ret = iwl_mvm_sta_state_authorized_to_assoc(mvm, vif, sta,
+ callbacks);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
if (vif->type == NL80211_IFTYPE_AP) {
mvmvif->ap_assoc_sta_count--;
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ callbacks->mac_ctxt_changed(mvm, vif, false);
} else if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
iwl_mvm_stop_session_protection(mvm, vif);
ret = 0;
@@ -3432,9 +4106,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = 0;
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST) {
- if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
+ if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) {
iwl_mvm_stop_session_protection(mvm, vif);
- ret = iwl_mvm_rm_sta(mvm, vif, sta);
+ mvmvif->ap_sta = NULL;
+ }
+ ret = callbacks->rm_sta(mvm, vif, sta);
if (sta->tdls) {
iwl_mvm_recalc_tdls_state(mvm, vif, false);
iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
@@ -3463,7 +4139,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
return ret;
}
-static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx,
+ u32 value)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -3472,18 +4149,16 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
return 0;
}
-static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u32 changed)
+void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta, u32 changed)
{
+ struct ieee80211_sta *sta = link_sta->sta;
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (changed & (IEEE80211_RC_BW_CHANGED |
IEEE80211_RC_SUPP_RATES_CHANGED |
IEEE80211_RC_NSS_CHANGED))
- iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
- true);
+ iwl_mvm_rs_rate_init_all_links(mvm, vif, sta);
if (vif->type == NL80211_IFTYPE_STATION &&
changed & IEEE80211_RC_NSS_CHANGED)
@@ -3498,37 +4173,36 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- mvmvif->queue_params[ac] = *params;
+ mvmvif->deflink.queue_params[ac] = *params;
/*
* No need to update right away, we'll get BSS_CHANGED_QOS
* The exception is P2P_DEVICE interface which needs immediate update.
*/
if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- int ret;
-
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- mutex_unlock(&mvm->mutex);
- return ret;
+ guard(mvm)(mvm);
+ return iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}
return 0;
}
-static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_prep_tx_info *info)
+void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
- iwl_mvm_protect_assoc(mvm, vif, info->duration);
- mutex_unlock(&mvm->mutex);
+ if (info->was_assoc && !mvmvif->session_prot_connection_loss)
+ return;
+
+ guard(mvm)(mvm);
+ iwl_mvm_protect_assoc(mvm, vif, info->duration, info->link_id);
}
-static void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_prep_tx_info *info)
+void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -3536,36 +4210,27 @@ static void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw,
if (info->success)
return;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
iwl_mvm_stop_session_protection(mvm, vif);
- mutex_unlock(&mvm->mutex);
}
-static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_sched_scan_request *req,
- struct ieee80211_scan_ies *ies)
+int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
+ guard(mvm)(mvm);
- mutex_lock(&mvm->mutex);
-
- if (!vif->cfg.idle) {
- ret = -EBUSY;
- goto out;
- }
-
- ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED);
+ if (!vif->cfg.idle)
+ return -EBUSY;
-out:
- mutex_unlock(&mvm->mutex);
- return ret;
+ return iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED);
}
-static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
@@ -3576,7 +4241,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
* us to stop a sched_scan when it's already stopped. This
* can happen, for instance, if we stopped the scan ourselves,
* called ieee80211_sched_scan_stopped() and the userspace called
- * stop sched scan scan before ieee80211_sched_scan_stopped_work()
+ * stop sched scan before ieee80211_sched_scan_stopped_work()
* could run. To handle this, simply return if the scan is
* not running.
*/
@@ -3601,7 +4266,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_sta *mvmsta = NULL;
- struct iwl_mvm_key_pn *ptk_pn;
+ struct iwl_mvm_key_pn *ptk_pn = NULL;
int keyidx = key->keyidx;
u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0);
@@ -3613,7 +4278,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
switch (key->cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- if (!mvm->trans->trans_cfg->gen2) {
+ if (!mvm->trans->mac_cfg->gen2) {
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
} else if (vif->type == NL80211_IFTYPE_STATION) {
@@ -3648,7 +4313,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
switch (cmd) {
case SET_KEY:
- if (keyidx == 6 || keyidx == 7)
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ (keyidx == 6 || keyidx == 7))
rcu_assign_pointer(mvmvif->bcn_prot.keys[keyidx - 6],
key);
@@ -3658,11 +4324,24 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
* GTK on AP interface is a TX-only key, return 0;
* on IBSS they're per-station and because we're lazy
* we don't support them for RX, so do the same.
- * CMAC/GMAC in AP/IBSS modes must be done in software.
+ * CMAC/GMAC in AP/IBSS modes must be done in software
+ * on older NICs.
+ *
+ * Except, of course, beacon protection - it must be
+ * offloaded since we just set a beacon template, and
+ * then we must also offload the IGTK (not just BIGTK)
+ * for firmware reasons.
+ *
+ * So just check for beacon protection - if we don't
+ * have it we cannot get here with keyidx >= 6, and
+ * if we do have it we need to send the key to FW in
+ * all cases (CMAC/GMAC).
*/
- if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
- key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
- key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
+ if (!wiphy_ext_feature_isset(hw->wiphy,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION) &&
+ (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
+ key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
+ key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256)) {
ret = -EOPNOTSUPP;
break;
}
@@ -3716,7 +4395,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx]));
ptk_pn = kzalloc(struct_size(ptk_pn, q,
- mvm->trans->num_rx_queues),
+ mvm->trans->info.num_rxqs),
GFP_KERNEL);
if (!ptk_pn) {
ret = -ENOMEM;
@@ -3725,7 +4404,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
ieee80211_get_key_rx_seq(key, tid, &seq);
- for (q = 0; q < mvm->trans->num_rx_queues; q++)
+ for (q = 0; q < mvm->trans->info.num_rxqs; q++)
memcpy(ptk_pn->q[q].pn[tid],
seq.ccmp.pn,
IEEE80211_CCMP_PN_LEN);
@@ -3743,7 +4422,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
if (mvmsta && key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
mvmsta->pairwise_cipher = key->cipher;
- IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
+ IWL_DEBUG_MAC80211(mvm, "set hwcrypto key (sta:%pM, id:%d)\n",
+ sta ? sta->addr : NULL, key->keyidx);
if (sec_key_ver)
ret = iwl_mvm_sec_key_add(mvm, vif, sta, key);
@@ -3753,6 +4433,10 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
if (ret) {
IWL_WARN(mvm, "set key failed\n");
key->hw_key_idx = STA_KEY_IDX_INVALID;
+ if (ptk_pn) {
+ RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL);
+ kfree(ptk_pn);
+ }
/*
* can't add key for RX, but we don't need it
* in the device for TX so still return 0,
@@ -3767,7 +4451,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
break;
case DISABLE_KEY:
- if (keyidx == 6 || keyidx == 7)
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ (keyidx == 6 || keyidx == 7))
RCU_INIT_POINTER(mvmvif->bcn_prot.keys[keyidx - 6],
NULL);
@@ -3814,27 +4499,25 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
return ret;
}
-static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
- enum set_key_cmd cmd,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key)
+int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
- mutex_lock(&mvm->mutex);
- ret = __iwl_mvm_mac_set_key(hw, cmd, vif, sta, key);
- mutex_unlock(&mvm->mutex);
+ /* When resuming from wowlan, FW already knows about the newest keys */
+ if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))
+ return 0;
- return ret;
+ guard(mvm)(mvm);
+ return __iwl_mvm_mac_set_key(hw, cmd, vif, sta, key);
}
-static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_key_conf *keyconf,
- struct ieee80211_sta *sta,
- u32 iv32, u16 *phase1key)
+void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf,
+ struct ieee80211_sta *sta,
+ u32 iv32, u16 *phase1key)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -3879,11 +4562,6 @@ static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
return true;
}
-#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100)
-#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200)
-#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600)
-#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20)
-#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10)
static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
struct ieee80211_channel *channel,
struct ieee80211_vif *vif,
@@ -3894,8 +4572,6 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
static const u16 time_event_response[] = { HOT_SPOT_CMD };
struct iwl_notification_wait wait_time_event;
- u32 dtim_interval = vif->bss_conf.dtim_period *
- vif->bss_conf.beacon_int;
u32 req_dur, delay;
struct iwl_hs20_roc_req aux_roc_req = {
.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
@@ -3916,29 +4592,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
/* Set the time and duration */
tail->apply_time = cpu_to_le32(iwl_mvm_get_systime(mvm));
- delay = AUX_ROC_MIN_DELAY;
- req_dur = MSEC_TO_TU(duration);
-
- /*
- * If we are associated we want the delay time to be at least one
- * dtim interval so that the FW can wait until after the DTIM and
- * then start the time event, this will potentially allow us to
- * remain off-channel for the max duration.
- * Since we want to use almost a whole dtim interval we would also
- * like the delay to be for 2-3 dtim intervals, in case there are
- * other time events with higher priority.
- */
- if (vif->cfg.assoc) {
- delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY);
- /* We cannot remain off-channel longer than the DTIM interval */
- if (dtim_interval <= req_dur) {
- req_dur = dtim_interval - AUX_ROC_SAFETY_BUFFER;
- if (req_dur <= AUX_ROC_MIN_DURATION)
- req_dur = dtim_interval -
- AUX_ROC_MIN_SAFETY_BUFFER;
- }
- }
-
+ iwl_mvm_roc_duration_and_delay(vif, duration, &req_dur, &delay);
tail->duration = cpu_to_le32(req_dur);
tail->apply_time_max_delay = cpu_to_le32(delay);
@@ -3946,8 +4600,8 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
"ROC: Requesting to remain on channel %u for %ums\n",
channel->hw_value, req_dur);
IWL_DEBUG_TE(mvm,
- "\t(requested = %ums, max_delay = %ums, dtim_interval = %ums)\n",
- duration, delay, dtim_interval);
+ "\t(requested = %ums, max_delay = %ums)\n",
+ duration, delay);
/* Set the node address */
memcpy(tail->node_addr, vif->addr, ETH_ALEN);
@@ -4005,204 +4659,263 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
return res;
}
+static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id)
+{
+ int ret = 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) {
+ IWL_ERR(mvm, "hotspot not supported\n");
+ return -EINVAL;
+ }
+
+ if (iwl_mvm_has_new_station_api(mvm->fw)) {
+ ret = iwl_mvm_add_aux_sta(mvm, lmac_id);
+ WARN(ret, "Failed to allocate aux station");
+ }
+
+ return ret;
+}
+
+static int iwl_mvm_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+ return ret;
+
+ /* The station and queue allocation must be done only after the binding
+ * is done, as otherwise the FW might incorrectly configure its state.
+ */
+ return iwl_mvm_add_p2p_bcast_sta(mvm, vif);
+}
+
static int iwl_mvm_roc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *channel,
int duration,
enum ieee80211_roc_type type)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct cfg80211_chan_def chandef;
- struct iwl_mvm_phy_ctxt *phy_ctxt;
- bool band_change_removal;
- int ret, i;
+ static const struct iwl_mvm_roc_ops ops = {
+ .add_aux_sta_for_hs20 = iwl_mvm_add_aux_sta_for_hs20,
+ .link = iwl_mvm_roc_link,
+ };
- IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
- duration, type);
+ return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops);
+}
- /*
- * Flush the done work, just in case it's still pending, so that
- * the work it does can complete and we can accept new frames.
- */
- flush_work(&mvm->roc_done_wk);
+static int iwl_mvm_roc_station(struct iwl_mvm *mvm,
+ struct ieee80211_channel *channel,
+ struct ieee80211_vif *vif,
+ int duration)
+{
+ int ret;
+ u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, ROC_CMD);
+ u8 fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
+ IWL_FW_CMD_VER_UNKNOWN);
+
+ if (fw_ver == IWL_FW_CMD_VER_UNKNOWN) {
+ ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, vif, duration);
+ } else if (fw_ver >= 3) {
+ ret = iwl_mvm_roc_add_cmd(mvm, channel, vif, duration,
+ ROC_ACTIVITY_HOTSPOT);
+ } else {
+ ret = -EOPNOTSUPP;
+ IWL_ERR(mvm, "ROC command version %d mismatch!\n", fw_ver);
+ }
- mutex_lock(&mvm->mutex);
+ return ret;
+}
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) {
- /* Use aux roc framework (HS20) */
- if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12) {
- u32 lmac_id;
-
- lmac_id = iwl_mvm_get_lmac_id(mvm->fw,
- channel->band);
- ret = iwl_mvm_add_aux_sta(mvm, lmac_id);
- if (WARN(ret,
- "Failed to allocate aux station"))
- goto out_unlock;
- }
- ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
- vif, duration);
- goto out_unlock;
- }
- IWL_ERR(mvm, "hotspot not supported\n");
- ret = -EINVAL;
- goto out_unlock;
- case NL80211_IFTYPE_P2P_DEVICE:
- /* handle below */
+static int iwl_mvm_roc_p2p(struct iwl_mvm *mvm,
+ struct ieee80211_channel *channel,
+ struct ieee80211_vif *vif,
+ int duration,
+ enum ieee80211_roc_type type)
+{
+ enum iwl_roc_activity activity;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ switch (type) {
+ case IEEE80211_ROC_TYPE_NORMAL:
+ activity = ROC_ACTIVITY_P2P_DISC;
+ break;
+ case IEEE80211_ROC_TYPE_MGMT_TX:
+ activity = ROC_ACTIVITY_P2P_NEG;
break;
default:
- IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
- ret = -EINVAL;
- goto out_unlock;
+ WARN_ONCE(1, "Got an invalid P2P ROC type\n");
+ return -EINVAL;
}
- for (i = 0; i < NUM_PHY_CTX; i++) {
- phy_ctxt = &mvm->phy_ctxts[i];
- if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
- continue;
+ ret = iwl_mvm_mld_add_aux_sta(mvm,
+ iwl_mvm_get_lmac_id(mvm, channel->band));
+ if (ret)
+ return ret;
- if (phy_ctxt->ref && channel == phy_ctxt->channel) {
- /*
- * Unbind the P2P_DEVICE from the current PHY context,
- * and if the PHY context is not used remove it.
- */
- ret = iwl_mvm_binding_remove_vif(mvm, vif);
- if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
- goto out_unlock;
+ return iwl_mvm_roc_add_cmd(mvm, channel, vif, duration, activity);
+}
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+static int iwl_mvm_p2p_find_phy_ctxt(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *channel)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct cfg80211_chan_def chandef;
+ int i;
- /* Bind the P2P_DEVICE to the current PHY Context */
- mvmvif->phy_ctxt = phy_ctxt;
+ lockdep_assert_held(&mvm->mutex);
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (WARN(ret, "Failed binding P2P_DEVICE\n"))
- goto out_unlock;
+ if (mvmvif->deflink.phy_ctxt &&
+ channel == mvmvif->deflink.phy_ctxt->channel)
+ return 0;
- iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
- goto schedule_time_event;
+ /* Try using a PHY context that is already in use */
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[i];
+
+ if (!phy_ctxt->ref || mvmvif->deflink.phy_ctxt == phy_ctxt)
+ continue;
+
+ if (channel == phy_ctxt->channel) {
+ if (mvmvif->deflink.phy_ctxt)
+ iwl_mvm_phy_ctxt_unref(mvm,
+ mvmvif->deflink.phy_ctxt);
+
+ mvmvif->deflink.phy_ctxt = phy_ctxt;
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt);
+ return 0;
}
}
- /* Need to update the PHY context only if the ROC channel changed */
- if (channel == mvmvif->phy_ctxt->channel)
- goto schedule_time_event;
+ /* We already have a phy_ctxt, but it's not on the right channel */
+ if (mvmvif->deflink.phy_ctxt)
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt);
+
+ mvmvif->deflink.phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!mvmvif->deflink.phy_ctxt)
+ return -ENOSPC;
cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
- /*
- * Check if the remain-on-channel is on a different band and that
- * requires context removal, see iwl_mvm_phy_ctxt_changed(). If
- * so, we'll need to release and then re-configure here, since we
- * must not remove a PHY context that's part of a binding.
- */
- band_change_removal =
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
- mvmvif->phy_ctxt->channel->band != chandef.chan->band;
+ return iwl_mvm_phy_ctxt_add(mvm, mvmvif->deflink.phy_ctxt,
+ &chandef, NULL, 1, 1);
+}
- if (mvmvif->phy_ctxt->ref == 1 && !band_change_removal) {
- /*
- * Change the PHY context configuration as it is currently
- * referenced only by the P2P Device MAC (and we can modify it)
- */
- ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
- &chandef, 1, 1);
- if (ret)
- goto out_unlock;
- } else {
- /*
- * The PHY context is shared with other MACs (or we're trying to
- * switch bands), so remove the P2P Device from the binding,
- * allocate an new PHY context and create a new binding.
- */
- phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
- if (!phy_ctxt) {
- ret = -ENOSPC;
- goto out_unlock;
- }
+/* Execute the common part for MLD and non-MLD modes */
+int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_channel *channel, int duration,
+ enum ieee80211_roc_type type,
+ const struct iwl_mvm_roc_ops *ops)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u32 lmac_id;
+ int ret;
- ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
- 1, 1);
- if (ret) {
- IWL_ERR(mvm, "Failed to change PHY context\n");
- goto out_unlock;
- }
+ IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+ duration, type);
- /* Unbind the P2P_DEVICE from the current PHY context */
- ret = iwl_mvm_binding_remove_vif(mvm, vif);
- if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
- goto out_unlock;
+ /*
+ * Flush the done work, just in case it's still pending, so that
+ * the work it does can complete and we can accept new frames.
+ */
+ flush_work(&mvm->roc_done_wk);
- iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+ guard(mvm)(mvm);
- /* Bind the P2P_DEVICE to the new allocated PHY context */
- mvmvif->phy_ctxt = phy_ctxt;
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ lmac_id = iwl_mvm_get_lmac_id(mvm, channel->band);
- ret = iwl_mvm_binding_add_vif(mvm, vif);
- if (WARN(ret, "Failed binding P2P_DEVICE\n"))
- goto out_unlock;
+ /* Use aux roc framework (HS20) */
+ ret = ops->add_aux_sta_for_hs20(mvm, lmac_id);
+ if (!ret)
+ ret = iwl_mvm_roc_station(mvm, channel, vif, duration);
+ return ret;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* handle below */
+ break;
+ default:
+ IWL_ERR(mvm, "ROC: Invalid vif type=%u\n", vif->type);
+ return -EINVAL;
+ }
- iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+ if (iwl_mvm_has_p2p_over_aux(mvm)) {
+ ret = iwl_mvm_roc_p2p(mvm, channel, vif, duration, type);
+ return ret;
}
-schedule_time_event:
- /* Schedule the time events */
- ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
+ ret = iwl_mvm_p2p_find_phy_ctxt(mvm, vif, channel);
+ if (ret)
+ return ret;
-out_unlock:
- mutex_unlock(&mvm->mutex);
- IWL_DEBUG_MAC80211(mvm, "leave\n");
- return ret;
+ ret = ops->link(mvm, vif);
+ if (ret)
+ return ret;
+
+ return iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
}
-static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+int iwl_mvm_cancel_roc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
IWL_DEBUG_MAC80211(mvm, "enter\n");
- mutex_lock(&mvm->mutex);
iwl_mvm_stop_roc(mvm, vif);
- mutex_unlock(&mvm->mutex);
IWL_DEBUG_MAC80211(mvm, "leave\n");
return 0;
}
-struct iwl_mvm_ftm_responder_iter_data {
- bool responder;
+struct iwl_mvm_chanctx_usage_data {
+ struct iwl_mvm *mvm;
struct ieee80211_chanctx_conf *ctx;
+ bool use_def;
};
-static void iwl_mvm_ftm_responder_chanctx_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
+static void iwl_mvm_chanctx_usage_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
{
- struct iwl_mvm_ftm_responder_iter_data *data = _data;
+ struct iwl_mvm_chanctx_usage_data *data = _data;
+ struct ieee80211_bss_conf *link_conf;
+ int link_id;
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx)
+ continue;
+
+ if (iwl_mvm_enable_fils(data->mvm, vif, data->ctx))
+ data->use_def = true;
- if (rcu_access_pointer(vif->bss_conf.chanctx_conf) == data->ctx &&
- vif->type == NL80211_IFTYPE_AP && vif->bss_conf.ftmr_params)
- data->responder = true;
+ if (vif->type == NL80211_IFTYPE_AP && link_conf->ftmr_params)
+ data->use_def = true;
+ }
}
-static bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_chanctx_conf *ctx)
+struct cfg80211_chan_def *
+iwl_mvm_chanctx_def(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx)
{
- struct iwl_mvm_ftm_responder_iter_data data = {
- .responder = false,
+ struct iwl_mvm_chanctx_usage_data data = {
+ .mvm = mvm,
.ctx = ctx,
+ .use_def = false,
};
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_ftm_responder_chanctx_iter,
- &data);
- return data.responder;
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_chanctx_usage_iter,
+ &data);
+
+ return data.use_def ? &ctx->def : &ctx->min_def;
}
static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
@@ -4210,8 +4923,7 @@ static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
{
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt;
- bool responder = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx);
- struct cfg80211_chan_def *def = responder ? &ctx->def : &ctx->min_def;
+ struct cfg80211_chan_def *def = iwl_mvm_chanctx_def(mvm, ctx);
int ret;
lockdep_assert_held(&mvm->mutex);
@@ -4224,31 +4936,26 @@ static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
goto out;
}
- ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def,
- ctx->rx_chains_static,
- ctx->rx_chains_dynamic);
+ ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, def, &ctx->ap,
+ ctx->rx_chains_static,
+ ctx->rx_chains_dynamic);
if (ret) {
IWL_ERR(mvm, "Failed to add PHY context\n");
goto out;
}
- iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
*phy_ctxt_id = phy_ctxt->id;
out:
return ret;
}
-static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx)
+int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
- mutex_lock(&mvm->mutex);
- ret = __iwl_mvm_add_chanctx(mvm, ctx);
- mutex_unlock(&mvm->mutex);
-
- return ret;
+ guard(mvm)(mvm);
+ return __iwl_mvm_add_chanctx(mvm, ctx);
}
static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
@@ -4262,70 +4969,70 @@ static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
}
-static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx)
+void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
__iwl_mvm_remove_chanctx(mvm, ctx);
- mutex_unlock(&mvm->mutex);
}
-static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx,
- u32 changed)
+void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx, u32 changed)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
- bool responder = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx);
- struct cfg80211_chan_def *def = responder ? &ctx->def : &ctx->min_def;
+ struct cfg80211_chan_def *def = iwl_mvm_chanctx_def(mvm, ctx);
if (WARN_ONCE((phy_ctxt->ref > 1) &&
(changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
IEEE80211_CHANCTX_CHANGE_RADAR |
- IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)),
+ IEEE80211_CHANCTX_CHANGE_MIN_DEF)),
"Cannot change PHY. Ref=%d, changed=0x%X\n",
phy_ctxt->ref, changed))
return;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
/* we are only changing the min_width, may be a noop */
- if (changed == IEEE80211_CHANCTX_CHANGE_MIN_WIDTH) {
+ if (changed == IEEE80211_CHANCTX_CHANGE_MIN_DEF) {
if (phy_ctxt->width == def->width)
- goto out_unlock;
+ return;
/* we are just toggling between 20_NOHT and 20 */
if (phy_ctxt->width <= NL80211_CHAN_WIDTH_20 &&
def->width <= NL80211_CHAN_WIDTH_20)
- goto out_unlock;
+ return;
}
iwl_mvm_bt_coex_vif_change(mvm);
- iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def,
+ iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def, &ctx->ap,
ctx->rx_chains_static,
ctx->rx_chains_dynamic);
-
-out_unlock:
- mutex_unlock(&mvm->mutex);
}
-static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx,
- bool switching_chanctx)
+/*
+ * This function executes the common part for MLD and non-MLD modes.
+ *
+ * Returns true if we're done assigning the chanctx
+ * (either on failure or success)
+ */
+static bool
+__iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx, int *ret)
{
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int ret;
lockdep_assert_held(&mvm->mutex);
- mvmvif->phy_ctxt = phy_ctxt;
+ mvmvif->deflink.phy_ctxt = phy_ctxt;
switch (vif->type) {
case NL80211_IFTYPE_AP:
@@ -4340,19 +5047,36 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
* The AP binding flow is handled as part of the start_ap flow
* (in bss_info_changed), similarly for IBSS.
*/
- ret = 0;
- goto out;
+ *ret = 0;
+ return true;
case NL80211_IFTYPE_STATION:
- mvmvif->csa_bcn_pending = false;
break;
case NL80211_IFTYPE_MONITOR:
/* always disable PS when a monitor interface is active */
mvmvif->ps_disabled = true;
break;
default:
- ret = -EINVAL;
- goto out;
+ *ret = -EINVAL;
+ return true;
}
+ return false;
+}
+
+static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (WARN_ON(!link_conf))
+ return -EINVAL;
+
+ if (__iwl_mvm_assign_vif_chanctx_common(mvm, vif, ctx,
+ switching_chanctx, &ret))
+ goto out;
ret = iwl_mvm_binding_add_vif(mvm, vif);
if (ret)
@@ -4386,12 +5110,17 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}
- if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (!switching_chanctx) {
+ mvmvif->csa_bcn_pending = false;
+ goto out;
+ }
+
mvmvif->csa_bcn_pending = true;
if (!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
- u32 duration = 3 * vif->bss_conf.beacon_int;
+ u32 duration = 5 * vif->bss_conf.beacon_int;
/* Protect the session to make sure we hear the first
* beacon on the new channel.
@@ -4402,6 +5131,10 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
}
iwl_mvm_update_quotas(mvm, false, NULL);
+
+ iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif,
+ link_conf,
+ false);
}
goto out;
@@ -4411,47 +5144,48 @@ out_remove_binding:
iwl_mvm_power_update_mac(mvm);
out:
if (ret)
- mvmvif->phy_ctxt = NULL;
+ mvmvif->deflink.phy_ctxt = NULL;
return ret;
}
+
static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
- mutex_lock(&mvm->mutex);
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
- mutex_unlock(&mvm->mutex);
-
- return ret;
+ guard(mvm)(mvm);
+ return __iwl_mvm_assign_vif_chanctx(mvm, vif, link_conf, ctx, false);
}
-static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx,
- bool switching_chanctx)
+/*
+ * This function executes the common part for MLD and non-MLD modes.
+ *
+ * Returns if chanctx unassign chanctx is done
+ * (either on failure or success)
+ */
+static bool __iwl_mvm_unassign_vif_chanctx_common(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool switching_chanctx)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct ieee80211_vif *disabled_vif = NULL;
lockdep_assert_held(&mvm->mutex);
- iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
+ iwl_mvm_remove_time_event(mvm, mvmvif,
+ &mvmvif->time_event_data);
switch (vif->type) {
case NL80211_IFTYPE_ADHOC:
- goto out;
+ return true;
case NL80211_IFTYPE_MONITOR:
mvmvif->monitor_active = false;
mvmvif->ps_disabled = false;
- iwl_mvm_rm_snif_sta(mvm, vif);
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
if (!switching_chanctx || !mvmvif->ap_ibss_active)
- goto out;
+ return true;
mvmvif->csa_countdown = false;
@@ -4463,18 +5197,33 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
mvmvif->ap_ibss_active = false;
break;
- case NL80211_IFTYPE_STATION:
- if (!switching_chanctx)
- break;
+ default:
+ break;
+ }
+ return false;
+}
+
+static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_vif *disabled_vif = NULL;
+
+ if (__iwl_mvm_unassign_vif_chanctx_common(mvm, vif, switching_chanctx))
+ goto out;
+
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+ iwl_mvm_rm_snif_sta(mvm, vif);
- disabled_vif = vif;
+ if (vif->type == NL80211_IFTYPE_STATION && switching_chanctx) {
+ disabled_vif = vif;
if (!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD))
iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
- break;
- default:
- break;
}
iwl_mvm_update_quotas(mvm, false, disabled_vif);
@@ -4484,7 +5233,7 @@ out:
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD) &&
switching_chanctx)
return;
- mvmvif->phy_ctxt = NULL;
+ mvmvif->deflink.phy_ctxt = NULL;
iwl_mvm_power_update_mac(mvm);
}
@@ -4495,19 +5244,20 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
- __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
- mutex_unlock(&mvm->mutex);
+ guard(mvm)(mvm);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vif, link_conf, ctx, false);
}
static int
iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
- struct ieee80211_vif_chanctx_switch *vifs)
+ struct ieee80211_vif_chanctx_switch *vifs,
+ const struct iwl_mvm_switch_vif_chanctx_ops *ops)
{
int ret;
- mutex_lock(&mvm->mutex);
- __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+ guard(mvm)(mvm);
+ ops->__unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx, true);
__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
@@ -4516,8 +5266,8 @@ iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
goto out_reassign;
}
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
- true);
+ ret = ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].new_ctx, true);
if (ret) {
IWL_ERR(mvm,
"failed to assign new_ctx during channel switch\n");
@@ -4528,7 +5278,7 @@ iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
if (iwl_mvm_phy_ctx_count(mvm) > 1)
iwl_mvm_teardown_tdls_peers(mvm);
- goto out;
+ return 0;
out_remove:
__iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
@@ -4539,66 +5289,63 @@ out_reassign:
goto out_restart;
}
- if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
- true)) {
+ if (ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx, true)) {
IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
goto out_restart;
}
- goto out;
+ return ret;
out_restart:
/* things keep failing, better restart the hw */
- iwl_mvm_nic_restart(mvm, false);
-
-out:
- mutex_unlock(&mvm->mutex);
-
+ iwl_force_nmi(mvm->trans);
return ret;
}
static int
iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
- struct ieee80211_vif_chanctx_switch *vifs)
+ struct ieee80211_vif_chanctx_switch *vifs,
+ const struct iwl_mvm_switch_vif_chanctx_ops *ops)
{
int ret;
- mutex_lock(&mvm->mutex);
- __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+ guard(mvm)(mvm);
+ ops->__unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx, true);
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
- true);
+ ret = ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].new_ctx, true);
if (ret) {
IWL_ERR(mvm,
"failed to assign new_ctx during channel switch\n");
goto out_reassign;
}
- goto out;
+ return 0;
out_reassign:
- if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
- true)) {
+ if (ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf,
+ vifs[0].old_ctx, true)) {
IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
goto out_restart;
}
- goto out;
+ return ret;
out_restart:
/* things keep failing, better restart the hw */
- iwl_mvm_nic_restart(mvm, false);
-
-out:
- mutex_unlock(&mvm->mutex);
-
+ iwl_force_nmi(mvm->trans);
return ret;
}
-static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif_chanctx_switch *vifs,
- int n_vifs,
- enum ieee80211_chanctx_switch_mode mode)
+/* Execute the common part for both MLD and non-MLD modes */
+int
+iwl_mvm_switch_vif_chanctx_common(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode,
+ const struct iwl_mvm_switch_vif_chanctx_ops *ops)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
@@ -4609,10 +5356,10 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
switch (mode) {
case CHANCTX_SWMODE_SWAP_CONTEXTS:
- ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
+ ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs, ops);
break;
case CHANCTX_SWMODE_REASSIGN_VIF:
- ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
+ ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs, ops);
break;
default:
ret = -EOPNOTSUPP;
@@ -4622,15 +5369,27 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
return ret;
}
-static int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw)
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ static const struct iwl_mvm_switch_vif_chanctx_ops ops = {
+ .__assign_vif_chanctx = __iwl_mvm_assign_vif_chanctx,
+ .__unassign_vif_chanctx = __iwl_mvm_unassign_vif_chanctx,
+ };
+
+ return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops);
+}
+
+int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
return mvm->ibss_manager;
}
-static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta,
+static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
bool set)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -4641,80 +5400,12 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
return -EINVAL;
}
- return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
-}
-
-#ifdef CONFIG_NL80211_TESTMODE
-static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
- [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
- [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
- [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
-};
-
-static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- void *data, int len)
-{
- struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
- int err;
- u32 noa_duration;
-
- err = nla_parse_deprecated(tb, IWL_MVM_TM_ATTR_MAX, data, len,
- iwl_mvm_tm_policy, NULL);
- if (err)
- return err;
-
- if (!tb[IWL_MVM_TM_ATTR_CMD])
- return -EINVAL;
-
- switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
- case IWL_MVM_TM_CMD_SET_NOA:
- if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
- !vif->bss_conf.enable_beacon ||
- !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
- return -EINVAL;
-
- noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
- if (noa_duration >= vif->bss_conf.beacon_int)
- return -EINVAL;
-
- mvm->noa_duration = noa_duration;
- mvm->noa_vif = vif;
-
- return iwl_mvm_update_quotas(mvm, true, NULL);
- case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
- /* must be associated client vif - ignore authorized */
- if (!vif || vif->type != NL80211_IFTYPE_STATION ||
- !vif->cfg.assoc || !vif->bss_conf.dtim_period ||
- !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
- return -EINVAL;
-
- if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
- return iwl_mvm_enable_beacon_filter(mvm, vif, 0);
- return iwl_mvm_disable_beacon_filter(mvm, vif, 0);
- }
-
- return -EOPNOTSUPP;
-}
-
-static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- void *data, int len)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int err;
-
- mutex_lock(&mvm->mutex);
- err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
- mutex_unlock(&mvm->mutex);
-
- return err;
+ return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif,
+ &mvm_sta->vif->bss_conf);
}
-#endif
-static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel_switch *chsw)
+void iwl_mvm_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
{
/* By implementing this operation, we prevent mac80211 from
* starting its own channel switch timer, so that we can call
@@ -4775,8 +5466,8 @@ static int iwl_mvm_old_pre_chan_sw_sta(struct iwl_mvm *mvm,
if (chsw->block_tx)
iwl_mvm_csa_client_absent(mvm, vif);
- if (mvmvif->bf_data.bf_enabled) {
- int ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ if (mvmvif->bf_enabled) {
+ int ret = iwl_mvm_disable_beacon_filter(mvm, vif);
if (ret)
return ret;
@@ -4788,19 +5479,32 @@ static int iwl_mvm_old_pre_chan_sw_sta(struct iwl_mvm *mvm,
return 0;
}
+static void iwl_mvm_csa_block_txqs(void *data, struct ieee80211_sta *sta)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct iwl_mvm_txq *mvmtxq =
+ iwl_mvm_txq_from_mac80211(sta->txq[i]);
+
+ set_bit(IWL_MVM_TXQ_STATE_STOP_AP_CSA, &mvmtxq->state);
+ }
+}
+
#define IWL_MAX_CSA_BLOCK_TX 1500
-static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
+static int iwl_mvm_pre_channel_switch(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_vif *csa_vif;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_txq *mvmtxq;
int ret;
- mutex_lock(&mvm->mutex);
+ lockdep_assert_held(&mvm->mutex);
mvmvif->csa_failed = false;
+ mvmvif->csa_blocks_tx = false;
IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
chsw->chandef.center_freq1);
@@ -4815,30 +5519,38 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (WARN_ONCE(csa_vif && csa_vif->bss_conf.csa_active,
- "Another CSA is already in progress")) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ "Another CSA is already in progress"))
+ return -EBUSY;
/* we still didn't unblock tx. prevent new CS meanwhile */
if (rcu_dereference_protected(mvm->csa_tx_blocked_vif,
- lockdep_is_held(&mvm->mutex))) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ lockdep_is_held(&mvm->mutex)))
+ return -EBUSY;
rcu_assign_pointer(mvm->csa_vif, vif);
if (WARN_ONCE(mvmvif->csa_countdown,
- "Previous CSA countdown didn't complete")) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ "Previous CSA countdown didn't complete"))
+ return -EBUSY;
mvmvif->csa_target_freq = chsw->chandef.chan->center_freq;
+ if (!chsw->block_tx)
+ break;
+ /* don't need blocking in driver otherwise - mac80211 will do */
+ if (!ieee80211_hw_check(mvm->hw, HANDLES_QUIET_CSA))
+ break;
+
+ mvmvif->csa_blocks_tx = true;
+ mvmtxq = iwl_mvm_txq_from_mac80211(vif->txq);
+ set_bit(IWL_MVM_TXQ_STATE_STOP_AP_CSA, &mvmtxq->state);
+ ieee80211_iterate_stations_atomic(mvm->hw,
+ iwl_mvm_csa_block_txqs,
+ NULL);
break;
case NL80211_IFTYPE_STATION:
+ mvmvif->csa_blocks_tx = chsw->block_tx;
+
/*
* In the new flow FW is in charge of timing the switch so there
* is no need for all of this
@@ -4853,12 +5565,11 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
* we don't know the dtim period. In this case, the firmware can't
* track the beacons.
*/
- if (!vif->cfg.assoc || !vif->bss_conf.dtim_period) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ if (!vif->cfg.assoc || !vif->bss_conf.dtim_period)
+ return -EBUSY;
- if (chsw->delay > IWL_MAX_CSA_BLOCK_TX)
+ if (chsw->delay > IWL_MAX_CSA_BLOCK_TX &&
+ hweight16(vif->valid_links) <= 1)
schedule_delayed_work(&mvmvif->csa_work, 0);
if (chsw->block_tx) {
@@ -4877,7 +5588,7 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
ret = iwl_mvm_old_pre_chan_sw_sta(mvm, vif, chsw);
if (ret)
- goto out_unlock;
+ return ret;
} else {
iwl_mvm_schedule_client_csa(mvm, vif, chsw);
}
@@ -4893,20 +5604,27 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
ret = iwl_mvm_power_update_ps(mvm);
if (ret)
- goto out_unlock;
+ return ret;
/* we won't be on this channel any longer */
iwl_mvm_teardown_tdls_peers(mvm);
-out_unlock:
- mutex_unlock(&mvm->mutex);
-
return ret;
}
-static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel_switch *chsw)
+int iwl_mvm_mac_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ guard(mvm)(mvm);
+ return iwl_mvm_pre_channel_switch(mvm, vif, chsw);
+}
+
+void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -4935,9 +5653,17 @@ static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
if (chsw->count >= mvmvif->csa_count && chsw->block_tx) {
if (mvmvif->csa_misbehave) {
+ struct ieee80211_bss_conf *link_conf;
+
/* Second time, give up on this AP*/
- iwl_mvm_abort_channel_switch(hw, vif);
- ieee80211_chswitch_done(vif, false);
+
+ link_conf = wiphy_dereference(hw->wiphy,
+ vif->link_conf[chsw->link_id]);
+ if (WARN_ON(!link_conf))
+ return;
+
+ iwl_mvm_abort_channel_switch(hw, vif, link_conf);
+ ieee80211_chswitch_done(vif, false, 0);
mvmvif->csa_misbehave = false;
return;
}
@@ -4945,16 +5671,14 @@ static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
}
mvmvif->csa_count = chsw->count;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
if (mvmvif->csa_failed)
- goto out_unlock;
+ return;
WARN_ON(iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP,
CHANNEL_SWITCH_TIME_EVENT_CMD),
0, sizeof(cmd), &cmd));
-out_unlock:
- mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_flush_no_vif(struct iwl_mvm *mvm, u32 queues, bool drop)
@@ -4962,18 +5686,21 @@ static void iwl_mvm_flush_no_vif(struct iwl_mvm *mvm, u32 queues, bool drop)
int i;
if (!iwl_mvm_has_new_tx_api(mvm)) {
+ /* we can't ask the firmware anything if it is dead */
+ if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+ &mvm->status))
+ return;
if (drop) {
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
iwl_mvm_flush_tx_path(mvm,
iwl_mvm_flushable_queues(mvm) & queues);
- mutex_unlock(&mvm->mutex);
} else {
iwl_trans_wait_tx_queues_empty(mvm->trans, queues);
}
return;
}
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
struct ieee80211_sta *sta;
@@ -4988,16 +5715,16 @@ static void iwl_mvm_flush_no_vif(struct iwl_mvm *mvm, u32 queues, bool drop)
iwl_mvm_wait_sta_queues_empty(mvm,
iwl_mvm_sta_from_mac80211(sta));
}
- mutex_unlock(&mvm->mutex);
}
-static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, u32 queues, bool drop)
+void iwl_mvm_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif;
struct iwl_mvm_sta *mvmsta;
struct ieee80211_sta *sta;
+ bool ap_sta_done = false;
int i;
u32 msk = 0;
@@ -5006,14 +5733,22 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
return;
}
- if (vif->type != NL80211_IFTYPE_STATION)
- return;
+ if (!drop && hweight16(vif->active_links) <= 1) {
+ int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
+ struct ieee80211_bss_conf *link_conf;
+
+ link_conf = wiphy_dereference(hw->wiphy,
+ vif->link_conf[link_id]);
+ if (WARN_ON(!link_conf))
+ return;
+ if (link_conf->csa_active && mvmvif->csa_blocks_tx)
+ drop = true;
+ }
/* Make sure we're done with the deferred traffic before flushing */
flush_work(&mvm->add_stream_wk);
mutex_lock(&mvm->mutex);
- mvmvif = iwl_mvm_vif_from_mac80211(vif);
/* flush the AP-station and all TDLS peers */
for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
@@ -5026,16 +5761,21 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
if (mvmsta->vif != vif)
continue;
- /* make sure only TDLS peers or the AP are flushed */
- WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
+ if (sta == mvmvif->ap_sta) {
+ if (ap_sta_done)
+ continue;
+ ap_sta_done = true;
+ }
if (drop) {
- if (iwl_mvm_flush_sta(mvm, mvmsta, false))
+ if (iwl_mvm_flush_sta(mvm, mvmsta->deflink.sta_id,
+ mvmsta->tfd_queue_msk))
IWL_ERR(mvm, "flush request fail\n");
} else {
- msk |= mvmsta->tfd_queue_msk;
if (iwl_mvm_has_new_tx_api(mvm))
iwl_mvm_wait_sta_queues_empty(mvm, mvmsta);
+ else /* only used for !iwl_mvm_has_new_tx_api() below */
+ msk |= mvmsta->tfd_queue_msk;
}
}
@@ -5043,42 +5783,129 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
/* this can take a while, and we may need/want other operations
* to succeed while doing this, so do it without the mutex held
+ * If the firmware is dead, this can't work...
*/
- if (!drop && !iwl_mvm_has_new_tx_api(mvm))
+ if (!drop && !iwl_mvm_has_new_tx_api(mvm) &&
+ !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+ &mvm->status))
iwl_trans_wait_tx_queues_empty(mvm->trans, msk);
}
-static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
- struct survey_info *survey)
+void iwl_mvm_mac_flush_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_link_sta *mvm_link_sta;
+ struct ieee80211_link_sta *link_sta;
+ int link_id;
+
+ guard(mvm)(mvm);
+ for_each_sta_active_link(vif, sta, link_sta, link_id) {
+ mvm_link_sta = rcu_dereference_protected(mvmsta->link[link_id],
+ lockdep_is_held(&mvm->mutex));
+ if (!mvm_link_sta)
+ continue;
+
+ if (iwl_mvm_flush_sta(mvm, mvm_link_sta->sta_id,
+ mvmsta->tfd_queue_msk))
+ IWL_ERR(mvm, "flush request fail\n");
+ }
+}
+
+static int iwl_mvm_mac_get_acs_survey(struct iwl_mvm *mvm, int idx,
+ struct survey_info *survey)
+{
+ int chan_idx;
+ enum nl80211_band band;
int ret;
- memset(survey, 0, sizeof(*survey));
+ mutex_lock(&mvm->mutex);
- /* only support global statistics right now */
- if (idx != 0)
- return -ENOENT;
+ if (!mvm->acs_survey) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ /* Find and return the next entry that has a non-zero active time */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *sband =
+ mvm->hw->wiphy->bands[band];
+
+ if (!sband)
+ continue;
+
+ for (chan_idx = 0; chan_idx < sband->n_channels; chan_idx++) {
+ struct iwl_mvm_acs_survey_channel *info =
+ &mvm->acs_survey->bands[band][chan_idx];
+
+ if (!info->time)
+ continue;
+
+ /* Found (the next) channel to report */
+ survey->channel = &sband->channels[chan_idx];
+ survey->filled = SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_BUSY |
+ SURVEY_INFO_TIME_RX |
+ SURVEY_INFO_TIME_TX;
+ survey->time = info->time;
+ survey->time_busy = info->time_busy;
+ survey->time_rx = info->time_rx;
+ survey->time_tx = info->time_tx;
+ survey->noise = info->noise;
+ if (survey->noise < 0)
+ survey->filled |= SURVEY_INFO_NOISE_DBM;
+
+ /* Clear time so that channel is only reported once */
+ info->time = 0;
+
+ ret = 0;
+ goto out;
+ }
+ }
+
+ ret = -ENOENT;
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
+ WIDE_ID(SYSTEM_GROUP,
+ SYSTEM_STATISTICS_CMD),
+ IWL_FW_CMD_VER_UNKNOWN);
+
+ memset(survey, 0, sizeof(*survey));
if (!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return -ENOENT;
- mutex_lock(&mvm->mutex);
+ /*
+ * Return the beacon stats at index zero and pass on following indices
+ * to the function returning the full survey, most likely for ACS
+ * (Automatic Channel Selection).
+ */
+ if (idx > 0)
+ return iwl_mvm_mac_get_acs_survey(mvm, idx - 1, survey);
+
+ guard(mvm)(mvm);
if (iwl_mvm_firmware_running(mvm)) {
- ret = iwl_mvm_request_statistics(mvm, false);
+ int ret = iwl_mvm_request_statistics(mvm, false);
+
if (ret)
- goto out;
+ return ret;
}
- survey->filled = SURVEY_INFO_TIME |
- SURVEY_INFO_TIME_RX |
- SURVEY_INFO_TIME_TX |
- SURVEY_INFO_TIME_SCAN;
- survey->time = mvm->accu_radio_stats.on_time_rf +
- mvm->radio_stats.on_time_rf;
- do_div(survey->time, USEC_PER_MSEC);
+ survey->filled = SURVEY_INFO_TIME_RX |
+ SURVEY_INFO_TIME_TX;
survey->time_rx = mvm->accu_radio_stats.rx_time +
mvm->radio_stats.rx_time;
@@ -5088,14 +5915,21 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
mvm->radio_stats.tx_time;
do_div(survey->time_tx, USEC_PER_MSEC);
+ /* the new fw api doesn't support the following fields */
+ if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN)
+ return 0;
+
+ survey->filled |= SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_SCAN;
+ survey->time = mvm->accu_radio_stats.on_time_rf +
+ mvm->radio_stats.on_time_rf;
+ do_div(survey->time, USEC_PER_MSEC);
+
survey->time_scan = mvm->accu_radio_stats.on_time_scan +
mvm->radio_stats.on_time_scan;
do_div(survey->time_scan, USEC_PER_MSEC);
- ret = 0;
- out:
- mutex_unlock(&mvm->mutex);
- return ret;
+ return 0;
}
static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
@@ -5116,14 +5950,17 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
case RATE_MCS_CHAN_WIDTH_160:
rinfo->bw = RATE_INFO_BW_160;
break;
+ case RATE_MCS_CHAN_WIDTH_320:
+ rinfo->bw = RATE_INFO_BW_320;
+ break;
}
- if (format == RATE_MCS_CCK_MSK ||
- format == RATE_MCS_LEGACY_OFDM_MSK) {
+ if (format == RATE_MCS_MOD_TYPE_CCK ||
+ format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) {
int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK);
/* add the offset needed to get to the legacy ofdm indices */
- if (format == RATE_MCS_LEGACY_OFDM_MSK)
+ if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
rate += IWL_FIRST_OFDM_RATE;
switch (rate) {
@@ -5168,7 +6005,7 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
rinfo->nss = u32_get_bits(rate_n_flags,
RATE_MCS_NSS_MSK) + 1;
- rinfo->mcs = format == RATE_MCS_HT_MSK ?
+ rinfo->mcs = format == RATE_MCS_MOD_TYPE_HT ?
RATE_HT_MCS_INDEX(rate_n_flags) :
u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK);
@@ -5176,7 +6013,11 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
switch (format) {
- case RATE_MCS_HE_MSK:
+ case RATE_MCS_MOD_TYPE_EHT:
+ /* TODO: GI/LTF/RU. How does the firmware encode them? */
+ rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS;
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK);
rinfo->flags |= RATE_INFO_FLAGS_HE_MCS;
@@ -5217,31 +6058,32 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK)
rinfo->he_dcm = 1;
break;
- case RATE_MCS_HT_MSK:
+ case RATE_MCS_MOD_TYPE_HT:
rinfo->flags |= RATE_INFO_FLAGS_MCS;
break;
- case RATE_MCS_VHT_MSK:
+ case RATE_MCS_MOD_TYPE_VHT:
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
break;
}
}
-static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct station_info *sinfo)
+void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ int i;
- if (mvmsta->avg_energy) {
- sinfo->signal_avg = -(s8)mvmsta->avg_energy;
+ if (mvmsta->deflink.avg_energy) {
+ sinfo->signal_avg = -(s8)mvmsta->deflink.avg_energy;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
}
if (iwl_mvm_has_tlc_offload(mvm)) {
- struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
+ struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->deflink.lq_sta.rs_fw;
iwl_mvm_set_sta_rate(lq_sta->last_rate_n_flags, &sinfo->txrate);
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
@@ -5254,24 +6096,26 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
if (!vif->cfg.assoc)
return;
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
- if (mvmvif->ap_sta_id != mvmsta->sta_id)
- goto unlock;
+ if (sta != mvmvif->ap_sta)
+ return;
if (iwl_mvm_request_statistics(mvm, false))
- goto unlock;
+ return;
+
+ sinfo->rx_beacon = 0;
+ for_each_mvm_vif_valid_link(mvmvif, i)
+ sinfo->rx_beacon += mvmvif->link[i]->beacon_stats.num_beacons +
+ mvmvif->link[i]->beacon_stats.accu_num_beacons;
- sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
- mvmvif->beacon_stats.accu_num_beacons;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX);
- if (mvmvif->beacon_stats.avg_signal) {
+ if (mvmvif->deflink.beacon_stats.avg_signal) {
/* firmware only reports a value after RXing a few beacons */
- sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
+ sinfo->rx_beacon_signal_avg =
+ mvmvif->deflink.beacon_stats.avg_signal;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
}
- unlock:
- mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_event_mlme_callback_ini(struct iwl_mvm *mvm,
@@ -5369,9 +6213,9 @@ static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,
event->u.ba.ssn);
}
-static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- const struct ieee80211_event *event)
+void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@@ -5391,42 +6235,42 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
}
}
+#define SYNC_RX_QUEUE_TIMEOUT (HZ)
void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
enum iwl_mvm_rxq_notif_type type,
bool sync,
const void *data, u32 size)
{
- struct {
- struct iwl_rxq_sync_cmd cmd;
- struct iwl_mvm_internal_rxq_notif notif;
- } __packed cmd = {
- .cmd.rxq_mask = cpu_to_le32(BIT(mvm->trans->num_rx_queues) - 1),
- .cmd.count =
- cpu_to_le32(sizeof(struct iwl_mvm_internal_rxq_notif) +
- size),
- .notif.type = type,
- .notif.sync = sync,
- };
+ DEFINE_RAW_FLEX(struct iwl_rxq_sync_cmd, cmd, payload,
+ sizeof(struct iwl_mvm_internal_rxq_notif));
+ struct iwl_mvm_internal_rxq_notif *notif =
+ (struct iwl_mvm_internal_rxq_notif *)cmd->payload;
struct iwl_host_cmd hcmd = {
.id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD),
- .data[0] = &cmd,
- .len[0] = sizeof(cmd),
+ .data[0] = cmd,
+ .len[0] = __struct_size(cmd),
.data[1] = data,
.len[1] = size,
- .flags = sync ? 0 : CMD_ASYNC,
+ .flags = CMD_SEND_IN_RFKILL | (sync ? 0 : CMD_ASYNC),
};
int ret;
+ cmd->rxq_mask = cpu_to_le32(BIT(mvm->trans->info.num_rxqs) - 1);
+ cmd->count = cpu_to_le32(sizeof(struct iwl_mvm_internal_rxq_notif) +
+ size);
+ notif->type = type;
+ notif->sync = sync;
+
/* size must be a multiple of DWORD */
- if (WARN_ON(cmd.cmd.count & cpu_to_le32(3)))
+ if (WARN_ON(cmd->count & cpu_to_le32(3)))
return;
if (!iwl_mvm_has_new_rx_api(mvm))
return;
if (sync) {
- cmd.notif.cookie = mvm->queue_sync_cookie;
- mvm->queue_sync_state = (1 << mvm->trans->num_rx_queues) - 1;
+ notif->cookie = mvm->queue_sync_cookie;
+ mvm->queue_sync_state = (1 << mvm->trans->info.num_rxqs) - 1;
}
ret = iwl_mvm_send_cmd(mvm, &hcmd);
@@ -5438,12 +6282,11 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
if (sync) {
lockdep_assert_held(&mvm->mutex);
ret = wait_event_timeout(mvm->rx_sync_waitq,
- READ_ONCE(mvm->queue_sync_state) == 0 ||
- iwl_mvm_is_radio_killed(mvm),
- HZ);
- WARN_ONCE(!ret && !iwl_mvm_is_radio_killed(mvm),
- "queue sync: failed to sync, state is 0x%lx\n",
- mvm->queue_sync_state);
+ READ_ONCE(mvm->queue_sync_state) == 0,
+ SYNC_RX_QUEUE_TIMEOUT);
+ WARN_ONCE(!ret, "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
+ mvm->queue_sync_state,
+ mvm->queue_sync_cookie);
}
out:
@@ -5453,16 +6296,15 @@ out:
}
}
-static void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)
+void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
iwl_mvm_sync_rx_queues_internal(mvm, IWL_MVM_RXQ_EMPTY, true, NULL, 0);
- mutex_unlock(&mvm->mutex);
}
-static int
+int
iwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_ftm_responder_stats *stats)
@@ -5491,29 +6333,22 @@ iwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw,
return 0;
}
-static int iwl_mvm_start_pmsr(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_pmsr_request *request)
+int iwl_mvm_start_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *request)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- int ret;
- mutex_lock(&mvm->mutex);
- ret = iwl_mvm_ftm_start(mvm, vif, request);
- mutex_unlock(&mvm->mutex);
-
- return ret;
+ guard(mvm)(mvm);
+ return iwl_mvm_ftm_start(mvm, vif, request);
}
-static void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_pmsr_request *request)
+void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct cfg80211_pmsr_request *request)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- mutex_lock(&mvm->mutex);
+ guard(mvm)(mvm);
iwl_mvm_ftm_abort(mvm, request);
- mutex_unlock(&mvm->mutex);
}
static bool iwl_mvm_can_hw_csum(struct sk_buff *skb)
@@ -5532,10 +6367,6 @@ static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
- return iwl_mvm_tx_csum_bz(mvm, head, true) ==
- iwl_mvm_tx_csum_bz(mvm, skb, true);
-
/* For now don't aggregate IPv6 in AMSDU */
if (skb->protocol != htons(ETH_P_IP))
return false;
@@ -5546,11 +6377,31 @@ static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,
return iwl_mvm_can_hw_csum(skb) == iwl_mvm_can_hw_csum(head);
}
+int iwl_mvm_set_hw_timestamp(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_set_hw_timestamp *hwts)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u32 protocols = 0;
+
+ /* HW timestamping is only supported for a specific station */
+ if (!hwts->macaddr)
+ return -EOPNOTSUPP;
+
+ if (hwts->enable)
+ protocols =
+ IWL_TIME_SYNC_PROTOCOL_TM | IWL_TIME_SYNC_PROTOCOL_FTM;
+
+ guard(mvm)(mvm);
+ return iwl_mvm_time_sync_config(mvm, hwts->macaddr, protocols);
+}
+
const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx,
.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,
@@ -5569,12 +6420,13 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
.release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
.set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
- .sta_rc_update = iwl_mvm_sta_rc_update,
+ .link_sta_rc_update = iwl_mvm_sta_rc_update,
.conf_tx = iwl_mvm_mac_conf_tx,
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
.mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx,
.mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
.flush = iwl_mvm_mac_flush,
+ .flush_sta = iwl_mvm_mac_flush_sta,
.sched_scan_start = iwl_mvm_mac_sched_scan_start,
.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
.set_key = iwl_mvm_mac_set_key,
@@ -5598,7 +6450,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.set_tim = iwl_mvm_set_tim,
.channel_switch = iwl_mvm_channel_switch,
- .pre_channel_switch = iwl_mvm_pre_channel_switch,
+ .pre_channel_switch = iwl_mvm_mac_pre_channel_switch,
.post_channel_switch = iwl_mvm_post_channel_switch,
.abort_channel_switch = iwl_mvm_abort_channel_switch,
.channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon,
@@ -5611,8 +6463,6 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.sync_rx_queues = iwl_mvm_sync_rx_queues,
- CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
-
#ifdef CONFIG_PM_SLEEP
/* look at d3.c */
.suspend = iwl_mvm_suspend,
@@ -5632,6 +6482,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.can_aggregate_in_amsdu = iwl_mvm_mac_can_aggregate,
#ifdef CONFIG_IWLWIFI_DEBUGFS
- .sta_add_debugfs = iwl_mvm_sta_add_debugfs,
+ .vif_add_debugfs = iwl_mvm_vif_add_debugfs,
+ .link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs,
#endif
+ .set_hw_timestamp = iwl_mvm_set_hw_timestamp,
};