1488 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2022-2024 Intel Corporation4 */5#include "mvm.h"6 7static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw,8 struct ieee80211_vif *vif)9{10 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);11 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);12 int ret;13 int i;14 15 guard(mvm)(mvm);16 17 iwl_mvm_mac_init_mvmvif(mvm, mvmvif);18 19 mvmvif->mvm = mvm;20 21 /* Not much to do here. The stack will not allow interface22 * types or combinations that we didn't advertise, so we23 * don't really have to check the types.24 */25 26 /* make sure that beacon statistics don't go backwards with FW reset */27 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))28 for_each_mvm_vif_valid_link(mvmvif, i)29 mvmvif->link[i]->beacon_stats.accu_num_beacons +=30 mvmvif->link[i]->beacon_stats.num_beacons;31 32 /* Allocate resources for the MAC context, and add it to the fw */33 ret = iwl_mvm_mac_ctxt_init(mvm, vif);34 if (ret)35 return ret;36 37 rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif);38 39 mvmvif->features |= hw->netdev_features;40 41 /* reset deflink MLO parameters */42 mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;43 mvmvif->deflink.active = 0;44 45 ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif);46 if (ret)47 return ret;48 49 /* beacon filtering */50 ret = iwl_mvm_disable_beacon_filter(mvm, vif);51 if (ret)52 goto out_remove_mac;53 54 if (!mvm->bf_allowed_vif &&55 vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {56 mvm->bf_allowed_vif = mvmvif;57 vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |58 IEEE80211_VIF_SUPPORTS_CQM_RSSI;59 }60 61 /* We want link[0] to point to the default link, unless we have MLO and62 * in this case this will be modified later by .change_vif_links()63 * If we are in the restart flow with an MLD connection, we will wait64 * to .change_vif_links() to setup the links.65 */66 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||67 !ieee80211_vif_is_mld(vif)) {68 mvmvif->link[0] = &mvmvif->deflink;69 70 ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);71 if (ret)72 goto out_free_bf;73 }74 75 /* Save a pointer to p2p device vif, so it can later be used to76 * update the p2p device MAC when a GO is started/stopped77 */78 if (vif->type == NL80211_IFTYPE_P2P_DEVICE)79 mvm->p2p_device_vif = vif;80 81 ret = iwl_mvm_power_update_mac(mvm);82 if (ret)83 goto out_free_bf;84 85 iwl_mvm_tcm_add_vif(mvm, vif);86 87 if (vif->type == NL80211_IFTYPE_MONITOR) {88 mvm->monitor_on = true;89 ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS);90 }91 92 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))93 iwl_mvm_vif_dbgfs_add_link(mvm, vif);94 95 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&96 vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&97 !mvm->csme_vif && mvm->mei_registered) {98 iwl_mei_set_nic_info(vif->addr, mvm->nvm_data->hw_addr);99 iwl_mei_set_netdev(ieee80211_vif_to_wdev(vif)->netdev);100 mvm->csme_vif = vif;101 }102 103 if (vif->p2p || iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) < 5)104 vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW;105 106 return 0;107 108 out_free_bf:109 if (mvm->bf_allowed_vif == mvmvif) {110 mvm->bf_allowed_vif = NULL;111 vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |112 IEEE80211_VIF_SUPPORTS_CQM_RSSI);113 }114 out_remove_mac:115 mvmvif->link[0] = NULL;116 iwl_mvm_mld_mac_ctxt_remove(mvm, vif);117 return ret;118}119 120static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw,121 struct ieee80211_vif *vif)122{123 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);124 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);125 struct iwl_probe_resp_data *probe_data;126 127 iwl_mvm_prepare_mac_removal(mvm, vif);128 129 if (!(vif->type == NL80211_IFTYPE_AP ||130 vif->type == NL80211_IFTYPE_ADHOC))131 iwl_mvm_tcm_rm_vif(mvm, vif);132 133 guard(mvm)(mvm);134 135 if (vif == mvm->csme_vif) {136 iwl_mei_set_netdev(NULL);137 mvm->csme_vif = NULL;138 }139 140 if (mvm->bf_allowed_vif == mvmvif) {141 mvm->bf_allowed_vif = NULL;142 vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |143 IEEE80211_VIF_SUPPORTS_CQM_RSSI);144 }145 146 if (vif->bss_conf.ftm_responder)147 memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats));148 149 iwl_mvm_vif_dbgfs_rm_link(mvm, vif);150 151 /* For AP/GO interface, the tear down of the resources allocated to the152 * interface is be handled as part of the stop_ap flow.153 */154 if (vif->type == NL80211_IFTYPE_AP ||155 vif->type == NL80211_IFTYPE_ADHOC) {156#ifdef CONFIG_NL80211_TESTMODE157 if (vif == mvm->noa_vif) {158 mvm->noa_vif = NULL;159 mvm->noa_duration = 0;160 }161#endif162 }163 164 iwl_mvm_power_update_mac(mvm);165 166 /* Before the interface removal, mac80211 would cancel the ROC, and the167 * ROC worker would be scheduled if needed. The worker would be flushed168 * in iwl_mvm_prepare_mac_removal() and thus at this point the link is169 * not active. So need only to remove the link.170 */171 if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {172 if (mvmvif->deflink.phy_ctxt) {173 iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt);174 mvmvif->deflink.phy_ctxt = NULL;175 }176 mvm->p2p_device_vif = NULL;177 iwl_mvm_remove_link(mvm, vif, &vif->bss_conf);178 } else {179 iwl_mvm_disable_link(mvm, vif, &vif->bss_conf);180 }181 182 iwl_mvm_mld_mac_ctxt_remove(mvm, vif);183 184 RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL);185 186 probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data,187 lockdep_is_held(&mvm->mutex));188 RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL);189 if (probe_data)190 kfree_rcu(probe_data, rcu_head);191 192 if (vif->type == NL80211_IFTYPE_MONITOR) {193 mvm->monitor_on = false;194 __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags);195 }196}197 198static unsigned int iwl_mvm_mld_count_active_links(struct iwl_mvm_vif *mvmvif)199{200 unsigned int n_active = 0;201 int i;202 203 for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {204 if (mvmvif->link[i] && mvmvif->link[i]->phy_ctxt)205 n_active++;206 }207 208 return n_active;209}210 211static void iwl_mvm_restart_mpdu_count(struct iwl_mvm *mvm,212 struct iwl_mvm_vif *mvmvif)213{214 struct ieee80211_sta *ap_sta = mvmvif->ap_sta;215 struct iwl_mvm_sta *mvmsta;216 217 lockdep_assert_held(&mvm->mutex);218 219 if (!ap_sta)220 return;221 222 mvmsta = iwl_mvm_sta_from_mac80211(ap_sta);223 if (!mvmsta->mpdu_counters)224 return;225 226 for (int q = 0; q < mvm->trans->num_rx_queues; q++) {227 spin_lock_bh(&mvmsta->mpdu_counters[q].lock);228 memset(mvmsta->mpdu_counters[q].per_link, 0,229 sizeof(mvmsta->mpdu_counters[q].per_link));230 mvmsta->mpdu_counters[q].window_start = jiffies;231 spin_unlock_bh(&mvmsta->mpdu_counters[q].lock);232 }233 234 IWL_DEBUG_INFO(mvm, "MPDU counters are cleared\n");235}236 237static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm,238 struct ieee80211_vif *vif)239{240 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);241 int link_id, ret = 0;242 243 mvmvif->esr_active = true;244 245 /* Indicate to mac80211 that EML is enabled */246 vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE;247 248 iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW,249 IEEE80211_SMPS_OFF);250 251 for_each_mvm_vif_valid_link(mvmvif, link_id) {252 struct iwl_mvm_vif_link_info *link = mvmvif->link[link_id];253 254 if (!link->phy_ctxt)255 continue;256 257 ret = iwl_mvm_phy_send_rlc(mvm, link->phy_ctxt, 2, 2);258 if (ret)259 break;260 261 link->phy_ctxt->rlc_disabled = true;262 }263 264 if (vif->active_links == mvmvif->link_selection_res &&265 !WARN_ON(!(vif->active_links & BIT(mvmvif->link_selection_primary))))266 mvmvif->primary_link = mvmvif->link_selection_primary;267 else268 mvmvif->primary_link = __ffs(vif->active_links);269 270 /* Needed for tracking RSSI */271 iwl_mvm_request_periodic_system_statistics(mvm, true);272 273 /*274 * Restart the MPDU counters and the counting window, so when the275 * statistics arrive (which is where we look at the counters) we276 * will be at the end of the window.277 */278 iwl_mvm_restart_mpdu_count(mvm, mvmvif);279 280 iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_ESR_LINK_UP,281 NULL);282 283 return ret;284}285 286static int287__iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm,288 struct ieee80211_vif *vif,289 struct ieee80211_bss_conf *link_conf,290 struct ieee80211_chanctx_conf *ctx,291 bool switching_chanctx)292{293 u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;294 struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];295 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);296 unsigned int n_active = iwl_mvm_mld_count_active_links(mvmvif);297 unsigned int link_id = link_conf->link_id;298 int ret;299 300 if (WARN_ON_ONCE(!mvmvif->link[link_id]))301 return -EINVAL;302 303 /* if the assigned one was not counted yet, count it now */304 if (!mvmvif->link[link_id]->phy_ctxt)305 n_active++;306 307 /* mac parameters such as HE support can change at this stage308 * For sta, need first to configure correct state from drv_sta_state309 * and only after that update mac config.310 */311 if (vif->type == NL80211_IFTYPE_AP) {312 ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false);313 if (ret) {314 IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);315 return -EINVAL;316 }317 }318 319 mvmvif->link[link_id]->phy_ctxt = phy_ctxt;320 321 if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) {322 mvmvif->link[link_id]->listen_lmac = true;323 ret = iwl_mvm_esr_mode_active(mvm, vif);324 if (ret) {325 IWL_ERR(mvm, "failed to activate ESR mode (%d)\n", ret);326 iwl_mvm_request_periodic_system_statistics(mvm, false);327 goto out;328 }329 }330 331 if (switching_chanctx) {332 /* reactivate if we turned this off during channel switch */333 if (vif->type == NL80211_IFTYPE_AP)334 mvmvif->ap_ibss_active = true;335 }336 337 /* send it first with phy context ID */338 ret = iwl_mvm_link_changed(mvm, vif, link_conf, 0, false);339 if (ret)340 goto out;341 342 /* Initialize rate control for the AP station, since we might be343 * doing a link switch here - we cannot initialize it before since344 * this needs the phy context assigned (and in FW?), and we cannot345 * do it later because it needs to be initialized as soon as we're346 * able to TX on the link, i.e. when active.347 */348 if (mvmvif->ap_sta) {349 struct ieee80211_link_sta *link_sta;350 351 rcu_read_lock();352 link_sta = rcu_dereference(mvmvif->ap_sta->link[link_id]);353 354 if (!WARN_ON_ONCE(!link_sta))355 iwl_mvm_rs_rate_init(mvm, vif, mvmvif->ap_sta,356 link_conf, link_sta,357 phy_ctxt->channel->band);358 rcu_read_unlock();359 }360 361 /* then activate */362 ret = iwl_mvm_link_changed(mvm, vif, link_conf,363 LINK_CONTEXT_MODIFY_ACTIVE |364 LINK_CONTEXT_MODIFY_RATES_INFO,365 true);366 if (ret)367 goto out;368 369 if (vif->type == NL80211_IFTYPE_STATION)370 iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif,371 link_conf,372 false);373 374 /*375 * Power state must be updated before quotas,376 * otherwise fw will complain.377 */378 iwl_mvm_power_update_mac(mvm);379 380 if (vif->type == NL80211_IFTYPE_MONITOR) {381 ret = iwl_mvm_mld_add_snif_sta(mvm, vif, link_conf);382 if (ret)383 goto deactivate;384 }385 386 return 0;387 388deactivate:389 iwl_mvm_link_changed(mvm, vif, link_conf, LINK_CONTEXT_MODIFY_ACTIVE,390 false);391out:392 mvmvif->link[link_id]->phy_ctxt = NULL;393 iwl_mvm_power_update_mac(mvm);394 return ret;395}396 397static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw,398 struct ieee80211_vif *vif,399 struct ieee80211_bss_conf *link_conf,400 struct ieee80211_chanctx_conf *ctx)401{402 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);403 404 /* update EMLSR mode */405 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {406 int ret;407 408 ret = iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id,409 true);410 /*411 * Don't activate this link if failed to exit EMLSR in412 * the BSS interface413 */414 if (ret)415 return ret;416 }417 418 guard(mvm)(mvm);419 return __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false);420}421 422static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm,423 struct ieee80211_vif *vif)424{425 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);426 struct ieee80211_bss_conf *link_conf;427 int link_id, ret = 0;428 429 mvmvif->esr_active = false;430 431 vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE;432 433 iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW,434 IEEE80211_SMPS_AUTOMATIC);435 436 for_each_vif_active_link(vif, link_conf, link_id) {437 struct ieee80211_chanctx_conf *chanctx_conf;438 struct iwl_mvm_phy_ctxt *phy_ctxt;439 u8 static_chains, dynamic_chains;440 441 mvmvif->link[link_id]->listen_lmac = false;442 443 rcu_read_lock();444 445 chanctx_conf = rcu_dereference(link_conf->chanctx_conf);446 phy_ctxt = mvmvif->link[link_id]->phy_ctxt;447 448 if (!chanctx_conf || !phy_ctxt) {449 rcu_read_unlock();450 continue;451 }452 453 phy_ctxt->rlc_disabled = false;454 static_chains = chanctx_conf->rx_chains_static;455 dynamic_chains = chanctx_conf->rx_chains_dynamic;456 457 rcu_read_unlock();458 459 ret = iwl_mvm_phy_send_rlc(mvm, phy_ctxt, static_chains,460 dynamic_chains);461 if (ret)462 break;463 }464 465 iwl_mvm_request_periodic_system_statistics(mvm, false);466 467 /* Start a new counting window */468 iwl_mvm_restart_mpdu_count(mvm, mvmvif);469 470 iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_ESR_LINK_DOWN,471 NULL);472 473 return ret;474}475 476static void477__iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm,478 struct ieee80211_vif *vif,479 struct ieee80211_bss_conf *link_conf,480 struct ieee80211_chanctx_conf *ctx,481 bool switching_chanctx)482 483{484 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);485 unsigned int n_active = iwl_mvm_mld_count_active_links(mvmvif);486 unsigned int link_id = link_conf->link_id;487 488 /* shouldn't happen, but verify link_id is valid before accessing */489 if (WARN_ON_ONCE(!mvmvif->link[link_id]))490 return;491 492 if (vif->type == NL80211_IFTYPE_AP && switching_chanctx) {493 mvmvif->csa_countdown = false;494 495 /* Set CS bit on all the stations */496 iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);497 498 /* Save blocked iface, the timeout is set on the next beacon */499 rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);500 501 mvmvif->ap_ibss_active = false;502 }503 504 iwl_mvm_link_changed(mvm, vif, link_conf,505 LINK_CONTEXT_MODIFY_ACTIVE, false);506 507 if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) {508 int ret = iwl_mvm_esr_mode_inactive(mvm, vif);509 510 if (ret)511 IWL_ERR(mvm, "failed to deactivate ESR mode (%d)\n",512 ret);513 }514 515 if (vif->type == NL80211_IFTYPE_MONITOR)516 iwl_mvm_mld_rm_snif_sta(mvm, vif);517 518 if (switching_chanctx)519 return;520 mvmvif->link[link_id]->phy_ctxt = NULL;521 iwl_mvm_power_update_mac(mvm);522}523 524static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,525 struct ieee80211_vif *vif,526 struct ieee80211_bss_conf *link_conf,527 struct ieee80211_chanctx_conf *ctx)528{529 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);530 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);531 532 mutex_lock(&mvm->mutex);533 __iwl_mvm_mld_unassign_vif_chanctx(mvm, vif, link_conf, ctx, false);534 /* in the non-MLD case, remove/re-add the link to clean up FW state */535 if (!ieee80211_vif_is_mld(vif) && !mvmvif->ap_sta &&536 !WARN_ON_ONCE(vif->cfg.assoc)) {537 iwl_mvm_remove_link(mvm, vif, link_conf);538 iwl_mvm_add_link(mvm, vif, link_conf);539 }540 mutex_unlock(&mvm->mutex);541 542 /* update EMLSR mode */543 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)544 iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, false);545}546 547static void548iwl_mvm_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd,549 const struct ieee80211_bss_conf *bss_info)550{551 u8 i;552 553 /*554 * NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT,555 * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE556 */557 558 BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) !=559 ARRAY_SIZE(bss_info->tpe.psd_local[0].power));560 561 /* if not valid, mac80211 puts default (max value) */562 for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++)563 cmd->psd_pwr[i] = min(bss_info->tpe.psd_local[0].power[i],564 bss_info->tpe.psd_reg_client[0].power[i]);565 566 BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) !=567 ARRAY_SIZE(bss_info->tpe.max_local[0].power));568 569 for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++)570 cmd->eirp_pwr[i] = min(bss_info->tpe.max_local[0].power[i],571 bss_info->tpe.max_reg_client[0].power[i]);572}573 574void575iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm,576 struct ieee80211_vif *vif,577 struct ieee80211_bss_conf *bss_conf,578 bool is_ap)579{580 struct iwl_txpower_constraints_cmd cmd = {};581 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);582 struct iwl_mvm_vif_link_info *link_info =583 mvmvif->link[bss_conf->link_id];584 u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, AP_TX_POWER_CONSTRAINTS_CMD);585 u32 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,586 IWL_FW_CMD_VER_UNKNOWN);587 int ret;588 589 lockdep_assert_held(&mvm->mutex);590 591 if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN)592 return;593 594 if (!link_info->active ||595 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)596 return;597 598 if (bss_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ)599 return;600 601 cmd.link_id = cpu_to_le16(link_info->fw_link_id);602 memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr));603 memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr));604 605 if (is_ap) {606 cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP);607 } else if (bss_conf->power_type == IEEE80211_REG_UNSET_AP) {608 return;609 } else {610 cmd.ap_type = cpu_to_le16(bss_conf->power_type - 1);611 iwl_mvm_tpe_sta_cmd_data(&cmd, bss_conf);612 }613 614 ret = iwl_mvm_send_cmd_pdu(mvm,615 WIDE_ID(PHY_OPS_GROUP,616 AP_TX_POWER_CONSTRAINTS_CMD),617 0, sizeof(cmd), &cmd);618 if (ret)619 IWL_ERR(mvm,620 "failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n",621 ret);622}623 624static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw,625 struct ieee80211_vif *vif,626 struct ieee80211_bss_conf *link_conf)627{628 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);629 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);630 int ret;631 632 guard(mvm)(mvm);633 634 if (vif->type == NL80211_IFTYPE_AP)635 iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif,636 link_conf, true);637 638 /* Send the beacon template */639 ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf);640 if (ret)641 return ret;642 643 /* the link should be already activated when assigning chan context */644 ret = iwl_mvm_link_changed(mvm, vif, link_conf,645 LINK_CONTEXT_MODIFY_ALL &646 ~LINK_CONTEXT_MODIFY_ACTIVE,647 true);648 if (ret)649 return ret;650 651 ret = iwl_mvm_mld_add_mcast_sta(mvm, vif, link_conf);652 if (ret)653 return ret;654 655 /* Send the bcast station. At this stage the TBTT and DTIM time656 * events are added and applied to the scheduler657 */658 ret = iwl_mvm_mld_add_bcast_sta(mvm, vif, link_conf);659 if (ret)660 goto out_rm_mcast;661 662 if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret))663 goto out_failed;664 665 /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */666 if (vif->p2p && mvm->p2p_device_vif)667 iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);668 669 iwl_mvm_bt_coex_vif_change(mvm);670 671 /* we don't support TDLS during DCM */672 if (iwl_mvm_phy_ctx_count(mvm) > 1)673 iwl_mvm_teardown_tdls_peers(mvm);674 675 iwl_mvm_ftm_restart_responder(mvm, vif, link_conf);676 677 return 0;678 679out_failed:680 iwl_mvm_power_update_mac(mvm);681 mvmvif->ap_ibss_active = false;682 iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf);683out_rm_mcast:684 iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf);685 return ret;686}687 688static int iwl_mvm_mld_start_ap(struct ieee80211_hw *hw,689 struct ieee80211_vif *vif,690 struct ieee80211_bss_conf *link_conf)691{692 return iwl_mvm_mld_start_ap_ibss(hw, vif, link_conf);693}694 695static int iwl_mvm_mld_start_ibss(struct ieee80211_hw *hw,696 struct ieee80211_vif *vif)697{698 return iwl_mvm_mld_start_ap_ibss(hw, vif, &vif->bss_conf);699}700 701static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw,702 struct ieee80211_vif *vif,703 struct ieee80211_bss_conf *link_conf)704{705 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);706 707 guard(mvm)(mvm);708 709 iwl_mvm_stop_ap_ibss_common(mvm, vif);710 711 /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */712 if (vif->p2p && mvm->p2p_device_vif)713 iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);714 715 iwl_mvm_ftm_responder_clear(mvm, vif);716 717 iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf);718 iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf);719 720 iwl_mvm_power_update_mac(mvm);721}722 723static void iwl_mvm_mld_stop_ap(struct ieee80211_hw *hw,724 struct ieee80211_vif *vif,725 struct ieee80211_bss_conf *link_conf)726{727 iwl_mvm_mld_stop_ap_ibss(hw, vif, link_conf);728}729 730static void iwl_mvm_mld_stop_ibss(struct ieee80211_hw *hw,731 struct ieee80211_vif *vif)732{733 iwl_mvm_mld_stop_ap_ibss(hw, vif, &vif->bss_conf);734}735 736static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw,737 struct ieee80211_vif *vif,738 struct ieee80211_sta *sta,739 enum ieee80211_sta_state old_state,740 enum ieee80211_sta_state new_state)741{742 static const struct iwl_mvm_sta_state_ops callbacks = {743 .add_sta = iwl_mvm_mld_add_sta,744 .update_sta = iwl_mvm_mld_update_sta,745 .rm_sta = iwl_mvm_mld_rm_sta,746 .mac_ctxt_changed = iwl_mvm_mld_mac_ctxt_changed,747 };748 749 return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state,750 &callbacks);751}752 753static bool iwl_mvm_esr_bw_criteria(struct iwl_mvm *mvm,754 struct ieee80211_vif *vif,755 struct ieee80211_bss_conf *link_conf)756{757 struct ieee80211_bss_conf *other_link;758 int link_id;759 760 /* Exit EMLSR if links don't have equal bandwidths */761 for_each_vif_active_link(vif, other_link, link_id) {762 if (link_id == link_conf->link_id)763 continue;764 if (link_conf->chanreq.oper.width ==765 other_link->chanreq.oper.width)766 return true;767 }768 769 return false;770}771 772static void773iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm,774 struct ieee80211_vif *vif,775 struct ieee80211_bss_conf *link_conf,776 u64 changes)777{778 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);779 bool has_he, has_eht;780 u32 link_changes = 0;781 int ret;782 783 if (WARN_ON_ONCE(!mvmvif->link[link_conf->link_id]))784 return;785 786 has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax;787 has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be;788 789 /* Update EDCA params */790 if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos)791 link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS;792 793 if (changes & BSS_CHANGED_ERP_SLOT)794 link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO;795 796 if (vif->cfg.assoc && (has_he || has_eht)) {797 IWL_DEBUG_MAC80211(mvm, "Associated in HE mode\n");798 link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;799 }800 801 if ((changes & BSS_CHANGED_BANDWIDTH) &&802 ieee80211_vif_link_active(vif, link_conf->link_id) &&803 mvmvif->esr_active &&804 !iwl_mvm_esr_bw_criteria(mvm, vif, link_conf))805 iwl_mvm_exit_esr(mvm, vif,806 IWL_MVM_ESR_EXIT_BANDWIDTH,807 iwl_mvm_get_primary_link(vif));808 809 /* if associated, maybe puncturing changed - we'll check later */810 if (vif->cfg.assoc)811 link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS;812 813 if (link_changes) {814 ret = iwl_mvm_link_changed(mvm, vif, link_conf, link_changes,815 true);816 if (ret)817 IWL_ERR(mvm, "failed to update link\n");818 }819 820 ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false);821 if (ret)822 IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);823 824 memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid,825 ETH_ALEN);826 827 iwl_mvm_bss_info_changed_station_common(mvm, vif, link_conf, changes);828}829 830static bool iwl_mvm_mld_vif_have_valid_ap_sta(struct iwl_mvm_vif *mvmvif)831{832 int i;833 834 for_each_mvm_vif_valid_link(mvmvif, i) {835 if (mvmvif->link[i]->ap_sta_id != IWL_MVM_INVALID_STA)836 return true;837 }838 839 return false;840}841 842static void iwl_mvm_mld_vif_delete_all_stas(struct iwl_mvm *mvm,843 struct ieee80211_vif *vif)844{845 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);846 int i, ret;847 848 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))849 return;850 851 for_each_mvm_vif_valid_link(mvmvif, i) {852 struct iwl_mvm_vif_link_info *link = mvmvif->link[i];853 854 if (!link)855 continue;856 857 iwl_mvm_sec_key_remove_ap(mvm, vif, link, i);858 ret = iwl_mvm_mld_rm_sta_id(mvm, link->ap_sta_id);859 if (ret)860 IWL_ERR(mvm, "failed to remove AP station\n");861 862 link->ap_sta_id = IWL_MVM_INVALID_STA;863 }864}865 866static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm,867 struct ieee80211_vif *vif,868 u64 changes)869{870 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);871 struct ieee80211_bss_conf *link_conf;872 bool protect = false;873 unsigned int i;874 int ret;875 876 /* This might get called without active links during the877 * chanctx switch, but we don't care about it anyway.878 */879 if (changes == BSS_CHANGED_IDLE)880 return;881 882 ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false);883 if (ret)884 IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);885 886 mvmvif->associated = vif->cfg.assoc;887 888 if (changes & BSS_CHANGED_ASSOC) {889 if (vif->cfg.assoc) {890 mvmvif->session_prot_connection_loss = false;891 892 /* clear statistics to get clean beacon counter */893 iwl_mvm_request_statistics(mvm, true);894 iwl_mvm_sf_update(mvm, vif, false);895 iwl_mvm_power_vif_assoc(mvm, vif);896 897 for_each_mvm_vif_valid_link(mvmvif, i) {898 memset(&mvmvif->link[i]->beacon_stats, 0,899 sizeof(mvmvif->link[i]->beacon_stats));900 901 if (vif->p2p) {902 iwl_mvm_update_smps(mvm, vif,903 IWL_MVM_SMPS_REQ_PROT,904 IEEE80211_SMPS_DYNAMIC, i);905 }906 907 rcu_read_lock();908 link_conf = rcu_dereference(vif->link_conf[i]);909 if (link_conf && !link_conf->dtim_period)910 protect = true;911 rcu_read_unlock();912 }913 914 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&915 protect) {916 /* We are in assoc so only one link is active-917 * The association link918 */919 unsigned int link_id =920 ffs(vif->active_links) - 1;921 922 /* If we're not restarting and still haven't923 * heard a beacon (dtim period unknown) then924 * make sure we still have enough minimum time925 * remaining in the time event, since the auth926 * might actually have taken quite a while927 * (especially for SAE) and so the remaining928 * time could be small without us having heard929 * a beacon yet.930 */931 iwl_mvm_protect_assoc(mvm, vif, 0, link_id);932 }933 934 iwl_mvm_sf_update(mvm, vif, false);935 936 /* FIXME: need to decide about misbehaving AP handling */937 iwl_mvm_power_vif_assoc(mvm, vif);938 } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) {939 iwl_mvm_mei_host_disassociated(mvm);940 941 /* If update fails - SF might be running in associated942 * mode while disassociated - which is forbidden.943 */944 ret = iwl_mvm_sf_update(mvm, vif, false);945 WARN_ONCE(ret &&946 !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,947 &mvm->status),948 "Failed to update SF upon disassociation\n");949 950 /* If we get an assert during the connection (after the951 * station has been added, but before the vif is set952 * to associated), mac80211 will re-add the station and953 * then configure the vif. Since the vif is not954 * associated, we would remove the station here and955 * this would fail the recovery.956 */957 iwl_mvm_mld_vif_delete_all_stas(mvm, vif);958 }959 960 iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes);961 }962 963 if (changes & BSS_CHANGED_PS) {964 ret = iwl_mvm_power_update_mac(mvm);965 if (ret)966 IWL_ERR(mvm, "failed to update power mode\n");967 }968 969 if (changes & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM) &&970 ieee80211_vif_is_mld(vif) && mvmvif->authorized)971 wiphy_delayed_work_queue(mvm->hw->wiphy,972 &mvmvif->mlo_int_scan_wk, 0);973}974 975static void976iwl_mvm_mld_link_info_changed_ap_ibss(struct iwl_mvm *mvm,977 struct ieee80211_vif *vif,978 struct ieee80211_bss_conf *link_conf,979 u64 changes)980{981 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);982 u32 link_changes = LINK_CONTEXT_MODIFY_PROTECT_FLAGS |983 LINK_CONTEXT_MODIFY_QOS_PARAMS;984 985 /* Changes will be applied when the AP/IBSS is started */986 if (!mvmvif->ap_ibss_active)987 return;988 989 if (link_conf->he_support)990 link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;991 992 if (changes & BSS_CHANGED_ERP_SLOT)993 link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO;994 995 if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_SLOT |996 BSS_CHANGED_HT |997 BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS |998 BSS_CHANGED_HE_BSS_COLOR) &&999 iwl_mvm_link_changed(mvm, vif, link_conf,1000 link_changes, true))1001 IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);1002 1003 /* Need to send a new beacon template to the FW */1004 if (changes & BSS_CHANGED_BEACON &&1005 iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf))1006 IWL_WARN(mvm, "Failed updating beacon data\n");1007 1008 /* FIXME: need to decide if we need FTM responder per link */1009 if (changes & BSS_CHANGED_FTM_RESPONDER) {1010 int ret = iwl_mvm_ftm_start_responder(mvm, vif, link_conf);1011 1012 if (ret)1013 IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n",1014 ret);1015 }1016}1017 1018static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw,1019 struct ieee80211_vif *vif,1020 struct ieee80211_bss_conf *link_conf,1021 u64 changes)1022{1023 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1024 1025 guard(mvm)(mvm);1026 1027 switch (vif->type) {1028 case NL80211_IFTYPE_STATION:1029 iwl_mvm_mld_link_info_changed_station(mvm, vif, link_conf,1030 changes);1031 break;1032 case NL80211_IFTYPE_AP:1033 case NL80211_IFTYPE_ADHOC:1034 iwl_mvm_mld_link_info_changed_ap_ibss(mvm, vif, link_conf,1035 changes);1036 break;1037 case NL80211_IFTYPE_MONITOR:1038 if (changes & BSS_CHANGED_MU_GROUPS)1039 iwl_mvm_update_mu_groups(mvm, vif);1040 break;1041 default:1042 /* shouldn't happen */1043 WARN_ON_ONCE(1);1044 }1045 1046 if (changes & BSS_CHANGED_TXPOWER) {1047 IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n",1048 link_conf->txpower);1049 iwl_mvm_set_tx_power(mvm, vif, link_conf->txpower);1050 }1051}1052 1053static void iwl_mvm_mld_vif_cfg_changed(struct ieee80211_hw *hw,1054 struct ieee80211_vif *vif,1055 u64 changes)1056{1057 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1058 1059 guard(mvm)(mvm);1060 1061 if (changes & BSS_CHANGED_IDLE && !vif->cfg.idle)1062 iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);1063 1064 if (vif->type == NL80211_IFTYPE_STATION)1065 iwl_mvm_mld_vif_cfg_changed_station(mvm, vif, changes);1066}1067 1068static int1069iwl_mvm_mld_switch_vif_chanctx(struct ieee80211_hw *hw,1070 struct ieee80211_vif_chanctx_switch *vifs,1071 int n_vifs,1072 enum ieee80211_chanctx_switch_mode mode)1073{1074 static const struct iwl_mvm_switch_vif_chanctx_ops ops = {1075 .__assign_vif_chanctx = __iwl_mvm_mld_assign_vif_chanctx,1076 .__unassign_vif_chanctx = __iwl_mvm_mld_unassign_vif_chanctx,1077 };1078 1079 return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops);1080}1081 1082static void iwl_mvm_mld_config_iface_filter(struct ieee80211_hw *hw,1083 struct ieee80211_vif *vif,1084 unsigned int filter_flags,1085 unsigned int changed_flags)1086{1087 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1088 1089 /* We support only filter for probe requests */1090 if (!(changed_flags & FIF_PROBE_REQ))1091 return;1092 1093 /* Supported only for p2p client interfaces */1094 if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc ||1095 !vif->p2p)1096 return;1097 1098 guard(mvm)(mvm);1099 iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false);1100}1101 1102static int1103iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw,1104 struct ieee80211_vif *vif,1105 unsigned int link_id, u16 ac,1106 const struct ieee80211_tx_queue_params *params)1107{1108 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1109 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);1110 struct iwl_mvm_vif_link_info *mvm_link = mvmvif->link[link_id];1111 1112 if (!mvm_link)1113 return -EINVAL;1114 1115 mvm_link->queue_params[ac] = *params;1116 1117 /* No need to update right away, we'll get BSS_CHANGED_QOS1118 * The exception is P2P_DEVICE interface which needs immediate update.1119 */1120 if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {1121 guard(mvm)(mvm);1122 return iwl_mvm_link_changed(mvm, vif, &vif->bss_conf,1123 LINK_CONTEXT_MODIFY_QOS_PARAMS,1124 true);1125 }1126 return 0;1127}1128 1129static int iwl_mvm_mld_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif)1130{1131 int ret;1132 1133 lockdep_assert_held(&mvm->mutex);1134 1135 /* The PHY context ID might have changed so need to set it */1136 ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 0, false);1137 if (WARN(ret, "Failed to set PHY context ID\n"))1138 return ret;1139 1140 ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf,1141 LINK_CONTEXT_MODIFY_ACTIVE |1142 LINK_CONTEXT_MODIFY_RATES_INFO,1143 true);1144 1145 if (WARN(ret, "Failed linking P2P_DEVICE\n"))1146 return ret;1147 1148 /* The station and queue allocation must be done only after the linking1149 * is done, as otherwise the FW might incorrectly configure its state.1150 */1151 return iwl_mvm_mld_add_bcast_sta(mvm, vif, &vif->bss_conf);1152}1153 1154static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,1155 struct ieee80211_channel *channel, int duration,1156 enum ieee80211_roc_type type)1157{1158 static const struct iwl_mvm_roc_ops ops = {1159 .add_aux_sta_for_hs20 = iwl_mvm_mld_add_aux_sta,1160 .link = iwl_mvm_mld_roc_link,1161 };1162 1163 return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops);1164}1165 1166static int1167iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw,1168 struct ieee80211_vif *vif,1169 u16 old_links, u16 new_links,1170 struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])1171{1172 struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {};1173 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);1174 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1175 u16 removed = old_links & ~new_links;1176 u16 added = new_links & ~old_links;1177 int err, i;1178 1179 for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {1180 int r;1181 1182 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))1183 break;1184 1185 if (!(added & BIT(i)))1186 continue;1187 new_link[i] = kzalloc(sizeof(*new_link[i]), GFP_KERNEL);1188 if (!new_link[i]) {1189 err = -ENOMEM;1190 goto free;1191 }1192 1193 new_link[i]->bcast_sta.sta_id = IWL_MVM_INVALID_STA;1194 new_link[i]->mcast_sta.sta_id = IWL_MVM_INVALID_STA;1195 new_link[i]->ap_sta_id = IWL_MVM_INVALID_STA;1196 new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;1197 1198 for (r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++)1199 new_link[i]->smps_requests[r] =1200 IEEE80211_SMPS_AUTOMATIC;1201 }1202 1203 mutex_lock(&mvm->mutex);1204 1205 /* If we're in RESTART flow, the default link wasn't added in1206 * drv_add_interface(), and link[0] doesn't point to it.1207 */1208 if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART,1209 &mvm->status)) {1210 err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf);1211 if (err)1212 goto out_err;1213 mvmvif->link[0] = NULL;1214 }1215 1216 for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {1217 if (removed & BIT(i)) {1218 struct ieee80211_bss_conf *link_conf = old[i];1219 1220 err = iwl_mvm_disable_link(mvm, vif, link_conf);1221 if (err)1222 goto out_err;1223 kfree(mvmvif->link[i]);1224 mvmvif->link[i] = NULL;1225 } else if (added & BIT(i)) {1226 struct ieee80211_bss_conf *link_conf;1227 1228 link_conf = link_conf_dereference_protected(vif, i);1229 if (WARN_ON(!link_conf))1230 continue;1231 1232 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART,1233 &mvm->status))1234 mvmvif->link[i] = new_link[i];1235 new_link[i] = NULL;1236 err = iwl_mvm_add_link(mvm, vif, link_conf);1237 if (err)1238 goto out_err;1239 }1240 }1241 1242 err = 0;1243 if (new_links == 0) {1244 mvmvif->link[0] = &mvmvif->deflink;1245 err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);1246 if (err == 0)1247 mvmvif->primary_link = 0;1248 } else if (!(new_links & BIT(mvmvif->primary_link))) {1249 /*1250 * Ensure we always have a valid primary_link, the real1251 * decision happens later when PHY is activated.1252 */1253 mvmvif->primary_link = __ffs(new_links);1254 }1255 1256out_err:1257 /* we really don't have a good way to roll back here ... */1258 mutex_unlock(&mvm->mutex);1259 1260free:1261 for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)1262 kfree(new_link[i]);1263 return err;1264}1265 1266static int1267iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw,1268 struct ieee80211_vif *vif,1269 struct ieee80211_sta *sta,1270 u16 old_links, u16 new_links)1271{1272 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1273 1274 guard(mvm)(mvm);1275 return iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links);1276}1277 1278bool iwl_mvm_vif_has_esr_cap(struct iwl_mvm *mvm, struct ieee80211_vif *vif)1279{1280 const struct wiphy_iftype_ext_capab *ext_capa;1281 1282 lockdep_assert_held(&mvm->mutex);1283 1284 if (!ieee80211_vif_is_mld(vif) || !vif->cfg.assoc ||1285 hweight16(ieee80211_vif_usable_links(vif)) == 1)1286 return false;1287 1288 if (!(vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP))1289 return false;1290 1291 ext_capa = cfg80211_get_iftype_ext_capa(mvm->hw->wiphy,1292 ieee80211_vif_type_p2p(vif));1293 return (ext_capa &&1294 (ext_capa->eml_capabilities & IEEE80211_EML_CAP_EMLSR_SUPP));1295}1296 1297static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw,1298 struct ieee80211_vif *vif,1299 u16 desired_links)1300{1301 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1302 int n_links = hweight16(desired_links);1303 1304 if (n_links <= 1)1305 return true;1306 1307 guard(mvm)(mvm);1308 1309 /* Check if HW supports the wanted number of links */1310 if (n_links > iwl_mvm_max_active_links(mvm, vif))1311 return false;1312 1313 /* If it is an eSR device, check that we can enter eSR */1314 return iwl_mvm_is_esr_supported(mvm->fwrt.trans) &&1315 iwl_mvm_vif_has_esr_cap(mvm, vif);1316}1317 1318static enum ieee80211_neg_ttlm_res1319iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,1320 struct ieee80211_neg_ttlm *neg_ttlm)1321{1322 u16 map;1323 u8 i;1324 1325 /* Verify all TIDs are mapped to the same links set */1326 map = neg_ttlm->downlink[0];1327 for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) {1328 if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] ||1329 neg_ttlm->uplink[i] != map)1330 return NEG_TTLM_RES_REJECT;1331 }1332 1333 return NEG_TTLM_RES_ACCEPT;1334}1335 1336static int1337iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw,1338 struct ieee80211_vif *vif,1339 struct ieee80211_channel_switch *chsw)1340{1341 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);1342 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);1343 int ret;1344 1345 mutex_lock(&mvm->mutex);1346 if (mvmvif->esr_active) {1347 u8 primary = iwl_mvm_get_primary_link(vif);1348 int selected;1349 1350 /* prefer primary unless quiet CSA on it */1351 if (chsw->link_id == primary && chsw->block_tx)1352 selected = iwl_mvm_get_other_link(vif, primary);1353 else1354 selected = primary;1355 1356 /*1357 * remembers to tell the firmware that this link can't tx1358 * Note that this logic seems to be unrelated to esr, but it1359 * really is needed only when esr is active. When we have a1360 * single link, the firmware will handle all this on its own.1361 * In multi-link scenarios, we can learn about the CSA from1362 * another link and this logic is too complex for the firmware1363 * to track.1364 * Since we want to de-activate the link that got a CSA, we1365 * need to tell the firmware not to send any frame on that link1366 * as the firmware may not be aware that link is under a CSA1367 * with mode=1 (no Tx allowed).1368 */1369 if (chsw->block_tx && mvmvif->link[chsw->link_id])1370 mvmvif->link[chsw->link_id]->csa_block_tx = true;1371 1372 iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_CSA, selected);1373 mutex_unlock(&mvm->mutex);1374 1375 /*1376 * If we've not kept the link active that's doing the CSA1377 * then we don't need to do anything else, just return.1378 */1379 if (selected != chsw->link_id)1380 return 0;1381 1382 mutex_lock(&mvm->mutex);1383 }1384 1385 ret = iwl_mvm_pre_channel_switch(mvm, vif, chsw);1386 mutex_unlock(&mvm->mutex);1387 1388 return ret;1389}1390 1391const struct ieee80211_ops iwl_mvm_mld_hw_ops = {1392 .tx = iwl_mvm_mac_tx,1393 .wake_tx_queue = iwl_mvm_mac_wake_tx_queue,1394 .ampdu_action = iwl_mvm_mac_ampdu_action,1395 .get_antenna = iwl_mvm_op_get_antenna,1396 .set_antenna = iwl_mvm_op_set_antenna,1397 .start = iwl_mvm_mac_start,1398 .reconfig_complete = iwl_mvm_mac_reconfig_complete,1399 .stop = iwl_mvm_mac_stop,1400 .add_interface = iwl_mvm_mld_mac_add_interface,1401 .remove_interface = iwl_mvm_mld_mac_remove_interface,1402 .config = iwl_mvm_mac_config,1403 .prepare_multicast = iwl_mvm_prepare_multicast,1404 .configure_filter = iwl_mvm_configure_filter,1405 .config_iface_filter = iwl_mvm_mld_config_iface_filter,1406 .link_info_changed = iwl_mvm_mld_link_info_changed,1407 .vif_cfg_changed = iwl_mvm_mld_vif_cfg_changed,1408 .hw_scan = iwl_mvm_mac_hw_scan,1409 .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,1410 .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove,1411 .sta_state = iwl_mvm_mld_mac_sta_state,1412 .sta_notify = iwl_mvm_mac_sta_notify,1413 .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,1414 .release_buffered_frames = iwl_mvm_mac_release_buffered_frames,1415 .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,1416 .sta_rc_update = iwl_mvm_sta_rc_update,1417 .conf_tx = iwl_mvm_mld_mac_conf_tx,1418 .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,1419 .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx,1420 .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,1421 .flush = iwl_mvm_mac_flush,1422 .flush_sta = iwl_mvm_mac_flush_sta,1423 .sched_scan_start = iwl_mvm_mac_sched_scan_start,1424 .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,1425 .set_key = iwl_mvm_mac_set_key,1426 .update_tkip_key = iwl_mvm_mac_update_tkip_key,1427 .remain_on_channel = iwl_mvm_mld_roc,1428 .cancel_remain_on_channel = iwl_mvm_cancel_roc,1429 .add_chanctx = iwl_mvm_add_chanctx,1430 .remove_chanctx = iwl_mvm_remove_chanctx,1431 .change_chanctx = iwl_mvm_change_chanctx,1432 .assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx,1433 .unassign_vif_chanctx = iwl_mvm_mld_unassign_vif_chanctx,1434 .switch_vif_chanctx = iwl_mvm_mld_switch_vif_chanctx,1435 1436 .start_ap = iwl_mvm_mld_start_ap,1437 .stop_ap = iwl_mvm_mld_stop_ap,1438 .join_ibss = iwl_mvm_mld_start_ibss,1439 .leave_ibss = iwl_mvm_mld_stop_ibss,1440 1441 .tx_last_beacon = iwl_mvm_tx_last_beacon,1442 1443 .channel_switch = iwl_mvm_channel_switch,1444 .pre_channel_switch = iwl_mvm_mld_mac_pre_channel_switch,1445 .post_channel_switch = iwl_mvm_post_channel_switch,1446 .abort_channel_switch = iwl_mvm_abort_channel_switch,1447 .channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon,1448 1449 .tdls_channel_switch = iwl_mvm_tdls_channel_switch,1450 .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,1451 .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,1452 1453 .event_callback = iwl_mvm_mac_event_callback,1454 1455 .sync_rx_queues = iwl_mvm_sync_rx_queues,1456 1457 CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)1458 1459#ifdef CONFIG_PM_SLEEP1460 /* look at d3.c */1461 .suspend = iwl_mvm_suspend,1462 .resume = iwl_mvm_resume,1463 .set_wakeup = iwl_mvm_set_wakeup,1464 .set_rekey_data = iwl_mvm_set_rekey_data,1465#if IS_ENABLED(CONFIG_IPV6)1466 .ipv6_addr_change = iwl_mvm_ipv6_addr_change,1467#endif1468 .set_default_unicast_key = iwl_mvm_set_default_unicast_key,1469#endif1470 .get_survey = iwl_mvm_mac_get_survey,1471 .sta_statistics = iwl_mvm_mac_sta_statistics,1472 .get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats,1473 .start_pmsr = iwl_mvm_start_pmsr,1474 .abort_pmsr = iwl_mvm_abort_pmsr,1475 1476#ifdef CONFIG_IWLWIFI_DEBUGFS1477 .vif_add_debugfs = iwl_mvm_vif_add_debugfs,1478 .link_add_debugfs = iwl_mvm_link_add_debugfs,1479 .link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs,1480#endif1481 .set_hw_timestamp = iwl_mvm_set_hw_timestamp,1482 1483 .change_vif_links = iwl_mvm_mld_change_vif_links,1484 .change_sta_links = iwl_mvm_mld_change_sta_links,1485 .can_activate_links = iwl_mvm_mld_can_activate_links,1486 .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm,1487};1488