diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 659 |
1 files changed, 380 insertions, 279 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 893b69fc841b..bf24f8cb673e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022-2023 Intel Corporation + * Copyright (C) 2022-2025 Intel Corporation */ #include "mvm.h" @@ -12,10 +12,14 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, int ret; int i; - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); + + iwl_mvm_mac_init_mvmvif(mvm, mvmvif); mvmvif->mvm = mvm; + vif->driver_flags |= IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC; + /* Not much to do here. The stack will not allow interface * types or combinations that we didn't advertise, so we * don't really have to check the types. @@ -30,7 +34,7 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, /* 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; + return ret; rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif); @@ -39,15 +43,13 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, /* reset deflink MLO parameters */ mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; mvmvif->deflink.active = 0; - /* the first link always points to the default one */ - mvmvif->link[0] = &mvmvif->deflink; ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); if (ret) - goto out_unlock; + return ret; /* 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; @@ -58,9 +60,19 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, IEEE80211_VIF_SUPPORTS_CQM_RSSI; } - ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); - if (ret) - goto out_free_bf; + /* We want link[0] to point to the default link, unless we have MLO and + * in this case this will be modified later by .change_vif_links() + * If we are in the restart flow with an MLD connection, we will wait + * to .change_vif_links() to setup the links. + */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || + !ieee80211_vif_is_mld(vif)) { + mvmvif->link[0] = &mvmvif->deflink; + + ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); + if (ret) + goto out_free_bf; + } /* 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 @@ -73,8 +85,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, goto out_free_bf; 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) { mvm->monitor_on = true; @@ -92,7 +102,10 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, mvm->csme_vif = vif; } - goto out_unlock; + if (vif->p2p || iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) < 5) + vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW; + + return 0; out_free_bf: if (mvm->bf_allowed_vif == mvmvif) { @@ -103,9 +116,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, out_remove_mac: mvmvif->link[0] = NULL; iwl_mvm_mld_mac_ctxt_remove(mvm, vif); - out_unlock: - mutex_unlock(&mvm->mutex); - return ret; } @@ -122,7 +132,7 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, vif->type == NL80211_IFTYPE_ADHOC)) iwl_mvm_tcm_rm_vif(mvm, vif); - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); if (vif == mvm->csme_vif) { iwl_mei_set_netdev(NULL); @@ -140,19 +150,6 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, 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_power_update_mac(mvm); /* Before the interface removal, mac80211 would cancel the ROC, and the @@ -185,21 +182,15 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, mvm->monitor_on = false; __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); } - - mutex_unlock(&mvm->mutex); } -static unsigned int iwl_mvm_mld_count_active_links(struct ieee80211_vif *vif) +static unsigned int iwl_mvm_mld_count_active_links(struct iwl_mvm_vif *mvmvif) { unsigned int n_active = 0; int i; for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - struct ieee80211_bss_conf *link_conf; - - link_conf = link_conf_dereference_protected(vif, i); - if (link_conf && - rcu_access_pointer(link_conf->chanctx_conf)) + if (mvmvif->link[i] && mvmvif->link[i]->phy_ctxt) n_active++; } @@ -233,6 +224,15 @@ static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, link->phy_ctxt->rlc_disabled = true; } + if (vif->active_links == mvmvif->link_selection_res && + !WARN_ON(!(vif->active_links & BIT(mvmvif->link_selection_primary)))) + mvmvif->primary_link = mvmvif->link_selection_primary; + else + mvmvif->primary_link = __ffs(vif->active_links); + + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_ESR_LINK_UP, + NULL); + return ret; } @@ -245,21 +245,18 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, { u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - unsigned int n_active = iwl_mvm_mld_count_active_links(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + unsigned int n_active = iwl_mvm_mld_count_active_links(mvmvif); unsigned int link_id = link_conf->link_id; int ret; - /* if the assigned one was not counted yet, count it now */ - if (!rcu_access_pointer(link_conf->chanctx_conf)) - n_active++; - - if (n_active > iwl_mvm_max_active_links(mvm, vif)) - return -EOPNOTSUPP; - if (WARN_ON_ONCE(!mvmvif->link[link_id])) return -EINVAL; + /* if the assigned one was not counted yet, count it now */ + if (!mvmvif->link[link_id]->phy_ctxt) + n_active++; + /* mac parameters such as HE support can change at this stage * For sta, need first to configure correct state from drv_sta_state * and only after that update mac config. @@ -294,37 +291,24 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, if (ret) goto out; - /* Initialize rate control for the AP station, since we might be - * doing a link switch here - we cannot initialize it before since - * this needs the phy context assigned (and in FW?), and we cannot - * do it later because it needs to be initialized as soon as we're - * able to TX on the link, i.e. when active. - * - * Firmware restart isn't quite correct yet for MLO, but we don't - * need to do it in that case anyway since it will happen from the - * normal station state callback. + /* + * if link switching (link not active yet) we'll activate it in + * firmware later on link-info change, which mac80211 guarantees + * for link switch after the stations are set up */ - if (mvmvif->ap_sta && - !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - struct ieee80211_link_sta *link_sta; - - rcu_read_lock(); - link_sta = rcu_dereference(mvmvif->ap_sta->link[link_id]); - - if (!WARN_ON_ONCE(!link_sta)) - iwl_mvm_rs_rate_init(mvm, vif, mvmvif->ap_sta, - link_conf, link_sta, - phy_ctxt->channel->band); - rcu_read_unlock(); + if (ieee80211_vif_link_active(vif, link_conf->link_id)) { + ret = iwl_mvm_link_changed(mvm, vif, link_conf, + LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_RATES_INFO, + true); + if (ret) + goto out; } - /* then activate */ - ret = iwl_mvm_link_changed(mvm, vif, link_conf, - LINK_CONTEXT_MODIFY_ACTIVE | - LINK_CONTEXT_MODIFY_RATES_INFO, - true); - if (ret) - goto out; + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif, + link_conf, + false); /* * Power state must be updated before quotas, @@ -355,13 +339,23 @@ static int iwl_mvm_mld_assign_vif_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_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false); - mutex_unlock(&mvm->mutex); + /* update EMLSR mode */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { + int ret; - return ret; + ret = iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, + true); + /* + * Don't activate this link if failed to exit EMLSR in + * the BSS interface + */ + if (ret) + return ret; + } + + guard(mvm)(mvm); + return __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false); } static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm, @@ -407,6 +401,9 @@ static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm, break; } + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_ESR_LINK_DOWN, + NULL); + return ret; } @@ -419,7 +416,7 @@ __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - unsigned int n_active = iwl_mvm_mld_count_active_links(vif); + unsigned int n_active = iwl_mvm_mld_count_active_links(mvmvif); unsigned int link_id = link_conf->link_id; /* shouldn't happen, but verify link_id is valid before accessing */ @@ -475,6 +472,87 @@ static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_add_link(mvm, vif, link_conf); } mutex_unlock(&mvm->mutex); + + /* update EMLSR mode */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, false); +} + +static void +iwl_mvm_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd, + const struct ieee80211_bss_conf *bss_info) +{ + u8 i; + + /* + * NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT, + * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE + */ + + BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) != + ARRAY_SIZE(bss_info->tpe.psd_local[0].power)); + + /* if not valid, mac80211 puts default (max value) */ + for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++) + cmd->psd_pwr[i] = min(bss_info->tpe.psd_local[0].power[i], + bss_info->tpe.psd_reg_client[0].power[i]); + + BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) != + ARRAY_SIZE(bss_info->tpe.max_local[0].power)); + + for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++) + cmd->eirp_pwr[i] = min(bss_info->tpe.max_local[0].power[i], + bss_info->tpe.max_reg_client[0].power[i]); +} + +void +iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + bool is_ap) +{ + struct iwl_txpower_constraints_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif_link_info *link_info = + mvmvif->link[bss_conf->link_id]; + u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, AP_TX_POWER_CONSTRAINTS_CMD); + u32 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) + return; + + if (!link_info->active || + link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) + return; + + if (bss_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ) + return; + + cmd.link_id = cpu_to_le16(link_info->fw_link_id); + memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr)); + memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr)); + + if (is_ap) { + cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP); + } else if (bss_conf->power_type == IEEE80211_REG_UNSET_AP) { + return; + } else { + cmd.ap_type = cpu_to_le16(bss_conf->power_type - 1); + iwl_mvm_tpe_sta_cmd_data(&cmd, bss_conf); + } + + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(PHY_OPS_GROUP, + AP_TX_POWER_CONSTRAINTS_CMD), + 0, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, + "failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n", + ret); } static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, @@ -485,11 +563,16 @@ static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); + + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif, + link_conf, true); + /* Send the beacon template */ ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf); if (ret) - goto out_unlock; + return ret; /* the link should be already activated when assigning chan context */ ret = iwl_mvm_link_changed(mvm, vif, link_conf, @@ -497,11 +580,11 @@ static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, ~LINK_CONTEXT_MODIFY_ACTIVE, true); if (ret) - goto out_unlock; + return ret; ret = iwl_mvm_mld_add_mcast_sta(mvm, vif, link_conf); if (ret) - goto out_unlock; + return ret; /* Send the bcast station. At this stage the TBTT and DTIM time * events are added and applied to the scheduler @@ -525,7 +608,7 @@ static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_ftm_restart_responder(mvm, vif, link_conf); - goto out_unlock; + return 0; out_failed: iwl_mvm_power_update_mac(mvm); @@ -533,8 +616,6 @@ out_failed: iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); out_rm_mcast: iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); -out_unlock: - mutex_unlock(&mvm->mutex); return ret; } @@ -557,7 +638,7 @@ static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); iwl_mvm_stop_ap_ibss_common(mvm, vif); @@ -571,7 +652,6 @@ static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); iwl_mvm_power_update_mac(mvm); - mutex_unlock(&mvm->mutex); } static void iwl_mvm_mld_stop_ap(struct ieee80211_hw *hw, @@ -604,124 +684,23 @@ static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, &callbacks); } -struct iwl_mvm_link_sel_data { - u8 link_id; - enum nl80211_band band; - bool active; -}; - -static bool iwl_mvm_mld_valid_link_pair(struct iwl_mvm_link_sel_data *a, - struct iwl_mvm_link_sel_data *b) -{ - return a->band != b->band; -} - -void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool valid_links_changed) +static bool iwl_mvm_esr_bw_criteria(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) { - struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; - unsigned long usable_links = ieee80211_vif_usable_links(vif); - u32 max_active_links = iwl_mvm_max_active_links(mvm, vif); - u16 new_active_links; - u8 link_id, n_data = 0, i, j; - - if (!IWL_MVM_AUTO_EML_ENABLE) - return; - - if (!ieee80211_vif_is_mld(vif) || usable_links == 1) - return; - - /* The logic below is a simple version that doesn't suit more than 2 - * links - */ - WARN_ON_ONCE(max_active_links > 2); - - /* if only a single active link is supported, assume that the one - * selected by higher layer for connection establishment is the best. - */ - if (max_active_links == 1 && !valid_links_changed) - return; - - /* If we are already using the maximal number of active links, don't do - * any change. This can later be optimized to pick a 'better' link pair. - */ - if (hweight16(vif->active_links) == max_active_links) - return; - - rcu_read_lock(); - - for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { - struct ieee80211_bss_conf *link_conf = - rcu_dereference(vif->link_conf[link_id]); + struct ieee80211_bss_conf *other_link; + int link_id; - if (WARN_ON_ONCE(!link_conf)) + /* Exit EMLSR if links don't have equal bandwidths */ + for_each_vif_active_link(vif, other_link, link_id) { + if (link_id == link_conf->link_id) continue; - - data[n_data].link_id = link_id; - data[n_data].band = link_conf->chandef.chan->band; - data[n_data].active = vif->active_links & BIT(link_id); - n_data++; - } - - rcu_read_unlock(); - - /* this is expected to be the current active link */ - if (n_data == 1) - return; - - new_active_links = 0; - - /* Assume that after association only a single link is active, thus, - * select only the 2nd link - */ - if (!valid_links_changed) { - for (i = 0; i < n_data; i++) { - if (data[i].active) - break; - } - - if (WARN_ON_ONCE(i == n_data)) - return; - - for (j = 0; j < n_data; j++) { - if (i == j) - continue; - - if (iwl_mvm_mld_valid_link_pair(&data[i], &data[j])) - break; - } - - if (j != n_data) - new_active_links = BIT(data[i].link_id) | - BIT(data[j].link_id); - } else { - /* Try to find a valid link pair for EMLSR operation. If a pair - * is not found continue using the current active link. - */ - for (i = 0; i < n_data; i++) { - for (j = 0; j < n_data; j++) { - if (i == j) - continue; - - if (iwl_mvm_mld_valid_link_pair(&data[i], - &data[j])) - break; - } - - /* found a valid pair for EMLSR, use it */ - if (j != n_data) { - new_active_links = BIT(data[i].link_id) | - BIT(data[j].link_id); - break; - } - } + if (link_conf->chanreq.oper.width == + other_link->chanreq.oper.width) + return true; } - if (!new_active_links) - return; - - if (vif->active_links != new_active_links) - ieee80211_set_active_links_async(vif, new_active_links); + return false; } static void @@ -738,6 +717,11 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, if (WARN_ON_ONCE(!mvmvif->link[link_conf->link_id])) return; + /* not yet marked active in vif means during link switch */ + if (!ieee80211_vif_link_active(vif, link_conf->link_id) && + vif->cfg.assoc && mvmvif->link[link_conf->link_id]->phy_ctxt) + link_changes |= LINK_CONTEXT_MODIFY_ACTIVE; + has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; @@ -753,8 +737,16 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; } - /* Update EHT Puncturing info */ - if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc) + if ((changes & BSS_CHANGED_BANDWIDTH) && + ieee80211_vif_link_active(vif, link_conf->link_id) && + mvmvif->esr_active && + !iwl_mvm_esr_bw_criteria(mvm, vif, link_conf)) + iwl_mvm_exit_esr(mvm, vif, + IWL_MVM_ESR_EXIT_BANDWIDTH, + iwl_mvm_get_primary_link(vif)); + + /* if associated, maybe puncturing changed - we'll check later */ + if (vif->cfg.assoc) link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; if (link_changes) { @@ -768,9 +760,6 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); - if (changes & BSS_CHANGED_MLD_VALID_LINKS) - iwl_mvm_mld_select_links(mvm, vif, true); - memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid, ETH_ALEN); @@ -782,37 +771,13 @@ static bool iwl_mvm_mld_vif_have_valid_ap_sta(struct iwl_mvm_vif *mvmvif) int i; for_each_mvm_vif_valid_link(mvmvif, i) { - if (mvmvif->link[i]->ap_sta_id != IWL_MVM_INVALID_STA) + if (mvmvif->link[i]->ap_sta_id != IWL_INVALID_STA) return true; } return false; } -static void iwl_mvm_mld_vif_delete_all_stas(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int i, ret; - - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - return; - - for_each_mvm_vif_valid_link(mvmvif, i) { - struct iwl_mvm_vif_link_info *link = mvmvif->link[i]; - - if (!link) - continue; - - iwl_mvm_sec_key_remove_ap(mvm, vif, link, i); - ret = iwl_mvm_mld_rm_sta_id(mvm, link->ap_sta_id); - if (ret) - IWL_ERR(mvm, "failed to remove AP station\n"); - - link->ap_sta_id = IWL_MVM_INVALID_STA; - } -} - static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u64 changes) @@ -837,8 +802,15 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, if (changes & BSS_CHANGED_ASSOC) { if (vif->cfg.assoc) { - /* clear statistics to get clean beacon counter */ + mvmvif->session_prot_connection_loss = false; + + /* + * Clear statistics to get clean beacon counter, and ask for + * periodic statistics, as they are needed for link + * selection and RX OMI decisions. + */ iwl_mvm_request_statistics(mvm, true); + iwl_mvm_request_periodic_system_statistics(mvm, true); iwl_mvm_sf_update(mvm, vif, false); iwl_mvm_power_vif_assoc(mvm, vif); @@ -886,6 +858,8 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { iwl_mvm_mei_host_disassociated(mvm); + iwl_mvm_request_periodic_system_statistics(mvm, false); + /* If update fails - SF might be running in associated * mode while disassociated - which is forbidden. */ @@ -894,25 +868,22 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &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. - */ - iwl_mvm_mld_vif_delete_all_stas(mvm, vif); } iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); } if (changes & BSS_CHANGED_PS) { + iwl_mvm_smps_workaround(mvm, vif, false); ret = iwl_mvm_power_update_mac(mvm); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } + + if (changes & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM) && + ieee80211_vif_is_mld(vif) && mvmvif->authorized) + wiphy_delayed_work_queue(mvm->hw->wiphy, + &mvmvif->mlo_int_scan_wk, 0); } static void @@ -965,7 +936,7 @@ static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); switch (vif->type) { case NL80211_IFTYPE_STATION: @@ -989,10 +960,8 @@ static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_TXPOWER) { IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n", link_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, link_conf->txpower); + iwl_mvm_set_tx_power(mvm, link_conf, link_conf->txpower); } - - mutex_unlock(&mvm->mutex); } static void iwl_mvm_mld_vif_cfg_changed(struct ieee80211_hw *hw, @@ -1001,15 +970,13 @@ static void iwl_mvm_mld_vif_cfg_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); if (vif->type == NL80211_IFTYPE_STATION) iwl_mvm_mld_vif_cfg_changed_station(mvm, vif, changes); - - mutex_unlock(&mvm->mutex); } static int @@ -1042,9 +1009,8 @@ static void iwl_mvm_mld_config_iface_filter(struct ieee80211_hw *hw, !vif->p2p) return; - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); - mutex_unlock(&mvm->mutex); } static int @@ -1066,14 +1032,10 @@ iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw, * 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_link_changed(mvm, vif, &vif->bss_conf, - LINK_CONTEXT_MODIFY_QOS_PARAMS, - true); - mutex_unlock(&mvm->mutex); - return ret; + guard(mvm)(mvm); + return iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, + LINK_CONTEXT_MODIFY_QOS_PARAMS, + true); } return 0; } @@ -1122,20 +1084,13 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) { struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {}; - unsigned int n_active = iwl_mvm_mld_count_active_links(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u16 removed = old_links & ~new_links; u16 added = new_links & ~old_links; int err, i; - if (hweight16(new_links) > 1 && - n_active > iwl_mvm_max_active_links(mvm, vif)) - return -EOPNOTSUPP; - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - int r; - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) break; @@ -1147,19 +1102,17 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, goto free; } - new_link[i]->bcast_sta.sta_id = IWL_MVM_INVALID_STA; - new_link[i]->mcast_sta.sta_id = IWL_MVM_INVALID_STA; - new_link[i]->ap_sta_id = IWL_MVM_INVALID_STA; new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; - - for (r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++) - new_link[i]->smps_requests[r] = - IEEE80211_SMPS_AUTOMATIC; + iwl_mvm_init_link(new_link[i]); } mutex_lock(&mvm->mutex); - if (old_links == 0) { + /* If we're in RESTART flow, the default link wasn't added in + * drv_add_interface(), and link[0] doesn't point to it. + */ + if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); if (err) goto out_err; @@ -1196,6 +1149,14 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, if (new_links == 0) { mvmvif->link[0] = &mvmvif->deflink; err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); + if (err == 0) + mvmvif->primary_link = 0; + } else if (!(new_links & BIT(mvmvif->primary_link))) { + /* + * Ensure we always have a valid primary_link, the real + * decision happens later when PHY is activated. + */ + mvmvif->primary_link = __ffs(new_links); } out_err: @@ -1215,15 +1176,154 @@ iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, u16 old_links, u16 new_links) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + guard(mvm)(mvm); + return iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links); +} + +bool iwl_mvm_vif_has_esr_cap(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + const struct wiphy_iftype_ext_capab *ext_capa; + + lockdep_assert_held(&mvm->mutex); + + if (!ieee80211_vif_is_mld(vif) || !vif->cfg.assoc || + hweight16(ieee80211_vif_usable_links(vif)) == 1) + return false; + + if (!(vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP)) + return false; + + ext_capa = cfg80211_get_iftype_ext_capa(mvm->hw->wiphy, + ieee80211_vif_type_p2p(vif)); + return (ext_capa && + (ext_capa->eml_capabilities & IEEE80211_EML_CAP_EMLSR_SUPP)); +} + +static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 desired_links) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int n_links = hweight16(desired_links); + + if (n_links <= 1) + return true; + + guard(mvm)(mvm); + + /* Check if HW supports the wanted number of links */ + if (n_links > iwl_mvm_max_active_links(mvm, vif)) + return false; + + /* If it is an eSR device, check that we can enter eSR */ + return iwl_mvm_is_esr_supported(mvm->fwrt.trans) && + iwl_mvm_vif_has_esr_cap(mvm, vif); +} + +static enum ieee80211_neg_ttlm_res +iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_neg_ttlm *neg_ttlm) +{ + u16 map; + u8 i; + + /* Verify all TIDs are mapped to the same links set */ + map = neg_ttlm->downlink[0]; + for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { + if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] || + neg_ttlm->uplink[i] != map) + return NEG_TTLM_RES_REJECT; + } + + return NEG_TTLM_RES_ACCEPT; +} + +static int +iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; mutex_lock(&mvm->mutex); - ret = iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links); + if (mvmvif->esr_active) { + u8 primary = iwl_mvm_get_primary_link(vif); + int selected; + + /* prefer primary unless quiet CSA on it */ + if (chsw->link_id == primary && chsw->block_tx) + selected = iwl_mvm_get_other_link(vif, primary); + else + selected = primary; + + /* + * remembers to tell the firmware that this link can't tx + * Note that this logic seems to be unrelated to esr, but it + * really is needed only when esr is active. When we have a + * single link, the firmware will handle all this on its own. + * In multi-link scenarios, we can learn about the CSA from + * another link and this logic is too complex for the firmware + * to track. + * Since we want to de-activate the link that got a CSA, we + * need to tell the firmware not to send any frame on that link + * as the firmware may not be aware that link is under a CSA + * with mode=1 (no Tx allowed). + */ + if (chsw->block_tx && mvmvif->link[chsw->link_id]) + mvmvif->link[chsw->link_id]->csa_block_tx = true; + + iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_CSA, selected); + mutex_unlock(&mvm->mutex); + + /* + * If we've not kept the link active that's doing the CSA + * then we don't need to do anything else, just return. + */ + if (selected != chsw->link_id) + return 0; + + mutex_lock(&mvm->mutex); + } + + ret = iwl_mvm_pre_channel_switch(mvm, vif, chsw); mutex_unlock(&mvm->mutex); return ret; } +#define IWL_MVM_MLD_UNBLOCK_ESR_NON_BSS_TIMEOUT (5 * HZ) + +static void iwl_mvm_mld_prep_add_interface(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm); + struct iwl_mvm_vif *mvmvif; + int ret; + + IWL_DEBUG_MAC80211(mvm, "prep_add_interface: type=%u\n", + type); + + if (IS_ERR_OR_NULL(bss_vif) || + !(type == NL80211_IFTYPE_AP || + type == NL80211_IFTYPE_P2P_GO || + type == NL80211_IFTYPE_P2P_CLIENT)) + return; + + mvmvif = iwl_mvm_vif_from_mac80211(bss_vif); + ret = iwl_mvm_block_esr_sync(mvm, bss_vif, + IWL_MVM_ESR_BLOCKED_TMP_NON_BSS); + if (ret) + return; + + wiphy_delayed_work_queue(mvmvif->mvm->hw->wiphy, + &mvmvif->unblock_esr_tmp_non_bss_wk, + IWL_MVM_MLD_UNBLOCK_ESR_NON_BSS_TIMEOUT); +} + const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .tx = iwl_mvm_mac_tx, .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, @@ -1249,7 +1349,7 @@ const struct ieee80211_ops iwl_mvm_mld_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_mld_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, @@ -1277,7 +1377,7 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .tx_last_beacon = iwl_mvm_tx_last_beacon, .channel_switch = iwl_mvm_channel_switch, - .pre_channel_switch = iwl_mvm_pre_channel_switch, + .pre_channel_switch = iwl_mvm_mld_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, @@ -1290,8 +1390,6 @@ const struct ieee80211_ops iwl_mvm_mld_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, @@ -1318,4 +1416,7 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .change_vif_links = iwl_mvm_mld_change_vif_links, .change_sta_links = iwl_mvm_mld_change_sta_links, + .can_activate_links = iwl_mvm_mld_can_activate_links, + .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm, + .prep_add_interface = iwl_mvm_mld_prep_add_interface, }; |