xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/mld-sta.c (revision a4128aad8503277614f2d214011ef60a19447b83)
19af1bba4SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
29af1bba4SBjoern A. Zeeb /*
3*a4128aadSBjoern A. Zeeb  * Copyright (C) 2022-2024 Intel Corporation
49af1bba4SBjoern A. Zeeb  */
59af1bba4SBjoern A. Zeeb #include "mvm.h"
69af1bba4SBjoern A. Zeeb #include "time-sync.h"
79af1bba4SBjoern A. Zeeb #include "sta.h"
89af1bba4SBjoern A. Zeeb 
99af1bba4SBjoern A. Zeeb u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
109af1bba4SBjoern A. Zeeb 			   int filter_link_id)
119af1bba4SBjoern A. Zeeb {
12*a4128aadSBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
139af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
14*a4128aadSBjoern A. Zeeb 	struct ieee80211_vif *vif;
159af1bba4SBjoern A. Zeeb 	unsigned int link_id;
169af1bba4SBjoern A. Zeeb 	u32 result = 0;
179af1bba4SBjoern A. Zeeb 
189af1bba4SBjoern A. Zeeb 	if (!sta)
199af1bba4SBjoern A. Zeeb 		return 0;
209af1bba4SBjoern A. Zeeb 
219af1bba4SBjoern A. Zeeb 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
22*a4128aadSBjoern A. Zeeb 	vif = mvmsta->vif;
239af1bba4SBjoern A. Zeeb 
249af1bba4SBjoern A. Zeeb 	/* it's easy when the STA is not an MLD */
259af1bba4SBjoern A. Zeeb 	if (!sta->valid_links)
269af1bba4SBjoern A. Zeeb 		return BIT(mvmsta->deflink.sta_id);
279af1bba4SBjoern A. Zeeb 
289af1bba4SBjoern A. Zeeb 	/* but if it is an MLD, get the mask of all the FW STAs it has ... */
29*a4128aadSBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
30*a4128aadSBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta;
319af1bba4SBjoern A. Zeeb 
329af1bba4SBjoern A. Zeeb 		/* unless we have a specific link in mind */
339af1bba4SBjoern A. Zeeb 		if (filter_link_id >= 0 && link_id != filter_link_id)
349af1bba4SBjoern A. Zeeb 			continue;
359af1bba4SBjoern A. Zeeb 
36*a4128aadSBjoern A. Zeeb 		mvm_link_sta =
379af1bba4SBjoern A. Zeeb 			rcu_dereference_check(mvmsta->link[link_id],
389af1bba4SBjoern A. Zeeb 					      lockdep_is_held(&mvm->mutex));
39*a4128aadSBjoern A. Zeeb 		if (!mvm_link_sta)
409af1bba4SBjoern A. Zeeb 			continue;
419af1bba4SBjoern A. Zeeb 
42*a4128aadSBjoern A. Zeeb 		result |= BIT(mvm_link_sta->sta_id);
439af1bba4SBjoern A. Zeeb 	}
449af1bba4SBjoern A. Zeeb 
459af1bba4SBjoern A. Zeeb 	return result;
469af1bba4SBjoern A. Zeeb }
479af1bba4SBjoern A. Zeeb 
489af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm,
499af1bba4SBjoern A. Zeeb 				    struct iwl_mvm_sta_cfg_cmd *cmd)
509af1bba4SBjoern A. Zeeb {
519af1bba4SBjoern A. Zeeb 	int ret = iwl_mvm_send_cmd_pdu(mvm,
529af1bba4SBjoern A. Zeeb 				       WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
539af1bba4SBjoern A. Zeeb 				       0, sizeof(*cmd), cmd);
549af1bba4SBjoern A. Zeeb 	if (ret)
559af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
569af1bba4SBjoern A. Zeeb 	return ret;
579af1bba4SBjoern A. Zeeb }
589af1bba4SBjoern A. Zeeb 
599af1bba4SBjoern A. Zeeb /*
609af1bba4SBjoern A. Zeeb  * Add an internal station to the FW table
619af1bba4SBjoern A. Zeeb  */
629af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm,
639af1bba4SBjoern A. Zeeb 					 struct iwl_mvm_int_sta *sta,
649af1bba4SBjoern A. Zeeb 					 const u8 *addr, int link_id)
659af1bba4SBjoern A. Zeeb {
669af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta_cfg_cmd cmd;
679af1bba4SBjoern A. Zeeb 
689af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
699af1bba4SBjoern A. Zeeb 
709af1bba4SBjoern A. Zeeb 	memset(&cmd, 0, sizeof(cmd));
719af1bba4SBjoern A. Zeeb 	cmd.sta_id = cpu_to_le32((u8)sta->sta_id);
729af1bba4SBjoern A. Zeeb 
739af1bba4SBjoern A. Zeeb 	cmd.link_id = cpu_to_le32(link_id);
749af1bba4SBjoern A. Zeeb 
759af1bba4SBjoern A. Zeeb 	cmd.station_type = cpu_to_le32(sta->type);
769af1bba4SBjoern A. Zeeb 
779af1bba4SBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa,
789af1bba4SBjoern A. Zeeb 			IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT) &&
799af1bba4SBjoern A. Zeeb 	    sta->type == STATION_TYPE_BCAST_MGMT)
809af1bba4SBjoern A. Zeeb 		cmd.mfp = cpu_to_le32(1);
819af1bba4SBjoern A. Zeeb 
829af1bba4SBjoern A. Zeeb 	if (addr) {
839af1bba4SBjoern A. Zeeb 		memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
849af1bba4SBjoern A. Zeeb 		memcpy(cmd.peer_link_address, addr, ETH_ALEN);
859af1bba4SBjoern A. Zeeb 	}
869af1bba4SBjoern A. Zeeb 
879af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_send_sta_cmd(mvm, &cmd);
889af1bba4SBjoern A. Zeeb }
899af1bba4SBjoern A. Zeeb 
909af1bba4SBjoern A. Zeeb /*
919af1bba4SBjoern A. Zeeb  * Remove a station from the FW table. Before sending the command to remove
929af1bba4SBjoern A. Zeeb  * the station validate that the station is indeed known to the driver (sanity
939af1bba4SBjoern A. Zeeb  * only).
949af1bba4SBjoern A. Zeeb  */
959af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm *mvm, u32 sta_id)
969af1bba4SBjoern A. Zeeb {
979af1bba4SBjoern A. Zeeb 	struct iwl_mvm_remove_sta_cmd rm_sta_cmd = {
989af1bba4SBjoern A. Zeeb 		.sta_id = cpu_to_le32(sta_id),
999af1bba4SBjoern A. Zeeb 	};
1009af1bba4SBjoern A. Zeeb 	int ret;
1019af1bba4SBjoern A. Zeeb 
1029af1bba4SBjoern A. Zeeb 	/* Note: internal stations are marked as error values */
1039af1bba4SBjoern A. Zeeb 	if (!rcu_access_pointer(mvm->fw_id_to_mac_id[sta_id])) {
1049af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Invalid station id %d\n", sta_id);
1059af1bba4SBjoern A. Zeeb 		return -EINVAL;
1069af1bba4SBjoern A. Zeeb 	}
1079af1bba4SBjoern A. Zeeb 
1089af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD),
1099af1bba4SBjoern A. Zeeb 				   0, sizeof(rm_sta_cmd), &rm_sta_cmd);
1109af1bba4SBjoern A. Zeeb 	if (ret) {
1119af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
1129af1bba4SBjoern A. Zeeb 		return ret;
1139af1bba4SBjoern A. Zeeb 	}
1149af1bba4SBjoern A. Zeeb 
1159af1bba4SBjoern A. Zeeb 	return 0;
1169af1bba4SBjoern A. Zeeb }
1179af1bba4SBjoern A. Zeeb 
1189af1bba4SBjoern A. Zeeb static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm,
1199af1bba4SBjoern A. Zeeb 				     struct iwl_mvm_int_sta *sta,
1209af1bba4SBjoern A. Zeeb 				     u32 lmac_id)
1219af1bba4SBjoern A. Zeeb {
1229af1bba4SBjoern A. Zeeb 	int ret;
1239af1bba4SBjoern A. Zeeb 
1249af1bba4SBjoern A. Zeeb 	struct iwl_mvm_aux_sta_cmd cmd = {
1259af1bba4SBjoern A. Zeeb 		.sta_id = cpu_to_le32(sta->sta_id),
1269af1bba4SBjoern A. Zeeb 		.lmac_id = cpu_to_le32(lmac_id),
1279af1bba4SBjoern A. Zeeb 	};
1289af1bba4SBjoern A. Zeeb 
1299af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD),
1309af1bba4SBjoern A. Zeeb 				   0, sizeof(cmd), &cmd);
1319af1bba4SBjoern A. Zeeb 	if (ret)
1329af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send AUX_STA_CMD\n");
1339af1bba4SBjoern A. Zeeb 	return ret;
1349af1bba4SBjoern A. Zeeb }
1359af1bba4SBjoern A. Zeeb 
1369af1bba4SBjoern A. Zeeb /*
1379af1bba4SBjoern A. Zeeb  * Adds an internal sta to the FW table with its queues
1389af1bba4SBjoern A. Zeeb  */
1399af1bba4SBjoern A. Zeeb int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm,
1409af1bba4SBjoern A. Zeeb 				       struct iwl_mvm_int_sta *sta,
1419af1bba4SBjoern A. Zeeb 				       const u8 *addr, int link_id,
1429af1bba4SBjoern A. Zeeb 				       u16 *queue, u8 tid,
1439af1bba4SBjoern A. Zeeb 				       unsigned int *_wdg_timeout)
1449af1bba4SBjoern A. Zeeb {
1459af1bba4SBjoern A. Zeeb 	int ret, txq;
1469af1bba4SBjoern A. Zeeb 	unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout :
1479af1bba4SBjoern A. Zeeb 		mvm->trans->trans_cfg->base_params->wd_timeout;
1489af1bba4SBjoern A. Zeeb 
1499af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA))
1509af1bba4SBjoern A. Zeeb 		return -ENOSPC;
1519af1bba4SBjoern A. Zeeb 
1529af1bba4SBjoern A. Zeeb 	if (sta->type == STATION_TYPE_AUX)
1539af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_add_aux_sta_to_fw(mvm, sta, link_id);
1549af1bba4SBjoern A. Zeeb 	else
1559af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_add_int_sta_to_fw(mvm, sta, addr, link_id);
1569af1bba4SBjoern A. Zeeb 	if (ret)
1579af1bba4SBjoern A. Zeeb 		return ret;
1589af1bba4SBjoern A. Zeeb 
1599af1bba4SBjoern A. Zeeb 	/*
1609af1bba4SBjoern A. Zeeb 	 * For 22000 firmware and on we cannot add queue to a station unknown
1619af1bba4SBjoern A. Zeeb 	 * to firmware so enable queue here - after the station was added
1629af1bba4SBjoern A. Zeeb 	 */
1639af1bba4SBjoern A. Zeeb 	txq = iwl_mvm_tvqm_enable_txq(mvm, NULL, sta->sta_id, tid,
1649af1bba4SBjoern A. Zeeb 				      wdg_timeout);
1659af1bba4SBjoern A. Zeeb 	if (txq < 0) {
1669af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_rm_sta_from_fw(mvm, sta->sta_id);
1679af1bba4SBjoern A. Zeeb 		return txq;
1689af1bba4SBjoern A. Zeeb 	}
1699af1bba4SBjoern A. Zeeb 	*queue = txq;
1709af1bba4SBjoern A. Zeeb 
1719af1bba4SBjoern A. Zeeb 	return 0;
1729af1bba4SBjoern A. Zeeb }
1739af1bba4SBjoern A. Zeeb 
1749af1bba4SBjoern A. Zeeb /*
1759af1bba4SBjoern A. Zeeb  * Adds a new int sta: allocate it in the driver, add it to the FW table,
1769af1bba4SBjoern A. Zeeb  * and add its queues.
1779af1bba4SBjoern A. Zeeb  */
1789af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_add_int_sta(struct iwl_mvm *mvm,
1799af1bba4SBjoern A. Zeeb 				   struct iwl_mvm_int_sta *int_sta, u16 *queue,
1809af1bba4SBjoern A. Zeeb 				   enum nl80211_iftype iftype,
1819af1bba4SBjoern A. Zeeb 				   enum iwl_fw_sta_type sta_type,
1829af1bba4SBjoern A. Zeeb 				   int link_id, const u8 *addr, u8 tid,
1839af1bba4SBjoern A. Zeeb 				   unsigned int *wdg_timeout)
1849af1bba4SBjoern A. Zeeb {
1859af1bba4SBjoern A. Zeeb 	int ret;
1869af1bba4SBjoern A. Zeeb 
1879af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1889af1bba4SBjoern A. Zeeb 
1899af1bba4SBjoern A. Zeeb 	/* qmask argument is not used in the new tx api, send a don't care */
1909af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_allocate_int_sta(mvm, int_sta, 0, iftype,
1919af1bba4SBjoern A. Zeeb 				       sta_type);
1929af1bba4SBjoern A. Zeeb 	if (ret)
1939af1bba4SBjoern A. Zeeb 		return ret;
1949af1bba4SBjoern A. Zeeb 
1959af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, int_sta, addr, link_id,
1969af1bba4SBjoern A. Zeeb 						 queue, tid, wdg_timeout);
1979af1bba4SBjoern A. Zeeb 	if (ret) {
1989af1bba4SBjoern A. Zeeb 		iwl_mvm_dealloc_int_sta(mvm, int_sta);
1999af1bba4SBjoern A. Zeeb 		return ret;
2009af1bba4SBjoern A. Zeeb 	}
2019af1bba4SBjoern A. Zeeb 
2029af1bba4SBjoern A. Zeeb 	return 0;
2039af1bba4SBjoern A. Zeeb }
2049af1bba4SBjoern A. Zeeb 
2059af1bba4SBjoern A. Zeeb /* Allocate a new station entry for the broadcast station to the given vif,
2069af1bba4SBjoern A. Zeeb  * and send it to the FW.
2079af1bba4SBjoern A. Zeeb  * Note that each P2P mac should have its own broadcast station.
2089af1bba4SBjoern A. Zeeb  */
2099af1bba4SBjoern A. Zeeb int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
2109af1bba4SBjoern A. Zeeb 			      struct ieee80211_bss_conf *link_conf)
2119af1bba4SBjoern A. Zeeb {
2129af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2139af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *mvm_link =
2149af1bba4SBjoern A. Zeeb 		mvmvif->link[link_conf->link_id];
2159af1bba4SBjoern A. Zeeb 	struct iwl_mvm_int_sta *bsta = &mvm_link->bcast_sta;
2169af1bba4SBjoern A. Zeeb 	static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
2179af1bba4SBjoern A. Zeeb 	const u8 *baddr = _baddr;
2189af1bba4SBjoern A. Zeeb 	unsigned int wdg_timeout =
2199af1bba4SBjoern A. Zeeb 		iwl_mvm_get_wd_timeout(mvm, vif, false, false);
2209af1bba4SBjoern A. Zeeb 	u16 *queue;
2219af1bba4SBjoern A. Zeeb 
2229af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
2239af1bba4SBjoern A. Zeeb 
2249af1bba4SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_ADHOC)
2259af1bba4SBjoern A. Zeeb 		baddr = link_conf->bssid;
2269af1bba4SBjoern A. Zeeb 
2279af1bba4SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_AP ||
2289af1bba4SBjoern A. Zeeb 	    vif->type == NL80211_IFTYPE_ADHOC) {
2299af1bba4SBjoern A. Zeeb 		queue = &mvm_link->mgmt_queue;
2309af1bba4SBjoern A. Zeeb 	} else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
2319af1bba4SBjoern A. Zeeb 		queue = &mvm->p2p_dev_queue;
2329af1bba4SBjoern A. Zeeb 	} else {
2339af1bba4SBjoern A. Zeeb 		WARN(1, "Missing required TXQ for adding bcast STA\n");
2349af1bba4SBjoern A. Zeeb 		return -EINVAL;
2359af1bba4SBjoern A. Zeeb 	}
2369af1bba4SBjoern A. Zeeb 
2379af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_add_int_sta(mvm, bsta, queue,
2389af1bba4SBjoern A. Zeeb 				       ieee80211_vif_type_p2p(vif),
2399af1bba4SBjoern A. Zeeb 				       STATION_TYPE_BCAST_MGMT,
2409af1bba4SBjoern A. Zeeb 				       mvm_link->fw_link_id, baddr,
2419af1bba4SBjoern A. Zeeb 				       IWL_MAX_TID_COUNT, &wdg_timeout);
2429af1bba4SBjoern A. Zeeb }
2439af1bba4SBjoern A. Zeeb 
244*a4128aadSBjoern A. Zeeb /* Allocate a new station entry for the multicast station to the given vif,
2459af1bba4SBjoern A. Zeeb  * and send it to the FW.
2469af1bba4SBjoern A. Zeeb  * Note that each AP/GO mac should have its own multicast station.
2479af1bba4SBjoern A. Zeeb  */
2489af1bba4SBjoern A. Zeeb int iwl_mvm_mld_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
2499af1bba4SBjoern A. Zeeb 			      struct ieee80211_bss_conf *link_conf)
2509af1bba4SBjoern A. Zeeb {
2519af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2529af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *mvm_link =
2539af1bba4SBjoern A. Zeeb 		mvmvif->link[link_conf->link_id];
2549af1bba4SBjoern A. Zeeb 	struct iwl_mvm_int_sta *msta = &mvm_link->mcast_sta;
2559af1bba4SBjoern A. Zeeb 	static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
2569af1bba4SBjoern A. Zeeb 	const u8 *maddr = _maddr;
2579af1bba4SBjoern A. Zeeb 	unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false);
2589af1bba4SBjoern A. Zeeb 
2599af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
2609af1bba4SBjoern A. Zeeb 
2619af1bba4SBjoern A. Zeeb 	if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
2629af1bba4SBjoern A. Zeeb 		    vif->type != NL80211_IFTYPE_ADHOC))
2639af1bba4SBjoern A. Zeeb 		return -EOPNOTSUPP;
2649af1bba4SBjoern A. Zeeb 
2659af1bba4SBjoern A. Zeeb 	/* In IBSS, ieee80211_check_queues() sets the cab_queue to be
2669af1bba4SBjoern A. Zeeb 	 * invalid, so make sure we use the queue we want.
2679af1bba4SBjoern A. Zeeb 	 * Note that this is done here as we want to avoid making DQA
2689af1bba4SBjoern A. Zeeb 	 * changes in mac80211 layer.
2699af1bba4SBjoern A. Zeeb 	 */
2709af1bba4SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_ADHOC)
2719af1bba4SBjoern A. Zeeb 		mvm_link->cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
2729af1bba4SBjoern A. Zeeb 
2739af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_add_int_sta(mvm, msta, &mvm_link->cab_queue,
2749af1bba4SBjoern A. Zeeb 				       vif->type, STATION_TYPE_MCAST,
2759af1bba4SBjoern A. Zeeb 				       mvm_link->fw_link_id, maddr, 0,
2769af1bba4SBjoern A. Zeeb 				       &timeout);
2779af1bba4SBjoern A. Zeeb }
2789af1bba4SBjoern A. Zeeb 
2799af1bba4SBjoern A. Zeeb /* Allocate a new station entry for the sniffer station to the given vif,
2809af1bba4SBjoern A. Zeeb  * and send it to the FW.
2819af1bba4SBjoern A. Zeeb  */
2829af1bba4SBjoern A. Zeeb int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
2839af1bba4SBjoern A. Zeeb 			     struct ieee80211_bss_conf *link_conf)
2849af1bba4SBjoern A. Zeeb {
2859af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2869af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *mvm_link =
2879af1bba4SBjoern A. Zeeb 		mvmvif->link[link_conf->link_id];
2889af1bba4SBjoern A. Zeeb 
2899af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
2909af1bba4SBjoern A. Zeeb 
2919af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_add_int_sta(mvm, &mvm->snif_sta, &mvm->snif_queue,
2929af1bba4SBjoern A. Zeeb 				       vif->type, STATION_TYPE_BCAST_MGMT,
2939af1bba4SBjoern A. Zeeb 				       mvm_link->fw_link_id, NULL,
2949af1bba4SBjoern A. Zeeb 				       IWL_MAX_TID_COUNT, NULL);
2959af1bba4SBjoern A. Zeeb }
2969af1bba4SBjoern A. Zeeb 
2979af1bba4SBjoern A. Zeeb int iwl_mvm_mld_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id)
2989af1bba4SBjoern A. Zeeb {
2999af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
3009af1bba4SBjoern A. Zeeb 
3019af1bba4SBjoern A. Zeeb 	/* In CDB NICs we need to specify which lmac to use for aux activity;
3029af1bba4SBjoern A. Zeeb 	 * use the link_id argument place to send lmac_id to the function.
3039af1bba4SBjoern A. Zeeb 	 */
3049af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_add_int_sta(mvm, &mvm->aux_sta, &mvm->aux_queue,
3059af1bba4SBjoern A. Zeeb 				       NL80211_IFTYPE_UNSPECIFIED,
3069af1bba4SBjoern A. Zeeb 				       STATION_TYPE_AUX, lmac_id, NULL,
3079af1bba4SBjoern A. Zeeb 				       IWL_MAX_TID_COUNT, NULL);
3089af1bba4SBjoern A. Zeeb }
3099af1bba4SBjoern A. Zeeb 
3109af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_disable_txq(struct iwl_mvm *mvm, u32 sta_mask,
3119af1bba4SBjoern A. Zeeb 				   u16 *queueptr, u8 tid)
3129af1bba4SBjoern A. Zeeb {
3139af1bba4SBjoern A. Zeeb 	int queue = *queueptr;
3149af1bba4SBjoern A. Zeeb 	int ret = 0;
3159af1bba4SBjoern A. Zeeb 
3169af1bba4SBjoern A. Zeeb 	if (tid == IWL_MAX_TID_COUNT)
3179af1bba4SBjoern A. Zeeb 		tid = IWL_MGMT_TID;
3189af1bba4SBjoern A. Zeeb 
3199af1bba4SBjoern A. Zeeb 	if (mvm->sta_remove_requires_queue_remove) {
3209af1bba4SBjoern A. Zeeb 		u32 cmd_id = WIDE_ID(DATA_PATH_GROUP,
3219af1bba4SBjoern A. Zeeb 				     SCD_QUEUE_CONFIG_CMD);
3229af1bba4SBjoern A. Zeeb 		struct iwl_scd_queue_cfg_cmd remove_cmd = {
3239af1bba4SBjoern A. Zeeb 			.operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE),
3249af1bba4SBjoern A. Zeeb 			.u.remove.tid = cpu_to_le32(tid),
3259af1bba4SBjoern A. Zeeb 			.u.remove.sta_mask = cpu_to_le32(sta_mask),
3269af1bba4SBjoern A. Zeeb 		};
3279af1bba4SBjoern A. Zeeb 
3289af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0,
3299af1bba4SBjoern A. Zeeb 					   sizeof(remove_cmd),
3309af1bba4SBjoern A. Zeeb 					   &remove_cmd);
3319af1bba4SBjoern A. Zeeb 	}
3329af1bba4SBjoern A. Zeeb 
3339af1bba4SBjoern A. Zeeb 	iwl_trans_txq_free(mvm->trans, queue);
3349af1bba4SBjoern A. Zeeb 	*queueptr = IWL_MVM_INVALID_QUEUE;
3359af1bba4SBjoern A. Zeeb 
3369af1bba4SBjoern A. Zeeb 	return ret;
3379af1bba4SBjoern A. Zeeb }
3389af1bba4SBjoern A. Zeeb 
3399af1bba4SBjoern A. Zeeb /* Removes a sta from the FW table, disable its queues, and dealloc it
3409af1bba4SBjoern A. Zeeb  */
3419af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_rm_int_sta(struct iwl_mvm *mvm,
3429af1bba4SBjoern A. Zeeb 				  struct iwl_mvm_int_sta *int_sta,
3439af1bba4SBjoern A. Zeeb 				  bool flush, u8 tid, u16 *queuptr)
3449af1bba4SBjoern A. Zeeb {
3459af1bba4SBjoern A. Zeeb 	int ret;
3469af1bba4SBjoern A. Zeeb 
3479af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
3489af1bba4SBjoern A. Zeeb 
3499af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(int_sta->sta_id == IWL_MVM_INVALID_STA))
3509af1bba4SBjoern A. Zeeb 		return -EINVAL;
3519af1bba4SBjoern A. Zeeb 
3529af1bba4SBjoern A. Zeeb 	if (flush)
353*a4128aadSBjoern A. Zeeb 		iwl_mvm_flush_sta(mvm, int_sta->sta_id, int_sta->tfd_queue_msk);
3549af1bba4SBjoern A. Zeeb 
3559af1bba4SBjoern A. Zeeb 	iwl_mvm_mld_disable_txq(mvm, BIT(int_sta->sta_id), queuptr, tid);
3569af1bba4SBjoern A. Zeeb 
3579af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_mld_rm_sta_from_fw(mvm, int_sta->sta_id);
3589af1bba4SBjoern A. Zeeb 	if (ret)
3599af1bba4SBjoern A. Zeeb 		IWL_WARN(mvm, "Failed sending remove station\n");
3609af1bba4SBjoern A. Zeeb 
3619af1bba4SBjoern A. Zeeb 	iwl_mvm_dealloc_int_sta(mvm, int_sta);
3629af1bba4SBjoern A. Zeeb 
3639af1bba4SBjoern A. Zeeb 	return ret;
3649af1bba4SBjoern A. Zeeb }
3659af1bba4SBjoern A. Zeeb 
3669af1bba4SBjoern A. Zeeb int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
3679af1bba4SBjoern A. Zeeb 			     struct ieee80211_bss_conf *link_conf)
3689af1bba4SBjoern A. Zeeb {
3699af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3709af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link = mvmvif->link[link_conf->link_id];
3719af1bba4SBjoern A. Zeeb 	u16 *queueptr;
3729af1bba4SBjoern A. Zeeb 
3739af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
3749af1bba4SBjoern A. Zeeb 
3759af1bba4SBjoern A. Zeeb 	if (WARN_ON(!link))
3769af1bba4SBjoern A. Zeeb 		return -EIO;
3779af1bba4SBjoern A. Zeeb 
3789af1bba4SBjoern A. Zeeb 	switch (vif->type) {
3799af1bba4SBjoern A. Zeeb 	case NL80211_IFTYPE_AP:
3809af1bba4SBjoern A. Zeeb 	case NL80211_IFTYPE_ADHOC:
3819af1bba4SBjoern A. Zeeb 		queueptr = &link->mgmt_queue;
3829af1bba4SBjoern A. Zeeb 		break;
3839af1bba4SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_DEVICE:
3849af1bba4SBjoern A. Zeeb 		queueptr = &mvm->p2p_dev_queue;
3859af1bba4SBjoern A. Zeeb 		break;
3869af1bba4SBjoern A. Zeeb 	default:
3879af1bba4SBjoern A. Zeeb 		WARN(1, "Can't free bcast queue on vif type %d\n",
3889af1bba4SBjoern A. Zeeb 		     vif->type);
3899af1bba4SBjoern A. Zeeb 		return -EINVAL;
3909af1bba4SBjoern A. Zeeb 	}
3919af1bba4SBjoern A. Zeeb 
3929af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_rm_int_sta(mvm, &link->bcast_sta,
3939af1bba4SBjoern A. Zeeb 				      true, IWL_MAX_TID_COUNT, queueptr);
3949af1bba4SBjoern A. Zeeb }
3959af1bba4SBjoern A. Zeeb 
3969af1bba4SBjoern A. Zeeb /* Send the FW a request to remove the station from it's internal data
3979af1bba4SBjoern A. Zeeb  * structures, and in addition remove it from the local data structure.
3989af1bba4SBjoern A. Zeeb  */
3999af1bba4SBjoern A. Zeeb int iwl_mvm_mld_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
4009af1bba4SBjoern A. Zeeb 			     struct ieee80211_bss_conf *link_conf)
4019af1bba4SBjoern A. Zeeb {
4029af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
4039af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link = mvmvif->link[link_conf->link_id];
4049af1bba4SBjoern A. Zeeb 
4059af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
4069af1bba4SBjoern A. Zeeb 
4079af1bba4SBjoern A. Zeeb 	if (WARN_ON(!link))
4089af1bba4SBjoern A. Zeeb 		return -EIO;
4099af1bba4SBjoern A. Zeeb 
4109af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_rm_int_sta(mvm, &link->mcast_sta, true, 0,
4119af1bba4SBjoern A. Zeeb 				      &link->cab_queue);
4129af1bba4SBjoern A. Zeeb }
4139af1bba4SBjoern A. Zeeb 
4149af1bba4SBjoern A. Zeeb int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
4159af1bba4SBjoern A. Zeeb {
4169af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
4179af1bba4SBjoern A. Zeeb 
4189af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_rm_int_sta(mvm, &mvm->snif_sta, false,
4199af1bba4SBjoern A. Zeeb 				      IWL_MAX_TID_COUNT, &mvm->snif_queue);
4209af1bba4SBjoern A. Zeeb }
4219af1bba4SBjoern A. Zeeb 
4229af1bba4SBjoern A. Zeeb int iwl_mvm_mld_rm_aux_sta(struct iwl_mvm *mvm)
4239af1bba4SBjoern A. Zeeb {
4249af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
4259af1bba4SBjoern A. Zeeb 
4269af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_rm_int_sta(mvm, &mvm->aux_sta, false,
4279af1bba4SBjoern A. Zeeb 				      IWL_MAX_TID_COUNT, &mvm->aux_queue);
4289af1bba4SBjoern A. Zeeb }
4299af1bba4SBjoern A. Zeeb 
4309af1bba4SBjoern A. Zeeb /* send a cfg sta command to add/update a sta in firmware */
4319af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
4329af1bba4SBjoern A. Zeeb 			       struct ieee80211_vif *vif,
4339af1bba4SBjoern A. Zeeb 			       struct ieee80211_link_sta *link_sta,
4349af1bba4SBjoern A. Zeeb 			       struct ieee80211_bss_conf *link_conf,
4359af1bba4SBjoern A. Zeeb 			       struct iwl_mvm_link_sta *mvm_link_sta)
4369af1bba4SBjoern A. Zeeb {
4379af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
4389af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
4399af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link_info =
4409af1bba4SBjoern A. Zeeb 					mvm_vif->link[link_conf->link_id];
4419af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta_cfg_cmd cmd = {
4429af1bba4SBjoern A. Zeeb 		.sta_id = cpu_to_le32(mvm_link_sta->sta_id),
4439af1bba4SBjoern A. Zeeb 		.station_type = cpu_to_le32(mvm_sta->sta_type),
4449af1bba4SBjoern A. Zeeb 	};
4459af1bba4SBjoern A. Zeeb 	u32 agg_size = 0, mpdu_dens = 0;
4469af1bba4SBjoern A. Zeeb 
4479af1bba4SBjoern A. Zeeb 	/* when adding sta, link should exist in FW */
4489af1bba4SBjoern A. Zeeb 	if (WARN_ON(link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
4499af1bba4SBjoern A. Zeeb 		return -EINVAL;
4509af1bba4SBjoern A. Zeeb 
4519af1bba4SBjoern A. Zeeb 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
4529af1bba4SBjoern A. Zeeb 
4539af1bba4SBjoern A. Zeeb 	memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
4549af1bba4SBjoern A. Zeeb 	memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN);
4559af1bba4SBjoern A. Zeeb 
4569af1bba4SBjoern A. Zeeb 	if (mvm_sta->sta_state >= IEEE80211_STA_ASSOC)
4579af1bba4SBjoern A. Zeeb 		cmd.assoc_id = cpu_to_le32(sta->aid);
4589af1bba4SBjoern A. Zeeb 
4599af1bba4SBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa,
4609af1bba4SBjoern A. Zeeb 			IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT) &&
4619af1bba4SBjoern A. Zeeb 	    (sta->mfp || mvm_sta->sta_state < IEEE80211_STA_AUTHORIZED))
4629af1bba4SBjoern A. Zeeb 		cmd.mfp = cpu_to_le32(1);
4639af1bba4SBjoern A. Zeeb 
4649af1bba4SBjoern A. Zeeb 	switch (link_sta->rx_nss) {
4659af1bba4SBjoern A. Zeeb 	case 1:
4669af1bba4SBjoern A. Zeeb 		cmd.mimo = cpu_to_le32(0);
4679af1bba4SBjoern A. Zeeb 		break;
4689af1bba4SBjoern A. Zeeb 	case 2 ... 8:
4699af1bba4SBjoern A. Zeeb 		cmd.mimo = cpu_to_le32(1);
4709af1bba4SBjoern A. Zeeb 		break;
4719af1bba4SBjoern A. Zeeb 	}
4729af1bba4SBjoern A. Zeeb 
473*a4128aadSBjoern A. Zeeb 	switch (link_sta->smps_mode) {
4749af1bba4SBjoern A. Zeeb 	case IEEE80211_SMPS_AUTOMATIC:
4759af1bba4SBjoern A. Zeeb 	case IEEE80211_SMPS_NUM_MODES:
4769af1bba4SBjoern A. Zeeb 		WARN_ON(1);
4779af1bba4SBjoern A. Zeeb 		break;
4789af1bba4SBjoern A. Zeeb 	case IEEE80211_SMPS_STATIC:
4799af1bba4SBjoern A. Zeeb 		/* override NSS */
4809af1bba4SBjoern A. Zeeb 		cmd.mimo = cpu_to_le32(0);
4819af1bba4SBjoern A. Zeeb 		break;
4829af1bba4SBjoern A. Zeeb 	case IEEE80211_SMPS_DYNAMIC:
4839af1bba4SBjoern A. Zeeb 		cmd.mimo_protection = cpu_to_le32(1);
4849af1bba4SBjoern A. Zeeb 		break;
4859af1bba4SBjoern A. Zeeb 	case IEEE80211_SMPS_OFF:
4869af1bba4SBjoern A. Zeeb 		/* nothing */
4879af1bba4SBjoern A. Zeeb 		break;
4889af1bba4SBjoern A. Zeeb 	}
4899af1bba4SBjoern A. Zeeb 
4909af1bba4SBjoern A. Zeeb 	mpdu_dens = iwl_mvm_get_sta_ampdu_dens(link_sta, link_conf, &agg_size);
4919af1bba4SBjoern A. Zeeb 	cmd.tx_ampdu_spacing = cpu_to_le32(mpdu_dens);
4929af1bba4SBjoern A. Zeeb 	cmd.tx_ampdu_max_size = cpu_to_le32(agg_size);
4939af1bba4SBjoern A. Zeeb 
4949af1bba4SBjoern A. Zeeb 	if (sta->wme) {
4959af1bba4SBjoern A. Zeeb 		cmd.sp_length =
4969af1bba4SBjoern A. Zeeb 			cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128);
4979af1bba4SBjoern A. Zeeb 		cmd.uapsd_acs = cpu_to_le32(iwl_mvm_get_sta_uapsd_acs(sta));
4989af1bba4SBjoern A. Zeeb 	}
4999af1bba4SBjoern A. Zeeb 
5009af1bba4SBjoern A. Zeeb 	if (link_sta->he_cap.has_he) {
5019af1bba4SBjoern A. Zeeb 		cmd.trig_rnd_alloc =
5029af1bba4SBjoern A. Zeeb 			cpu_to_le32(link_conf->uora_exists ? 1 : 0);
5039af1bba4SBjoern A. Zeeb 
5049af1bba4SBjoern A. Zeeb 		/* PPE Thresholds */
5059af1bba4SBjoern A. Zeeb 		iwl_mvm_set_sta_pkt_ext(mvm, link_sta, &cmd.pkt_ext);
5069af1bba4SBjoern A. Zeeb 
5079af1bba4SBjoern A. Zeeb 		/* HTC flags */
5089af1bba4SBjoern A. Zeeb 		cmd.htc_flags = iwl_mvm_get_sta_htc_flags(sta, link_sta);
5099af1bba4SBjoern A. Zeeb 
5109af1bba4SBjoern A. Zeeb 		if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] &
5119af1bba4SBjoern A. Zeeb 		    IEEE80211_HE_MAC_CAP2_ACK_EN)
5129af1bba4SBjoern A. Zeeb 			cmd.ack_enabled = cpu_to_le32(1);
5139af1bba4SBjoern A. Zeeb 	}
5149af1bba4SBjoern A. Zeeb 
5159af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_send_sta_cmd(mvm, &cmd);
5169af1bba4SBjoern A. Zeeb }
5179af1bba4SBjoern A. Zeeb 
518*a4128aadSBjoern A. Zeeb void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm,
5199af1bba4SBjoern A. Zeeb 			       struct iwl_mvm_sta *mvm_sta,
5209af1bba4SBjoern A. Zeeb 			       struct iwl_mvm_link_sta *mvm_sta_link,
5219af1bba4SBjoern A. Zeeb 			       unsigned int link_id,
5229af1bba4SBjoern A. Zeeb 			       bool is_in_fw)
5239af1bba4SBjoern A. Zeeb {
5249af1bba4SBjoern A. Zeeb 	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id],
5259af1bba4SBjoern A. Zeeb 			 is_in_fw ? ERR_PTR(-EINVAL) : NULL);
5269af1bba4SBjoern A. Zeeb 	RCU_INIT_POINTER(mvm->fw_id_to_link_sta[mvm_sta_link->sta_id], NULL);
5279af1bba4SBjoern A. Zeeb 	RCU_INIT_POINTER(mvm_sta->link[link_id], NULL);
5289af1bba4SBjoern A. Zeeb 
5299af1bba4SBjoern A. Zeeb 	if (mvm_sta_link != &mvm_sta->deflink)
5309af1bba4SBjoern A. Zeeb 		kfree_rcu(mvm_sta_link, rcu_head);
5319af1bba4SBjoern A. Zeeb }
5329af1bba4SBjoern A. Zeeb 
5339af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_sta_rm_all_sta_links(struct iwl_mvm *mvm,
5349af1bba4SBjoern A. Zeeb 					     struct iwl_mvm_sta *mvm_sta)
5359af1bba4SBjoern A. Zeeb {
5369af1bba4SBjoern A. Zeeb 	unsigned int link_id;
5379af1bba4SBjoern A. Zeeb 
5389af1bba4SBjoern A. Zeeb 	for (link_id = 0; link_id < ARRAY_SIZE(mvm_sta->link); link_id++) {
5399af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *link =
5409af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
5419af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
5429af1bba4SBjoern A. Zeeb 
5439af1bba4SBjoern A. Zeeb 		if (!link)
5449af1bba4SBjoern A. Zeeb 			continue;
5459af1bba4SBjoern A. Zeeb 
5469af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, link, link_id, false);
5479af1bba4SBjoern A. Zeeb 	}
5489af1bba4SBjoern A. Zeeb }
5499af1bba4SBjoern A. Zeeb 
5509af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_alloc_sta_link(struct iwl_mvm *mvm,
5519af1bba4SBjoern A. Zeeb 				      struct ieee80211_vif *vif,
5529af1bba4SBjoern A. Zeeb 				      struct ieee80211_sta *sta,
5539af1bba4SBjoern A. Zeeb 				      unsigned int link_id)
5549af1bba4SBjoern A. Zeeb {
5559af1bba4SBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta =
5569af1bba4SBjoern A. Zeeb 		link_sta_dereference_protected(sta, link_id);
5579af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
5589af1bba4SBjoern A. Zeeb 	struct iwl_mvm_link_sta *link;
5599af1bba4SBjoern A. Zeeb 	u32 sta_id = iwl_mvm_find_free_sta_id(mvm,
5609af1bba4SBjoern A. Zeeb 					  ieee80211_vif_type_p2p(vif));
5619af1bba4SBjoern A. Zeeb 
5629af1bba4SBjoern A. Zeeb 	if (sta_id == IWL_MVM_INVALID_STA)
5639af1bba4SBjoern A. Zeeb 		return -ENOSPC;
5649af1bba4SBjoern A. Zeeb 
5659af1bba4SBjoern A. Zeeb 	if (rcu_access_pointer(sta->link[link_id]) == &sta->deflink) {
5669af1bba4SBjoern A. Zeeb 		link = &mvm_sta->deflink;
5679af1bba4SBjoern A. Zeeb 	} else {
5689af1bba4SBjoern A. Zeeb 		link = kzalloc(sizeof(*link), GFP_KERNEL);
5699af1bba4SBjoern A. Zeeb 		if (!link)
5709af1bba4SBjoern A. Zeeb 			return -ENOMEM;
5719af1bba4SBjoern A. Zeeb 	}
5729af1bba4SBjoern A. Zeeb 
5739af1bba4SBjoern A. Zeeb 	link->sta_id = sta_id;
5749af1bba4SBjoern A. Zeeb 	rcu_assign_pointer(mvm_sta->link[link_id], link);
5759af1bba4SBjoern A. Zeeb 	rcu_assign_pointer(mvm->fw_id_to_mac_id[link->sta_id], sta);
5769af1bba4SBjoern A. Zeeb 	rcu_assign_pointer(mvm->fw_id_to_link_sta[link->sta_id],
5779af1bba4SBjoern A. Zeeb 			   link_sta);
5789af1bba4SBjoern A. Zeeb 
5799af1bba4SBjoern A. Zeeb 	return 0;
5809af1bba4SBjoern A. Zeeb }
5819af1bba4SBjoern A. Zeeb 
5829af1bba4SBjoern A. Zeeb /* allocate all the links of a sta, called when the station is first added */
5839af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_alloc_sta_links(struct iwl_mvm *mvm,
5849af1bba4SBjoern A. Zeeb 				       struct ieee80211_vif *vif,
5859af1bba4SBjoern A. Zeeb 				       struct ieee80211_sta *sta)
5869af1bba4SBjoern A. Zeeb {
5879af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
588*a4128aadSBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
5899af1bba4SBjoern A. Zeeb 	unsigned int link_id;
5909af1bba4SBjoern A. Zeeb 	int ret;
5919af1bba4SBjoern A. Zeeb 
5929af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
5939af1bba4SBjoern A. Zeeb 
594*a4128aadSBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
595*a4128aadSBjoern A. Zeeb 		if (WARN_ON(mvm_sta->link[link_id]))
5969af1bba4SBjoern A. Zeeb 			continue;
5979af1bba4SBjoern A. Zeeb 
5989af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta, link_id);
5999af1bba4SBjoern A. Zeeb 		if (ret)
6009af1bba4SBjoern A. Zeeb 			goto err;
6019af1bba4SBjoern A. Zeeb 	}
6029af1bba4SBjoern A. Zeeb 
6039af1bba4SBjoern A. Zeeb 	return 0;
6049af1bba4SBjoern A. Zeeb 
6059af1bba4SBjoern A. Zeeb err:
6069af1bba4SBjoern A. Zeeb 	iwl_mvm_mld_sta_rm_all_sta_links(mvm, mvm_sta);
6079af1bba4SBjoern A. Zeeb 	return ret;
6089af1bba4SBjoern A. Zeeb }
6099af1bba4SBjoern A. Zeeb 
6109af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_set_ap_sta_id(struct ieee80211_sta *sta,
6119af1bba4SBjoern A. Zeeb 				      struct iwl_mvm_vif_link_info *vif_link,
6129af1bba4SBjoern A. Zeeb 				      struct iwl_mvm_link_sta *sta_link)
6139af1bba4SBjoern A. Zeeb {
6149af1bba4SBjoern A. Zeeb 	if (!sta->tdls) {
6159af1bba4SBjoern A. Zeeb 		WARN_ON(vif_link->ap_sta_id != IWL_MVM_INVALID_STA);
6169af1bba4SBjoern A. Zeeb 		vif_link->ap_sta_id = sta_link->sta_id;
6179af1bba4SBjoern A. Zeeb 	} else {
6189af1bba4SBjoern A. Zeeb 		WARN_ON(vif_link->ap_sta_id == IWL_MVM_INVALID_STA);
6199af1bba4SBjoern A. Zeeb 	}
6209af1bba4SBjoern A. Zeeb }
6219af1bba4SBjoern A. Zeeb 
6229af1bba4SBjoern A. Zeeb static int iwl_mvm_alloc_sta_after_restart(struct iwl_mvm *mvm,
6239af1bba4SBjoern A. Zeeb 					   struct ieee80211_vif *vif,
6249af1bba4SBjoern A. Zeeb 					   struct ieee80211_sta *sta)
6259af1bba4SBjoern A. Zeeb {
6269af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
6279af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
6289af1bba4SBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
6299af1bba4SBjoern A. Zeeb 	unsigned int link_id;
6309af1bba4SBjoern A. Zeeb 	/* no active link found */
6319af1bba4SBjoern A. Zeeb 	int ret = -EINVAL;
6329af1bba4SBjoern A. Zeeb 	int sta_id;
6339af1bba4SBjoern A. Zeeb 
6349af1bba4SBjoern A. Zeeb 	/* First add an empty station since allocating a queue requires
6359af1bba4SBjoern A. Zeeb 	 * a valid station. Since we need a link_id to allocate a station,
6369af1bba4SBjoern A. Zeeb 	 * pick up the first valid one.
6379af1bba4SBjoern A. Zeeb 	 */
6389af1bba4SBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
6399af1bba4SBjoern A. Zeeb 		struct iwl_mvm_vif_link_info *mvm_link;
6409af1bba4SBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf =
6419af1bba4SBjoern A. Zeeb 			link_conf_dereference_protected(vif, link_id);
6429af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta =
6439af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
6449af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
6459af1bba4SBjoern A. Zeeb 
6469af1bba4SBjoern A. Zeeb 		if (!link_conf)
6479af1bba4SBjoern A. Zeeb 			continue;
6489af1bba4SBjoern A. Zeeb 
6499af1bba4SBjoern A. Zeeb 		mvm_link = mvmvif->link[link_conf->link_id];
6509af1bba4SBjoern A. Zeeb 
6519af1bba4SBjoern A. Zeeb 		if (!mvm_link || !mvm_link_sta)
6529af1bba4SBjoern A. Zeeb 			continue;
6539af1bba4SBjoern A. Zeeb 
6549af1bba4SBjoern A. Zeeb 		sta_id = mvm_link_sta->sta_id;
6559af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta,
6569af1bba4SBjoern A. Zeeb 					  link_conf, mvm_link_sta);
6579af1bba4SBjoern A. Zeeb 		if (ret)
6589af1bba4SBjoern A. Zeeb 			return ret;
6599af1bba4SBjoern A. Zeeb 
6609af1bba4SBjoern A. Zeeb 		rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
6619af1bba4SBjoern A. Zeeb 		rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id], link_sta);
6629af1bba4SBjoern A. Zeeb 		ret = 0;
6639af1bba4SBjoern A. Zeeb 	}
6649af1bba4SBjoern A. Zeeb 
6659af1bba4SBjoern A. Zeeb 	iwl_mvm_realloc_queues_after_restart(mvm, sta);
6669af1bba4SBjoern A. Zeeb 
6679af1bba4SBjoern A. Zeeb 	return ret;
6689af1bba4SBjoern A. Zeeb }
6699af1bba4SBjoern A. Zeeb 
6709af1bba4SBjoern A. Zeeb int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
6719af1bba4SBjoern A. Zeeb 			struct ieee80211_sta *sta)
6729af1bba4SBjoern A. Zeeb {
6739af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
6749af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
6759af1bba4SBjoern A. Zeeb 	unsigned long link_sta_added_to_fw = 0;
6769af1bba4SBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
6779af1bba4SBjoern A. Zeeb 	int ret = 0;
6789af1bba4SBjoern A. Zeeb 	unsigned int link_id;
6799af1bba4SBjoern A. Zeeb 
6809af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
6819af1bba4SBjoern A. Zeeb 
6829af1bba4SBjoern A. Zeeb 	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
6839af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_alloc_sta_links(mvm, vif, sta);
6849af1bba4SBjoern A. Zeeb 		if (ret)
6859af1bba4SBjoern A. Zeeb 			return ret;
6869af1bba4SBjoern A. Zeeb 
6879af1bba4SBjoern A. Zeeb 		spin_lock_init(&mvm_sta->lock);
6889af1bba4SBjoern A. Zeeb 
6899af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_sta_init(mvm, vif, sta, IWL_MVM_INVALID_STA,
6909af1bba4SBjoern A. Zeeb 				       STATION_TYPE_PEER);
6919af1bba4SBjoern A. Zeeb 	} else {
6929af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_alloc_sta_after_restart(mvm, vif, sta);
6939af1bba4SBjoern A. Zeeb 	}
6949af1bba4SBjoern A. Zeeb 
6959af1bba4SBjoern A. Zeeb 	if (ret)
6969af1bba4SBjoern A. Zeeb 		goto err;
6979af1bba4SBjoern A. Zeeb 
6989af1bba4SBjoern A. Zeeb 	/* at this stage sta link pointers are already allocated */
6999af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_mld_update_sta(mvm, vif, sta);
700*a4128aadSBjoern A. Zeeb 	if (ret)
701*a4128aadSBjoern A. Zeeb 		goto err;
7029af1bba4SBjoern A. Zeeb 
7039af1bba4SBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
7049af1bba4SBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf =
7059af1bba4SBjoern A. Zeeb 			link_conf_dereference_protected(vif, link_id);
7069af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta =
7079af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
7089af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
7099af1bba4SBjoern A. Zeeb 
710*a4128aadSBjoern A. Zeeb 		if (WARN_ON(!link_conf || !mvm_link_sta)) {
711*a4128aadSBjoern A. Zeeb 			ret = -EINVAL;
7129af1bba4SBjoern A. Zeeb 			goto err;
713*a4128aadSBjoern A. Zeeb 		}
7149af1bba4SBjoern A. Zeeb 
7159af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
7169af1bba4SBjoern A. Zeeb 					  mvm_link_sta);
7179af1bba4SBjoern A. Zeeb 		if (ret)
7189af1bba4SBjoern A. Zeeb 			goto err;
7199af1bba4SBjoern A. Zeeb 
7209af1bba4SBjoern A. Zeeb 		link_sta_added_to_fw |= BIT(link_id);
7219af1bba4SBjoern A. Zeeb 
7229af1bba4SBjoern A. Zeeb 		if (vif->type == NL80211_IFTYPE_STATION)
7239af1bba4SBjoern A. Zeeb 			iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif->link[link_id],
7249af1bba4SBjoern A. Zeeb 						  mvm_link_sta);
7259af1bba4SBjoern A. Zeeb 	}
7269af1bba4SBjoern A. Zeeb 	return 0;
7279af1bba4SBjoern A. Zeeb 
7289af1bba4SBjoern A. Zeeb err:
7299af1bba4SBjoern A. Zeeb 	/* remove all already allocated stations in FW */
7309af1bba4SBjoern A. Zeeb 	for_each_set_bit(link_id, &link_sta_added_to_fw,
7319af1bba4SBjoern A. Zeeb 			 IEEE80211_MLD_MAX_NUM_LINKS) {
7329af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta =
7339af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
7349af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
7359af1bba4SBjoern A. Zeeb 
7369af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_link_sta->sta_id);
7379af1bba4SBjoern A. Zeeb 	}
7389af1bba4SBjoern A. Zeeb 
7399af1bba4SBjoern A. Zeeb 	/* free all sta resources in the driver */
7409af1bba4SBjoern A. Zeeb 	iwl_mvm_mld_sta_rm_all_sta_links(mvm, mvm_sta);
7419af1bba4SBjoern A. Zeeb 	return ret;
7429af1bba4SBjoern A. Zeeb }
7439af1bba4SBjoern A. Zeeb 
7449af1bba4SBjoern A. Zeeb int iwl_mvm_mld_update_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
7459af1bba4SBjoern A. Zeeb 			   struct ieee80211_sta *sta)
7469af1bba4SBjoern A. Zeeb {
7479af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
7489af1bba4SBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
7499af1bba4SBjoern A. Zeeb 	unsigned int link_id;
7509af1bba4SBjoern A. Zeeb 	int ret = -EINVAL;
7519af1bba4SBjoern A. Zeeb 
7529af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
7539af1bba4SBjoern A. Zeeb 
7549af1bba4SBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
7559af1bba4SBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf =
7569af1bba4SBjoern A. Zeeb 			link_conf_dereference_protected(vif, link_id);
7579af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta =
7589af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
7599af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
7609af1bba4SBjoern A. Zeeb 
7619af1bba4SBjoern A. Zeeb 		if (WARN_ON(!link_conf || !mvm_link_sta))
7629af1bba4SBjoern A. Zeeb 			return -EINVAL;
7639af1bba4SBjoern A. Zeeb 
7649af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
7659af1bba4SBjoern A. Zeeb 					  mvm_link_sta);
7669af1bba4SBjoern A. Zeeb 
7679af1bba4SBjoern A. Zeeb 		if (ret) {
7689af1bba4SBjoern A. Zeeb 			IWL_ERR(mvm, "Failed to update sta link %d\n", link_id);
7699af1bba4SBjoern A. Zeeb 			break;
7709af1bba4SBjoern A. Zeeb 		}
7719af1bba4SBjoern A. Zeeb 	}
7729af1bba4SBjoern A. Zeeb 
7739af1bba4SBjoern A. Zeeb 	return ret;
7749af1bba4SBjoern A. Zeeb }
7759af1bba4SBjoern A. Zeeb 
7769af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_disable_sta_queues(struct iwl_mvm *mvm,
7779af1bba4SBjoern A. Zeeb 					   struct ieee80211_vif *vif,
7789af1bba4SBjoern A. Zeeb 					   struct ieee80211_sta *sta)
7799af1bba4SBjoern A. Zeeb {
7809af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
7819af1bba4SBjoern A. Zeeb 	u32 sta_mask = iwl_mvm_sta_fw_id_mask(mvm, sta, -1);
7829af1bba4SBjoern A. Zeeb 	int i;
7839af1bba4SBjoern A. Zeeb 
7849af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
7859af1bba4SBjoern A. Zeeb 
7869af1bba4SBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) {
7879af1bba4SBjoern A. Zeeb 		if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE)
7889af1bba4SBjoern A. Zeeb 			continue;
7899af1bba4SBjoern A. Zeeb 
7909af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_disable_txq(mvm, sta_mask,
7919af1bba4SBjoern A. Zeeb 					&mvm_sta->tid_data[i].txq_id, i);
7929af1bba4SBjoern A. Zeeb 		mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE;
7939af1bba4SBjoern A. Zeeb 	}
7949af1bba4SBjoern A. Zeeb 
7959af1bba4SBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
7969af1bba4SBjoern A. Zeeb 		struct iwl_mvm_txq *mvmtxq =
7979af1bba4SBjoern A. Zeeb 			iwl_mvm_txq_from_mac80211(sta->txq[i]);
7989af1bba4SBjoern A. Zeeb 
7999af1bba4SBjoern A. Zeeb 		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
8009af1bba4SBjoern A. Zeeb 	}
8019af1bba4SBjoern A. Zeeb }
8029af1bba4SBjoern A. Zeeb 
8039af1bba4SBjoern A. Zeeb int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
8049af1bba4SBjoern A. Zeeb 		       struct ieee80211_sta *sta)
8059af1bba4SBjoern A. Zeeb {
8069af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
8079af1bba4SBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
8089af1bba4SBjoern A. Zeeb 	unsigned int link_id;
8099af1bba4SBjoern A. Zeeb 	int ret;
8109af1bba4SBjoern A. Zeeb 
8119af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
8129af1bba4SBjoern A. Zeeb 
8139af1bba4SBjoern A. Zeeb 	/* flush its queues here since we are freeing mvm_sta */
8149af1bba4SBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
8159af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta =
8169af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
8179af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
8189af1bba4SBjoern A. Zeeb 
8199af1bba4SBjoern A. Zeeb 		if (WARN_ON(!mvm_link_sta))
8209af1bba4SBjoern A. Zeeb 			return -EINVAL;
8219af1bba4SBjoern A. Zeeb 
8229af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_flush_sta_tids(mvm, mvm_link_sta->sta_id,
8239af1bba4SBjoern A. Zeeb 					     0xffff);
8249af1bba4SBjoern A. Zeeb 		if (ret)
8259af1bba4SBjoern A. Zeeb 			return ret;
8269af1bba4SBjoern A. Zeeb 	}
8279af1bba4SBjoern A. Zeeb 
8289af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta);
8299af1bba4SBjoern A. Zeeb 	if (ret)
8309af1bba4SBjoern A. Zeeb 		return ret;
8319af1bba4SBjoern A. Zeeb 
8329af1bba4SBjoern A. Zeeb 	iwl_mvm_mld_disable_sta_queues(mvm, vif, sta);
8339af1bba4SBjoern A. Zeeb 
8349af1bba4SBjoern A. Zeeb 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
8359af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *mvm_link_sta =
8369af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
8379af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
8389af1bba4SBjoern A. Zeeb 		bool stay_in_fw;
8399af1bba4SBjoern A. Zeeb 
8409af1bba4SBjoern A. Zeeb 		stay_in_fw = iwl_mvm_sta_del(mvm, vif, sta, link_sta, &ret);
8419af1bba4SBjoern A. Zeeb 		if (ret)
8429af1bba4SBjoern A. Zeeb 			break;
8439af1bba4SBjoern A. Zeeb 
8449af1bba4SBjoern A. Zeeb 		if (!stay_in_fw)
8459af1bba4SBjoern A. Zeeb 			ret = iwl_mvm_mld_rm_sta_from_fw(mvm,
8469af1bba4SBjoern A. Zeeb 							 mvm_link_sta->sta_id);
8479af1bba4SBjoern A. Zeeb 
8489af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta,
8499af1bba4SBjoern A. Zeeb 					  link_id, stay_in_fw);
8509af1bba4SBjoern A. Zeeb 	}
851*a4128aadSBjoern A. Zeeb 	kfree(mvm_sta->mpdu_counters);
852*a4128aadSBjoern A. Zeeb 	mvm_sta->mpdu_counters = NULL;
8539af1bba4SBjoern A. Zeeb 
8549af1bba4SBjoern A. Zeeb 	return ret;
8559af1bba4SBjoern A. Zeeb }
8569af1bba4SBjoern A. Zeeb 
8579af1bba4SBjoern A. Zeeb int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id)
8589af1bba4SBjoern A. Zeeb {
859*a4128aadSBjoern A. Zeeb 	int ret;
8609af1bba4SBjoern A. Zeeb 
8619af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
8629af1bba4SBjoern A. Zeeb 
863*a4128aadSBjoern A. Zeeb 	if (WARN_ON(sta_id == IWL_MVM_INVALID_STA))
864*a4128aadSBjoern A. Zeeb 		return 0;
865*a4128aadSBjoern A. Zeeb 
866*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_mld_rm_sta_from_fw(mvm, sta_id);
867*a4128aadSBjoern A. Zeeb 
8689af1bba4SBjoern A. Zeeb 	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
8699af1bba4SBjoern A. Zeeb 	RCU_INIT_POINTER(mvm->fw_id_to_link_sta[sta_id], NULL);
8709af1bba4SBjoern A. Zeeb 	return ret;
8719af1bba4SBjoern A. Zeeb }
8729af1bba4SBjoern A. Zeeb 
8739af1bba4SBjoern A. Zeeb void iwl_mvm_mld_sta_modify_disable_tx(struct iwl_mvm *mvm,
8749af1bba4SBjoern A. Zeeb 				       struct iwl_mvm_sta *mvmsta,
8759af1bba4SBjoern A. Zeeb 				       bool disable)
8769af1bba4SBjoern A. Zeeb {
8779af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta_disable_tx_cmd cmd;
8789af1bba4SBjoern A. Zeeb 	int ret;
8799af1bba4SBjoern A. Zeeb 
8809af1bba4SBjoern A. Zeeb 	cmd.sta_id = cpu_to_le32(mvmsta->deflink.sta_id);
8819af1bba4SBjoern A. Zeeb 	cmd.disable = cpu_to_le32(disable);
8829af1bba4SBjoern A. Zeeb 
883*a4128aadSBjoern A. Zeeb 	if (WARN_ON(iwl_mvm_has_no_host_disable_tx(mvm)))
884*a4128aadSBjoern A. Zeeb 		return;
885*a4128aadSBjoern A. Zeeb 
8869af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm,
8879af1bba4SBjoern A. Zeeb 				   WIDE_ID(MAC_CONF_GROUP, STA_DISABLE_TX_CMD),
8889af1bba4SBjoern A. Zeeb 				   CMD_ASYNC, sizeof(cmd), &cmd);
8899af1bba4SBjoern A. Zeeb 	if (ret)
8909af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm,
8919af1bba4SBjoern A. Zeeb 			"Failed to send STA_DISABLE_TX_CMD command (%d)\n",
8929af1bba4SBjoern A. Zeeb 			ret);
8939af1bba4SBjoern A. Zeeb }
8949af1bba4SBjoern A. Zeeb 
8959af1bba4SBjoern A. Zeeb void iwl_mvm_mld_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
8969af1bba4SBjoern A. Zeeb 					  struct ieee80211_sta *sta,
8979af1bba4SBjoern A. Zeeb 					  bool disable)
8989af1bba4SBjoern A. Zeeb {
8999af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
9009af1bba4SBjoern A. Zeeb 
9019af1bba4SBjoern A. Zeeb 	spin_lock_bh(&mvm_sta->lock);
9029af1bba4SBjoern A. Zeeb 
9039af1bba4SBjoern A. Zeeb 	if (mvm_sta->disable_tx == disable) {
9049af1bba4SBjoern A. Zeeb 		spin_unlock_bh(&mvm_sta->lock);
9059af1bba4SBjoern A. Zeeb 		return;
9069af1bba4SBjoern A. Zeeb 	}
9079af1bba4SBjoern A. Zeeb 
9089af1bba4SBjoern A. Zeeb 	iwl_mvm_mld_sta_modify_disable_tx(mvm, mvm_sta, disable);
9099af1bba4SBjoern A. Zeeb 
9109af1bba4SBjoern A. Zeeb 	spin_unlock_bh(&mvm_sta->lock);
9119af1bba4SBjoern A. Zeeb }
9129af1bba4SBjoern A. Zeeb 
9139af1bba4SBjoern A. Zeeb void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
9149af1bba4SBjoern A. Zeeb 					   struct iwl_mvm_vif *mvmvif,
9159af1bba4SBjoern A. Zeeb 					   bool disable)
9169af1bba4SBjoern A. Zeeb {
9179af1bba4SBjoern A. Zeeb 	struct ieee80211_sta *sta;
9189af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta;
9199af1bba4SBjoern A. Zeeb 	int i;
9209af1bba4SBjoern A. Zeeb 
9219af1bba4SBjoern A. Zeeb 	rcu_read_lock();
9229af1bba4SBjoern A. Zeeb 
9239af1bba4SBjoern A. Zeeb 	/* Block/unblock all the stations of the given mvmvif */
9249af1bba4SBjoern A. Zeeb 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
9259af1bba4SBjoern A. Zeeb 		sta = rcu_dereference(mvm->fw_id_to_mac_id[i]);
9269af1bba4SBjoern A. Zeeb 		if (IS_ERR_OR_NULL(sta))
9279af1bba4SBjoern A. Zeeb 			continue;
9289af1bba4SBjoern A. Zeeb 
9299af1bba4SBjoern A. Zeeb 		mvm_sta = iwl_mvm_sta_from_mac80211(sta);
9309af1bba4SBjoern A. Zeeb 		if (mvm_sta->mac_id_n_color !=
9319af1bba4SBjoern A. Zeeb 		    FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
9329af1bba4SBjoern A. Zeeb 			continue;
9339af1bba4SBjoern A. Zeeb 
9349af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_sta_modify_disable_tx(mvm, mvm_sta, disable);
9359af1bba4SBjoern A. Zeeb 	}
9369af1bba4SBjoern A. Zeeb 
9379af1bba4SBjoern A. Zeeb 	rcu_read_unlock();
9389af1bba4SBjoern A. Zeeb }
9399af1bba4SBjoern A. Zeeb 
9409af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_update_sta_queues(struct iwl_mvm *mvm,
9419af1bba4SBjoern A. Zeeb 					 struct ieee80211_sta *sta,
9429af1bba4SBjoern A. Zeeb 					 u32 old_sta_mask,
9439af1bba4SBjoern A. Zeeb 					 u32 new_sta_mask)
9449af1bba4SBjoern A. Zeeb {
9459af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
9469af1bba4SBjoern A. Zeeb 	struct iwl_scd_queue_cfg_cmd cmd = {
9479af1bba4SBjoern A. Zeeb 		.operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY),
9489af1bba4SBjoern A. Zeeb 		.u.modify.old_sta_mask = cpu_to_le32(old_sta_mask),
9499af1bba4SBjoern A. Zeeb 		.u.modify.new_sta_mask = cpu_to_le32(new_sta_mask),
9509af1bba4SBjoern A. Zeeb 	};
9519af1bba4SBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
9529af1bba4SBjoern A. Zeeb 		.id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD),
9539af1bba4SBjoern A. Zeeb 		.len[0] = sizeof(cmd),
9549af1bba4SBjoern A. Zeeb 		.data[0] = &cmd
9559af1bba4SBjoern A. Zeeb 	};
9569af1bba4SBjoern A. Zeeb 	int tid;
9579af1bba4SBjoern A. Zeeb 	int ret;
9589af1bba4SBjoern A. Zeeb 
9599af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
9609af1bba4SBjoern A. Zeeb 
9619af1bba4SBjoern A. Zeeb 	for (tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) {
9629af1bba4SBjoern A. Zeeb 		struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[tid];
9639af1bba4SBjoern A. Zeeb 		int txq_id = tid_data->txq_id;
9649af1bba4SBjoern A. Zeeb 
9659af1bba4SBjoern A. Zeeb 		if (txq_id == IWL_MVM_INVALID_QUEUE)
9669af1bba4SBjoern A. Zeeb 			continue;
9679af1bba4SBjoern A. Zeeb 
9689af1bba4SBjoern A. Zeeb 		if (tid == IWL_MAX_TID_COUNT)
9699af1bba4SBjoern A. Zeeb 			cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID);
9709af1bba4SBjoern A. Zeeb 		else
9719af1bba4SBjoern A. Zeeb 			cmd.u.modify.tid = cpu_to_le32(tid);
9729af1bba4SBjoern A. Zeeb 
9739af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_send_cmd(mvm, &hcmd);
9749af1bba4SBjoern A. Zeeb 		if (ret)
9759af1bba4SBjoern A. Zeeb 			return ret;
9769af1bba4SBjoern A. Zeeb 	}
9779af1bba4SBjoern A. Zeeb 
9789af1bba4SBjoern A. Zeeb 	return 0;
9799af1bba4SBjoern A. Zeeb }
9809af1bba4SBjoern A. Zeeb 
9819af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm,
9829af1bba4SBjoern A. Zeeb 					u32 old_sta_mask,
9839af1bba4SBjoern A. Zeeb 					u32 new_sta_mask)
9849af1bba4SBjoern A. Zeeb {
9859af1bba4SBjoern A. Zeeb 	struct iwl_rx_baid_cfg_cmd cmd = {
9869af1bba4SBjoern A. Zeeb 		.action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY),
9879af1bba4SBjoern A. Zeeb 		.modify.old_sta_id_mask = cpu_to_le32(old_sta_mask),
9889af1bba4SBjoern A. Zeeb 		.modify.new_sta_id_mask = cpu_to_le32(new_sta_mask),
9899af1bba4SBjoern A. Zeeb 	};
9909af1bba4SBjoern A. Zeeb 	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD);
9919af1bba4SBjoern A. Zeeb 	int baid;
9929af1bba4SBjoern A. Zeeb 
993*a4128aadSBjoern A. Zeeb 	/* mac80211 will remove sessions later, but we ignore all that */
994*a4128aadSBjoern A. Zeeb 	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
995*a4128aadSBjoern A. Zeeb 		return 0;
996*a4128aadSBjoern A. Zeeb 
9979af1bba4SBjoern A. Zeeb 	BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));
9989af1bba4SBjoern A. Zeeb 
9999af1bba4SBjoern A. Zeeb 	for (baid = 0; baid < ARRAY_SIZE(mvm->baid_map); baid++) {
10009af1bba4SBjoern A. Zeeb 		struct iwl_mvm_baid_data *data;
10019af1bba4SBjoern A. Zeeb 		int ret;
10029af1bba4SBjoern A. Zeeb 
10039af1bba4SBjoern A. Zeeb 		data = rcu_dereference_protected(mvm->baid_map[baid],
10049af1bba4SBjoern A. Zeeb 						 lockdep_is_held(&mvm->mutex));
10059af1bba4SBjoern A. Zeeb 		if (!data)
10069af1bba4SBjoern A. Zeeb 			continue;
10079af1bba4SBjoern A. Zeeb 
10089af1bba4SBjoern A. Zeeb 		if (!(data->sta_mask & old_sta_mask))
10099af1bba4SBjoern A. Zeeb 			continue;
10109af1bba4SBjoern A. Zeeb 
10119af1bba4SBjoern A. Zeeb 		WARN_ONCE(data->sta_mask != old_sta_mask,
10129af1bba4SBjoern A. Zeeb 			  "BAID data for %d corrupted - expected 0x%x found 0x%x\n",
10139af1bba4SBjoern A. Zeeb 			  baid, old_sta_mask, data->sta_mask);
10149af1bba4SBjoern A. Zeeb 
10159af1bba4SBjoern A. Zeeb 		cmd.modify.tid = cpu_to_le32(data->tid);
10169af1bba4SBjoern A. Zeeb 
1017*a4128aadSBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL,
1018*a4128aadSBjoern A. Zeeb 					   sizeof(cmd), &cmd);
10199af1bba4SBjoern A. Zeeb 		data->sta_mask = new_sta_mask;
10209af1bba4SBjoern A. Zeeb 		if (ret)
10219af1bba4SBjoern A. Zeeb 			return ret;
10229af1bba4SBjoern A. Zeeb 	}
10239af1bba4SBjoern A. Zeeb 
10249af1bba4SBjoern A. Zeeb 	return 0;
10259af1bba4SBjoern A. Zeeb }
10269af1bba4SBjoern A. Zeeb 
10279af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_update_sta_resources(struct iwl_mvm *mvm,
10289af1bba4SBjoern A. Zeeb 					    struct ieee80211_vif *vif,
10299af1bba4SBjoern A. Zeeb 					    struct ieee80211_sta *sta,
10309af1bba4SBjoern A. Zeeb 					    u32 old_sta_mask,
10319af1bba4SBjoern A. Zeeb 					    u32 new_sta_mask)
10329af1bba4SBjoern A. Zeeb {
10339af1bba4SBjoern A. Zeeb 	int ret;
10349af1bba4SBjoern A. Zeeb 
10359af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_mld_update_sta_queues(mvm, sta,
10369af1bba4SBjoern A. Zeeb 					    old_sta_mask,
10379af1bba4SBjoern A. Zeeb 					    new_sta_mask);
10389af1bba4SBjoern A. Zeeb 	if (ret)
10399af1bba4SBjoern A. Zeeb 		return ret;
10409af1bba4SBjoern A. Zeeb 
10419af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_mld_update_sta_keys(mvm, vif, sta,
10429af1bba4SBjoern A. Zeeb 					  old_sta_mask,
10439af1bba4SBjoern A. Zeeb 					  new_sta_mask);
10449af1bba4SBjoern A. Zeeb 	if (ret)
10459af1bba4SBjoern A. Zeeb 		return ret;
10469af1bba4SBjoern A. Zeeb 
10479af1bba4SBjoern A. Zeeb 	return iwl_mvm_mld_update_sta_baids(mvm, old_sta_mask, new_sta_mask);
10489af1bba4SBjoern A. Zeeb }
10499af1bba4SBjoern A. Zeeb 
10509af1bba4SBjoern A. Zeeb int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm,
10519af1bba4SBjoern A. Zeeb 				 struct ieee80211_vif *vif,
10529af1bba4SBjoern A. Zeeb 				 struct ieee80211_sta *sta,
10539af1bba4SBjoern A. Zeeb 				 u16 old_links, u16 new_links)
10549af1bba4SBjoern A. Zeeb {
10559af1bba4SBjoern A. Zeeb 	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
10569af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
10579af1bba4SBjoern A. Zeeb 	struct iwl_mvm_link_sta *mvm_sta_link;
10589af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *mvm_vif_link;
10599af1bba4SBjoern A. Zeeb 	unsigned long links_to_add = ~old_links & new_links;
10609af1bba4SBjoern A. Zeeb 	unsigned long links_to_rem = old_links & ~new_links;
10619af1bba4SBjoern A. Zeeb 	unsigned long old_links_long = old_links;
10629af1bba4SBjoern A. Zeeb 	u32 current_sta_mask = 0, sta_mask_added = 0, sta_mask_to_rem = 0;
10639af1bba4SBjoern A. Zeeb 	unsigned long link_sta_added_to_fw = 0, link_sta_allocated = 0;
10649af1bba4SBjoern A. Zeeb 	unsigned int link_id;
10659af1bba4SBjoern A. Zeeb 	int ret;
10669af1bba4SBjoern A. Zeeb 
10679af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
10689af1bba4SBjoern A. Zeeb 
10699af1bba4SBjoern A. Zeeb 	for_each_set_bit(link_id, &old_links_long,
10709af1bba4SBjoern A. Zeeb 			 IEEE80211_MLD_MAX_NUM_LINKS) {
10719af1bba4SBjoern A. Zeeb 		mvm_sta_link =
10729af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
10739af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
10749af1bba4SBjoern A. Zeeb 
10759af1bba4SBjoern A. Zeeb 		if (WARN_ON(!mvm_sta_link)) {
10769af1bba4SBjoern A. Zeeb 			ret = -EINVAL;
10779af1bba4SBjoern A. Zeeb 			goto err;
10789af1bba4SBjoern A. Zeeb 		}
10799af1bba4SBjoern A. Zeeb 
10809af1bba4SBjoern A. Zeeb 		current_sta_mask |= BIT(mvm_sta_link->sta_id);
10819af1bba4SBjoern A. Zeeb 		if (links_to_rem & BIT(link_id))
10829af1bba4SBjoern A. Zeeb 			sta_mask_to_rem |= BIT(mvm_sta_link->sta_id);
10839af1bba4SBjoern A. Zeeb 	}
10849af1bba4SBjoern A. Zeeb 
10859af1bba4SBjoern A. Zeeb 	if (sta_mask_to_rem) {
10869af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
10879af1bba4SBjoern A. Zeeb 						       current_sta_mask,
10889af1bba4SBjoern A. Zeeb 						       current_sta_mask &
10899af1bba4SBjoern A. Zeeb 							~sta_mask_to_rem);
10909af1bba4SBjoern A. Zeeb 		if (WARN_ON(ret))
10919af1bba4SBjoern A. Zeeb 			goto err;
10929af1bba4SBjoern A. Zeeb 
10939af1bba4SBjoern A. Zeeb 		current_sta_mask &= ~sta_mask_to_rem;
10949af1bba4SBjoern A. Zeeb 	}
10959af1bba4SBjoern A. Zeeb 
10969af1bba4SBjoern A. Zeeb 	for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
10979af1bba4SBjoern A. Zeeb 		mvm_sta_link =
10989af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
10999af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
11009af1bba4SBjoern A. Zeeb 		mvm_vif_link = mvm_vif->link[link_id];
11019af1bba4SBjoern A. Zeeb 
11029af1bba4SBjoern A. Zeeb 		if (WARN_ON(!mvm_sta_link || !mvm_vif_link)) {
11039af1bba4SBjoern A. Zeeb 			ret = -EINVAL;
11049af1bba4SBjoern A. Zeeb 			goto err;
11059af1bba4SBjoern A. Zeeb 		}
11069af1bba4SBjoern A. Zeeb 
11079af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
11089af1bba4SBjoern A. Zeeb 		if (WARN_ON(ret))
11099af1bba4SBjoern A. Zeeb 			goto err;
11109af1bba4SBjoern A. Zeeb 
11119af1bba4SBjoern A. Zeeb 		if (vif->type == NL80211_IFTYPE_STATION)
11129af1bba4SBjoern A. Zeeb 			mvm_vif_link->ap_sta_id = IWL_MVM_INVALID_STA;
11139af1bba4SBjoern A. Zeeb 
11149af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id,
11159af1bba4SBjoern A. Zeeb 					  false);
11169af1bba4SBjoern A. Zeeb 	}
11179af1bba4SBjoern A. Zeeb 
11189af1bba4SBjoern A. Zeeb 	for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
11199af1bba4SBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf =
11209af1bba4SBjoern A. Zeeb 			link_conf_dereference_protected(vif, link_id);
11219af1bba4SBjoern A. Zeeb 		struct ieee80211_link_sta *link_sta =
11229af1bba4SBjoern A. Zeeb 			link_sta_dereference_protected(sta, link_id);
11239af1bba4SBjoern A. Zeeb 		mvm_vif_link = mvm_vif->link[link_id];
11249af1bba4SBjoern A. Zeeb 
1125*a4128aadSBjoern A. Zeeb 		if (WARN_ON(!mvm_vif_link || !link_conf || !link_sta)) {
11269af1bba4SBjoern A. Zeeb 			ret = -EINVAL;
11279af1bba4SBjoern A. Zeeb 			goto err;
11289af1bba4SBjoern A. Zeeb 		}
11299af1bba4SBjoern A. Zeeb 
1130*a4128aadSBjoern A. Zeeb 		if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
1131*a4128aadSBjoern A. Zeeb 			struct iwl_mvm_link_sta *mvm_link_sta =
1132*a4128aadSBjoern A. Zeeb 				rcu_dereference_protected(mvm_sta->link[link_id],
1133*a4128aadSBjoern A. Zeeb 							  lockdep_is_held(&mvm->mutex));
1134*a4128aadSBjoern A. Zeeb 			u32 sta_id;
1135*a4128aadSBjoern A. Zeeb 
1136*a4128aadSBjoern A. Zeeb 			if (WARN_ON(!mvm_link_sta)) {
1137*a4128aadSBjoern A. Zeeb 				ret = -EINVAL;
1138*a4128aadSBjoern A. Zeeb 				goto err;
1139*a4128aadSBjoern A. Zeeb 			}
1140*a4128aadSBjoern A. Zeeb 
1141*a4128aadSBjoern A. Zeeb 			sta_id = mvm_link_sta->sta_id;
1142*a4128aadSBjoern A. Zeeb 
1143*a4128aadSBjoern A. Zeeb 			rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
1144*a4128aadSBjoern A. Zeeb 			rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id],
1145*a4128aadSBjoern A. Zeeb 					   link_sta);
1146*a4128aadSBjoern A. Zeeb 		} else {
1147*a4128aadSBjoern A. Zeeb 			if (WARN_ON(mvm_sta->link[link_id])) {
1148*a4128aadSBjoern A. Zeeb 				ret = -EINVAL;
1149*a4128aadSBjoern A. Zeeb 				goto err;
1150*a4128aadSBjoern A. Zeeb 			}
1151*a4128aadSBjoern A. Zeeb 			ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta,
1152*a4128aadSBjoern A. Zeeb 							 link_id);
11539af1bba4SBjoern A. Zeeb 			if (WARN_ON(ret))
11549af1bba4SBjoern A. Zeeb 				goto err;
1155*a4128aadSBjoern A. Zeeb 		}
11569af1bba4SBjoern A. Zeeb 
11579af1bba4SBjoern A. Zeeb 		link_sta->agg.max_rc_amsdu_len = 1;
11589af1bba4SBjoern A. Zeeb 		ieee80211_sta_recalc_aggregates(sta);
11599af1bba4SBjoern A. Zeeb 
11609af1bba4SBjoern A. Zeeb 		mvm_sta_link =
11619af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
11629af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
11639af1bba4SBjoern A. Zeeb 
11649af1bba4SBjoern A. Zeeb 		if (WARN_ON(!mvm_sta_link)) {
11659af1bba4SBjoern A. Zeeb 			ret = -EINVAL;
11669af1bba4SBjoern A. Zeeb 			goto err;
11679af1bba4SBjoern A. Zeeb 		}
11689af1bba4SBjoern A. Zeeb 
11699af1bba4SBjoern A. Zeeb 		if (vif->type == NL80211_IFTYPE_STATION)
11709af1bba4SBjoern A. Zeeb 			iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif_link,
11719af1bba4SBjoern A. Zeeb 						  mvm_sta_link);
11729af1bba4SBjoern A. Zeeb 
11739af1bba4SBjoern A. Zeeb 		link_sta_allocated |= BIT(link_id);
11749af1bba4SBjoern A. Zeeb 
11759af1bba4SBjoern A. Zeeb 		sta_mask_added |= BIT(mvm_sta_link->sta_id);
11769af1bba4SBjoern A. Zeeb 
11779af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
11789af1bba4SBjoern A. Zeeb 					  mvm_sta_link);
11799af1bba4SBjoern A. Zeeb 		if (WARN_ON(ret))
11809af1bba4SBjoern A. Zeeb 			goto err;
11819af1bba4SBjoern A. Zeeb 
11829af1bba4SBjoern A. Zeeb 		link_sta_added_to_fw |= BIT(link_id);
11839af1bba4SBjoern A. Zeeb 
11849af1bba4SBjoern A. Zeeb 		iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link);
11859af1bba4SBjoern A. Zeeb 	}
11869af1bba4SBjoern A. Zeeb 
11879af1bba4SBjoern A. Zeeb 	if (sta_mask_added) {
11889af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
11899af1bba4SBjoern A. Zeeb 						       current_sta_mask,
11909af1bba4SBjoern A. Zeeb 						       current_sta_mask |
11919af1bba4SBjoern A. Zeeb 							sta_mask_added);
11929af1bba4SBjoern A. Zeeb 		if (WARN_ON(ret))
11939af1bba4SBjoern A. Zeeb 			goto err;
11949af1bba4SBjoern A. Zeeb 	}
11959af1bba4SBjoern A. Zeeb 
11969af1bba4SBjoern A. Zeeb 	return 0;
11979af1bba4SBjoern A. Zeeb 
11989af1bba4SBjoern A. Zeeb err:
11999af1bba4SBjoern A. Zeeb 	/* remove all already allocated stations in FW */
12009af1bba4SBjoern A. Zeeb 	for_each_set_bit(link_id, &link_sta_added_to_fw,
12019af1bba4SBjoern A. Zeeb 			 IEEE80211_MLD_MAX_NUM_LINKS) {
12029af1bba4SBjoern A. Zeeb 		mvm_sta_link =
12039af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
12049af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
12059af1bba4SBjoern A. Zeeb 
12069af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
12079af1bba4SBjoern A. Zeeb 	}
12089af1bba4SBjoern A. Zeeb 
12099af1bba4SBjoern A. Zeeb 	/* remove all already allocated station links in driver */
12109af1bba4SBjoern A. Zeeb 	for_each_set_bit(link_id, &link_sta_allocated,
12119af1bba4SBjoern A. Zeeb 			 IEEE80211_MLD_MAX_NUM_LINKS) {
12129af1bba4SBjoern A. Zeeb 		mvm_sta_link =
12139af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvm_sta->link[link_id],
12149af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
12159af1bba4SBjoern A. Zeeb 
12169af1bba4SBjoern A. Zeeb 		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id,
12179af1bba4SBjoern A. Zeeb 					  false);
12189af1bba4SBjoern A. Zeeb 	}
12199af1bba4SBjoern A. Zeeb 
12209af1bba4SBjoern A. Zeeb 	return ret;
12219af1bba4SBjoern A. Zeeb }
1222