brintos

brintos / linux-shallow public Read only

0
0
Text · 41.9 KiB · e252f0d Raw
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