xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/sta.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2d1e879ecSMiri Korenblit /*
3d1e879ecSMiri Korenblit  * Copyright (C) 2024-2025 Intel Corporation
4d1e879ecSMiri Korenblit  */
5d1e879ecSMiri Korenblit 
6d1e879ecSMiri Korenblit #include <linux/ieee80211.h>
7d1e879ecSMiri Korenblit #include <kunit/static_stub.h>
8d1e879ecSMiri Korenblit 
9d1e879ecSMiri Korenblit #include "sta.h"
10d1e879ecSMiri Korenblit #include "hcmd.h"
11d1e879ecSMiri Korenblit #include "iface.h"
12d1e879ecSMiri Korenblit #include "mlo.h"
13d1e879ecSMiri Korenblit #include "key.h"
14d1e879ecSMiri Korenblit #include "agg.h"
15d1e879ecSMiri Korenblit #include "tlc.h"
16d1e879ecSMiri Korenblit #include "fw/api/sta.h"
17d1e879ecSMiri Korenblit #include "fw/api/mac.h"
18d1e879ecSMiri Korenblit #include "fw/api/rx.h"
19d1e879ecSMiri Korenblit 
iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta)20d1e879ecSMiri Korenblit int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
21d1e879ecSMiri Korenblit 				    struct ieee80211_link_sta *link_sta)
22d1e879ecSMiri Korenblit {
23d1e879ecSMiri Korenblit 	struct iwl_mld_link_sta *mld_link_sta;
24d1e879ecSMiri Korenblit 
25d1e879ecSMiri Korenblit 	/* This function should only be used with the wiphy lock held,
26d1e879ecSMiri Korenblit 	 * In other cases, it is not guaranteed that the link_sta will exist
27d1e879ecSMiri Korenblit 	 * in the driver too, and it is checked here.
28d1e879ecSMiri Korenblit 	 */
29d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
30d1e879ecSMiri Korenblit 
31d1e879ecSMiri Korenblit 	/* This is not meant to be called with a NULL pointer */
32d1e879ecSMiri Korenblit 	if (WARN_ON(!link_sta))
33d1e879ecSMiri Korenblit 		return -ENOENT;
34d1e879ecSMiri Korenblit 
35d1e879ecSMiri Korenblit 	mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
36d1e879ecSMiri Korenblit 	if (!mld_link_sta) {
37d1e879ecSMiri Korenblit 		WARN_ON(!iwl_mld_error_before_recovery(mld));
38d1e879ecSMiri Korenblit 		return -ENOENT;
39d1e879ecSMiri Korenblit 	}
40d1e879ecSMiri Korenblit 
41d1e879ecSMiri Korenblit 	return mld_link_sta->fw_id;
42d1e879ecSMiri Korenblit }
43d1e879ecSMiri Korenblit 
44d1e879ecSMiri Korenblit static void
iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta * link_sta,struct ieee80211_bss_conf * link,__le32 * tx_ampdu_max_size,__le32 * tx_ampdu_spacing)45d1e879ecSMiri Korenblit iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta,
46d1e879ecSMiri Korenblit 				 struct ieee80211_bss_conf *link,
47d1e879ecSMiri Korenblit 				 __le32 *tx_ampdu_max_size,
48d1e879ecSMiri Korenblit 				 __le32 *tx_ampdu_spacing)
49d1e879ecSMiri Korenblit {
50d1e879ecSMiri Korenblit 	u32 agg_size = 0, mpdu_dens = 0;
51d1e879ecSMiri Korenblit 
52d1e879ecSMiri Korenblit 	if (WARN_ON(!link_sta || !link))
53d1e879ecSMiri Korenblit 		return;
54d1e879ecSMiri Korenblit 
55d1e879ecSMiri Korenblit 	/* Note that we always use only legacy & highest supported PPDUs, so
56d1e879ecSMiri Korenblit 	 * of Draft P802.11be D.30 Table 10-12a--Fields used for calculating
57d1e879ecSMiri Korenblit 	 * the maximum A-MPDU size of various PPDU types in different bands,
58d1e879ecSMiri Korenblit 	 * we only need to worry about the highest supported PPDU type here.
59d1e879ecSMiri Korenblit 	 */
60d1e879ecSMiri Korenblit 
61d1e879ecSMiri Korenblit 	if (link_sta->ht_cap.ht_supported) {
62d1e879ecSMiri Korenblit 		agg_size = link_sta->ht_cap.ampdu_factor;
63d1e879ecSMiri Korenblit 		mpdu_dens = link_sta->ht_cap.ampdu_density;
64d1e879ecSMiri Korenblit 	}
65d1e879ecSMiri Korenblit 
66d1e879ecSMiri Korenblit 	if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) {
67d1e879ecSMiri Korenblit 		/* overwrite HT values on 6 GHz */
68d1e879ecSMiri Korenblit 		mpdu_dens =
69d1e879ecSMiri Korenblit 			le16_get_bits(link_sta->he_6ghz_capa.capa,
70d1e879ecSMiri Korenblit 				      IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
71d1e879ecSMiri Korenblit 		agg_size =
72d1e879ecSMiri Korenblit 			le16_get_bits(link_sta->he_6ghz_capa.capa,
73d1e879ecSMiri Korenblit 				      IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
74d1e879ecSMiri Korenblit 	} else if (link_sta->vht_cap.vht_supported) {
75d1e879ecSMiri Korenblit 		/* if VHT supported overwrite HT value */
76d1e879ecSMiri Korenblit 		agg_size =
77d1e879ecSMiri Korenblit 			u32_get_bits(link_sta->vht_cap.cap,
78d1e879ecSMiri Korenblit 				     IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
79d1e879ecSMiri Korenblit 	}
80d1e879ecSMiri Korenblit 
81d1e879ecSMiri Korenblit 	/* D6.0 10.12.2 A-MPDU length limit rules
82d1e879ecSMiri Korenblit 	 * A STA indicates the maximum length of the A-MPDU preEOF padding
83d1e879ecSMiri Korenblit 	 * that it can receive in an HE PPDU in the Maximum A-MPDU Length
84d1e879ecSMiri Korenblit 	 * Exponent field in its HT Capabilities, VHT Capabilities,
85d1e879ecSMiri Korenblit 	 * and HE 6 GHz Band Capabilities elements (if present) and the
86d1e879ecSMiri Korenblit 	 * Maximum AMPDU Length Exponent Extension field in its HE
87d1e879ecSMiri Korenblit 	 * Capabilities element
88d1e879ecSMiri Korenblit 	 */
89d1e879ecSMiri Korenblit 	if (link_sta->he_cap.has_he)
90d1e879ecSMiri Korenblit 		agg_size +=
91d1e879ecSMiri Korenblit 			u8_get_bits(link_sta->he_cap.he_cap_elem.mac_cap_info[3],
92d1e879ecSMiri Korenblit 				    IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK);
93d1e879ecSMiri Korenblit 
94d1e879ecSMiri Korenblit 	if (link_sta->eht_cap.has_eht)
95d1e879ecSMiri Korenblit 		agg_size +=
96d1e879ecSMiri Korenblit 			u8_get_bits(link_sta->eht_cap.eht_cap_elem.mac_cap_info[1],
97d1e879ecSMiri Korenblit 				    IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK);
98d1e879ecSMiri Korenblit 
99d1e879ecSMiri Korenblit 	/* Limit to max A-MPDU supported by FW */
100d1e879ecSMiri Korenblit 	agg_size = min_t(u32, agg_size,
101d1e879ecSMiri Korenblit 			 STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT);
102d1e879ecSMiri Korenblit 
103d1e879ecSMiri Korenblit 	*tx_ampdu_max_size = cpu_to_le32(agg_size);
104d1e879ecSMiri Korenblit 	*tx_ampdu_spacing = cpu_to_le32(mpdu_dens);
105d1e879ecSMiri Korenblit }
106d1e879ecSMiri Korenblit 
iwl_mld_get_uapsd_acs(struct ieee80211_sta * sta)107d1e879ecSMiri Korenblit static u8 iwl_mld_get_uapsd_acs(struct ieee80211_sta *sta)
108d1e879ecSMiri Korenblit {
109d1e879ecSMiri Korenblit 	u8 uapsd_acs = 0;
110d1e879ecSMiri Korenblit 
111d1e879ecSMiri Korenblit 	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
112d1e879ecSMiri Korenblit 		uapsd_acs |= BIT(AC_BK);
113d1e879ecSMiri Korenblit 	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
114d1e879ecSMiri Korenblit 		uapsd_acs |= BIT(AC_BE);
115d1e879ecSMiri Korenblit 	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
116d1e879ecSMiri Korenblit 		uapsd_acs |= BIT(AC_VI);
117d1e879ecSMiri Korenblit 	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
118d1e879ecSMiri Korenblit 		uapsd_acs |= BIT(AC_VO);
119d1e879ecSMiri Korenblit 
120d1e879ecSMiri Korenblit 	return uapsd_acs | uapsd_acs << 4;
121d1e879ecSMiri Korenblit }
122d1e879ecSMiri Korenblit 
iwl_mld_he_get_ppe_val(u8 * ppe,u8 ppe_pos_bit)123d1e879ecSMiri Korenblit static u8 iwl_mld_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit)
124d1e879ecSMiri Korenblit {
125d1e879ecSMiri Korenblit 	u8 byte_num = ppe_pos_bit / 8;
126d1e879ecSMiri Korenblit 	u8 bit_num = ppe_pos_bit % 8;
127d1e879ecSMiri Korenblit 	u8 residue_bits;
128d1e879ecSMiri Korenblit 	u8 res;
129d1e879ecSMiri Korenblit 
130d1e879ecSMiri Korenblit 	if (bit_num <= 5)
131d1e879ecSMiri Korenblit 		return (ppe[byte_num] >> bit_num) &
132d1e879ecSMiri Korenblit 		       (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE) - 1);
133d1e879ecSMiri Korenblit 
134d1e879ecSMiri Korenblit 	/* If bit_num > 5, we have to combine bits with next byte.
135d1e879ecSMiri Korenblit 	 * Calculate how many bits we need to take from current byte (called
136d1e879ecSMiri Korenblit 	 * here "residue_bits"), and add them to bits from next byte.
137d1e879ecSMiri Korenblit 	 */
138d1e879ecSMiri Korenblit 
139d1e879ecSMiri Korenblit 	residue_bits = 8 - bit_num;
140d1e879ecSMiri Korenblit 
141d1e879ecSMiri Korenblit 	res = (ppe[byte_num + 1] &
142d1e879ecSMiri Korenblit 	       (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE - residue_bits) - 1)) <<
143d1e879ecSMiri Korenblit 	      residue_bits;
144d1e879ecSMiri Korenblit 	res += (ppe[byte_num] >> bit_num) & (BIT(residue_bits) - 1);
145d1e879ecSMiri Korenblit 
146d1e879ecSMiri Korenblit 	return res;
147d1e879ecSMiri Korenblit }
148d1e879ecSMiri Korenblit 
iwl_mld_parse_ppe(struct iwl_mld * mld,struct iwl_he_pkt_ext_v2 * pkt_ext,u8 nss,u8 ru_index_bitmap,u8 * ppe,u8 ppe_pos_bit,bool inheritance)149d1e879ecSMiri Korenblit static void iwl_mld_parse_ppe(struct iwl_mld *mld,
150d1e879ecSMiri Korenblit 			      struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss,
151d1e879ecSMiri Korenblit 			      u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit,
152d1e879ecSMiri Korenblit 			      bool inheritance)
153d1e879ecSMiri Korenblit {
154d1e879ecSMiri Korenblit 	/* FW currently supports only nss == MAX_HE_SUPP_NSS
155d1e879ecSMiri Korenblit 	 *
156d1e879ecSMiri Korenblit 	 * If nss > MAX: we can ignore values we don't support
157d1e879ecSMiri Korenblit 	 * If nss < MAX: we can set zeros in other streams
158d1e879ecSMiri Korenblit 	 */
159d1e879ecSMiri Korenblit 	if (nss > MAX_HE_SUPP_NSS) {
160d1e879ecSMiri Korenblit 		IWL_DEBUG_INFO(mld, "Got NSS = %d - trimming to %d\n", nss,
161d1e879ecSMiri Korenblit 			       MAX_HE_SUPP_NSS);
162d1e879ecSMiri Korenblit 		nss = MAX_HE_SUPP_NSS;
163d1e879ecSMiri Korenblit 	}
164d1e879ecSMiri Korenblit 
165d1e879ecSMiri Korenblit 	for (int i = 0; i < nss; i++) {
166d1e879ecSMiri Korenblit 		u8 ru_index_tmp = ru_index_bitmap << 1;
167d1e879ecSMiri Korenblit 		u8 low_th = IWL_HE_PKT_EXT_NONE, high_th = IWL_HE_PKT_EXT_NONE;
168d1e879ecSMiri Korenblit 
169d1e879ecSMiri Korenblit 		for (u8 bw = 0;
170d1e879ecSMiri Korenblit 		     bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
171d1e879ecSMiri Korenblit 		     bw++) {
172d1e879ecSMiri Korenblit 			ru_index_tmp >>= 1;
173d1e879ecSMiri Korenblit 
174d1e879ecSMiri Korenblit 			/* According to the 11be spec, if for a specific BW the PPE Thresholds
175d1e879ecSMiri Korenblit 			 * isn't present - it should inherit the thresholds from the last
176d1e879ecSMiri Korenblit 			 * BW for which we had PPE Thresholds. In 11ax though, we don't have
177d1e879ecSMiri Korenblit 			 * this inheritance - continue in this case
178d1e879ecSMiri Korenblit 			 */
179d1e879ecSMiri Korenblit 			if (!(ru_index_tmp & 1)) {
180d1e879ecSMiri Korenblit 				if (inheritance)
181d1e879ecSMiri Korenblit 					goto set_thresholds;
182d1e879ecSMiri Korenblit 				else
183d1e879ecSMiri Korenblit 					continue;
184d1e879ecSMiri Korenblit 			}
185d1e879ecSMiri Korenblit 
186d1e879ecSMiri Korenblit 			high_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit);
187d1e879ecSMiri Korenblit 			ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
188d1e879ecSMiri Korenblit 			low_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit);
189d1e879ecSMiri Korenblit 			ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
190d1e879ecSMiri Korenblit 
191d1e879ecSMiri Korenblit set_thresholds:
192d1e879ecSMiri Korenblit 			pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
193d1e879ecSMiri Korenblit 			pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
194d1e879ecSMiri Korenblit 		}
195d1e879ecSMiri Korenblit 	}
196d1e879ecSMiri Korenblit }
197d1e879ecSMiri Korenblit 
iwl_mld_set_pkt_ext_from_he_ppe(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta,struct iwl_he_pkt_ext_v2 * pkt_ext,bool inheritance)198d1e879ecSMiri Korenblit static void iwl_mld_set_pkt_ext_from_he_ppe(struct iwl_mld *mld,
199d1e879ecSMiri Korenblit 					    struct ieee80211_link_sta *link_sta,
200d1e879ecSMiri Korenblit 					    struct iwl_he_pkt_ext_v2 *pkt_ext,
201d1e879ecSMiri Korenblit 					    bool inheritance)
202d1e879ecSMiri Korenblit {
203d1e879ecSMiri Korenblit 	u8 nss = (link_sta->he_cap.ppe_thres[0] &
204d1e879ecSMiri Korenblit 		  IEEE80211_PPE_THRES_NSS_MASK) + 1;
205d1e879ecSMiri Korenblit 	u8 *ppe = &link_sta->he_cap.ppe_thres[0];
206d1e879ecSMiri Korenblit 	u8 ru_index_bitmap =
207d1e879ecSMiri Korenblit 		u8_get_bits(*ppe,
208d1e879ecSMiri Korenblit 			    IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
209d1e879ecSMiri Korenblit 	/* Starting after PPE header */
210d1e879ecSMiri Korenblit 	u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE;
211d1e879ecSMiri Korenblit 
212d1e879ecSMiri Korenblit 	iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit,
213d1e879ecSMiri Korenblit 			  inheritance);
214d1e879ecSMiri Korenblit }
215d1e879ecSMiri Korenblit 
216d1e879ecSMiri Korenblit static int
iwl_mld_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 * pkt_ext,u8 nominal_padding)217d1e879ecSMiri Korenblit iwl_mld_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext,
218d1e879ecSMiri Korenblit 					 u8 nominal_padding)
219d1e879ecSMiri Korenblit {
220d1e879ecSMiri Korenblit 	int low_th = -1;
221d1e879ecSMiri Korenblit 	int high_th = -1;
222d1e879ecSMiri Korenblit 
223d1e879ecSMiri Korenblit 	/* all the macros are the same for EHT and HE */
224d1e879ecSMiri Korenblit 	switch (nominal_padding) {
225d1e879ecSMiri Korenblit 	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US:
226d1e879ecSMiri Korenblit 		low_th = IWL_HE_PKT_EXT_NONE;
227d1e879ecSMiri Korenblit 		high_th = IWL_HE_PKT_EXT_NONE;
228d1e879ecSMiri Korenblit 		break;
229d1e879ecSMiri Korenblit 	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US:
230d1e879ecSMiri Korenblit 		low_th = IWL_HE_PKT_EXT_BPSK;
231d1e879ecSMiri Korenblit 		high_th = IWL_HE_PKT_EXT_NONE;
232d1e879ecSMiri Korenblit 		break;
233d1e879ecSMiri Korenblit 	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US:
234d1e879ecSMiri Korenblit 	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US:
235d1e879ecSMiri Korenblit 		low_th = IWL_HE_PKT_EXT_NONE;
236d1e879ecSMiri Korenblit 		high_th = IWL_HE_PKT_EXT_BPSK;
237d1e879ecSMiri Korenblit 		break;
238d1e879ecSMiri Korenblit 	}
239d1e879ecSMiri Korenblit 
240d1e879ecSMiri Korenblit 	if (low_th < 0 || high_th < 0)
241d1e879ecSMiri Korenblit 		return -EINVAL;
242d1e879ecSMiri Korenblit 
243d1e879ecSMiri Korenblit 	/* Set the PPE thresholds accordingly */
244d1e879ecSMiri Korenblit 	for (int i = 0; i < MAX_HE_SUPP_NSS; i++) {
245d1e879ecSMiri Korenblit 		for (u8 bw = 0;
246d1e879ecSMiri Korenblit 			bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
247d1e879ecSMiri Korenblit 			bw++) {
248d1e879ecSMiri Korenblit 			pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
249d1e879ecSMiri Korenblit 			pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
250d1e879ecSMiri Korenblit 		}
251d1e879ecSMiri Korenblit 	}
252d1e879ecSMiri Korenblit 
253d1e879ecSMiri Korenblit 	return 0;
254d1e879ecSMiri Korenblit }
255d1e879ecSMiri Korenblit 
iwl_mld_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 * pkt_ext,u8 nominal_padding)256d1e879ecSMiri Korenblit static void iwl_mld_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,
257d1e879ecSMiri Korenblit 					 u8 nominal_padding)
258d1e879ecSMiri Korenblit {
259d1e879ecSMiri Korenblit 	for (int i = 0; i < MAX_HE_SUPP_NSS; i++) {
260d1e879ecSMiri Korenblit 		for (u8 bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
261d1e879ecSMiri Korenblit 		     bw++) {
262d1e879ecSMiri Korenblit 			u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0];
263d1e879ecSMiri Korenblit 
264d1e879ecSMiri Korenblit 			if (nominal_padding >
265d1e879ecSMiri Korenblit 			    IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US &&
266d1e879ecSMiri Korenblit 			    qam_th[1] == IWL_HE_PKT_EXT_NONE)
267d1e879ecSMiri Korenblit 				qam_th[1] = IWL_HE_PKT_EXT_4096QAM;
268d1e879ecSMiri Korenblit 			else if (nominal_padding ==
269d1e879ecSMiri Korenblit 				 IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US &&
270d1e879ecSMiri Korenblit 				 qam_th[0] == IWL_HE_PKT_EXT_NONE &&
271d1e879ecSMiri Korenblit 				 qam_th[1] == IWL_HE_PKT_EXT_NONE)
272d1e879ecSMiri Korenblit 				qam_th[0] = IWL_HE_PKT_EXT_4096QAM;
273d1e879ecSMiri Korenblit 		}
274d1e879ecSMiri Korenblit 	}
275d1e879ecSMiri Korenblit }
276d1e879ecSMiri Korenblit 
iwl_mld_fill_pkt_ext(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta,struct iwl_he_pkt_ext_v2 * pkt_ext)277d1e879ecSMiri Korenblit static void iwl_mld_fill_pkt_ext(struct iwl_mld *mld,
278d1e879ecSMiri Korenblit 				 struct ieee80211_link_sta *link_sta,
279d1e879ecSMiri Korenblit 				 struct iwl_he_pkt_ext_v2 *pkt_ext)
280d1e879ecSMiri Korenblit {
281d1e879ecSMiri Korenblit 	if (WARN_ON(!link_sta))
282d1e879ecSMiri Korenblit 		return;
283d1e879ecSMiri Korenblit 
284d1e879ecSMiri Korenblit 	/* Initialize the PPE thresholds to "None" (7), as described in Table
285d1e879ecSMiri Korenblit 	 * 9-262ac of 80211.ax/D3.0.
286d1e879ecSMiri Korenblit 	 */
287d1e879ecSMiri Korenblit 	memset(pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(*pkt_ext));
288d1e879ecSMiri Korenblit 
289d1e879ecSMiri Korenblit 	if (link_sta->eht_cap.has_eht) {
290d1e879ecSMiri Korenblit 		u8 nominal_padding =
291d1e879ecSMiri Korenblit 			u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5],
292d1e879ecSMiri Korenblit 				    IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK);
293d1e879ecSMiri Korenblit 
294d1e879ecSMiri Korenblit 		/* If PPE Thresholds exists, parse them into a FW-familiar
295d1e879ecSMiri Korenblit 		 * format.
296d1e879ecSMiri Korenblit 		 */
297d1e879ecSMiri Korenblit 		if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
298d1e879ecSMiri Korenblit 		    IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
299d1e879ecSMiri Korenblit 			u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] &
300d1e879ecSMiri Korenblit 				IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1;
301d1e879ecSMiri Korenblit 			u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0];
302d1e879ecSMiri Korenblit 			u8 ru_index_bitmap =
303d1e879ecSMiri Korenblit 				u16_get_bits(*ppe,
304d1e879ecSMiri Korenblit 					     IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK);
305d1e879ecSMiri Korenblit 			 /* Starting after PPE header */
306d1e879ecSMiri Korenblit 			u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE;
307d1e879ecSMiri Korenblit 
308d1e879ecSMiri Korenblit 			iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap,
309d1e879ecSMiri Korenblit 					  ppe, ppe_pos_bit, true);
310d1e879ecSMiri Korenblit 		/* EHT PPE Thresholds doesn't exist - set the API according to
311d1e879ecSMiri Korenblit 		 * HE PPE Tresholds
312d1e879ecSMiri Korenblit 		 */
313d1e879ecSMiri Korenblit 		} else if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
314d1e879ecSMiri Korenblit 			   IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
315d1e879ecSMiri Korenblit 			/* Even though HE Capabilities IE doesn't contain PPE
316d1e879ecSMiri Korenblit 			 * Thresholds for BW 320Mhz, thresholds for this BW will
317d1e879ecSMiri Korenblit 			 * be filled in with the same values as 160Mhz, due to
318d1e879ecSMiri Korenblit 			 * the inheritance, as required.
319d1e879ecSMiri Korenblit 			 */
320d1e879ecSMiri Korenblit 			iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext,
321d1e879ecSMiri Korenblit 							true);
322d1e879ecSMiri Korenblit 
323d1e879ecSMiri Korenblit 			/* According to the requirements, for MCSs 12-13 the
324d1e879ecSMiri Korenblit 			 * maximum value between HE PPE Threshold and Common
325d1e879ecSMiri Korenblit 			 * Nominal Packet Padding needs to be taken
326d1e879ecSMiri Korenblit 			 */
327d1e879ecSMiri Korenblit 			iwl_mld_get_optimal_ppe_info(pkt_ext, nominal_padding);
328d1e879ecSMiri Korenblit 
329d1e879ecSMiri Korenblit 		/* if PPE Thresholds doesn't present in both EHT IE and HE IE -
330d1e879ecSMiri Korenblit 		 * take the Thresholds from Common Nominal Packet Padding field
331d1e879ecSMiri Korenblit 		 */
332d1e879ecSMiri Korenblit 		} else {
333d1e879ecSMiri Korenblit 			iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext,
334d1e879ecSMiri Korenblit 								 nominal_padding);
335d1e879ecSMiri Korenblit 		}
336d1e879ecSMiri Korenblit 	} else if (link_sta->he_cap.has_he) {
337d1e879ecSMiri Korenblit 		/* If PPE Thresholds exist, parse them into a FW-familiar format. */
338d1e879ecSMiri Korenblit 		if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
339d1e879ecSMiri Korenblit 			IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
340d1e879ecSMiri Korenblit 			iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext,
341d1e879ecSMiri Korenblit 							false);
342d1e879ecSMiri Korenblit 		/* PPE Thresholds doesn't exist - set the API PPE values
343d1e879ecSMiri Korenblit 		 * according to Common Nominal Packet Padding field.
344d1e879ecSMiri Korenblit 		 */
345d1e879ecSMiri Korenblit 		} else {
346d1e879ecSMiri Korenblit 			u8 nominal_padding =
347d1e879ecSMiri Korenblit 				u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9],
348d1e879ecSMiri Korenblit 					    IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
349d1e879ecSMiri Korenblit 			if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED)
350d1e879ecSMiri Korenblit 				iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext,
351d1e879ecSMiri Korenblit 									 nominal_padding);
352d1e879ecSMiri Korenblit 		}
353d1e879ecSMiri Korenblit 	}
354d1e879ecSMiri Korenblit 
355d1e879ecSMiri Korenblit 	for (int i = 0; i < MAX_HE_SUPP_NSS; i++) {
356d1e879ecSMiri Korenblit 		for (int bw = 0;
357d1e879ecSMiri Korenblit 		     bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]);
358d1e879ecSMiri Korenblit 		     bw++) {
359d1e879ecSMiri Korenblit 			u8 *qam_th =
360d1e879ecSMiri Korenblit 				&pkt_ext->pkt_ext_qam_th[i][bw][0];
361d1e879ecSMiri Korenblit 
362d1e879ecSMiri Korenblit 			IWL_DEBUG_HT(mld,
363d1e879ecSMiri Korenblit 				     "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n",
364d1e879ecSMiri Korenblit 				     i, bw, qam_th[0], qam_th[1]);
365d1e879ecSMiri Korenblit 		}
366d1e879ecSMiri Korenblit 	}
367d1e879ecSMiri Korenblit }
368d1e879ecSMiri Korenblit 
iwl_mld_get_htc_flags(struct ieee80211_link_sta * link_sta)369d1e879ecSMiri Korenblit static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta)
370d1e879ecSMiri Korenblit {
371d1e879ecSMiri Korenblit 	u8 *mac_cap_info =
372d1e879ecSMiri Korenblit 		&link_sta->he_cap.he_cap_elem.mac_cap_info[0];
373d1e879ecSMiri Korenblit 	u32 htc_flags = 0;
374d1e879ecSMiri Korenblit 
375d1e879ecSMiri Korenblit 	if (mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE)
376d1e879ecSMiri Korenblit 		htc_flags |= IWL_HE_HTC_SUPPORT;
377d1e879ecSMiri Korenblit 	if ((mac_cap_info[1] & IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) ||
378d1e879ecSMiri Korenblit 	    (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) {
379d1e879ecSMiri Korenblit 		u8 link_adap =
380d1e879ecSMiri Korenblit 			((mac_cap_info[2] &
381d1e879ecSMiri Korenblit 			  IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) +
382d1e879ecSMiri Korenblit 			 (mac_cap_info[1] &
383d1e879ecSMiri Korenblit 			  IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION);
384d1e879ecSMiri Korenblit 
385d1e879ecSMiri Korenblit 		if (link_adap == 2)
386d1e879ecSMiri Korenblit 			htc_flags |=
387d1e879ecSMiri Korenblit 				IWL_HE_HTC_LINK_ADAP_UNSOLICITED;
388d1e879ecSMiri Korenblit 		else if (link_adap == 3)
389d1e879ecSMiri Korenblit 			htc_flags |= IWL_HE_HTC_LINK_ADAP_BOTH;
390d1e879ecSMiri Korenblit 	}
391d1e879ecSMiri Korenblit 	if (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR)
392d1e879ecSMiri Korenblit 		htc_flags |= IWL_HE_HTC_BSR_SUPP;
393d1e879ecSMiri Korenblit 	if (mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL)
394d1e879ecSMiri Korenblit 		htc_flags |= IWL_HE_HTC_OMI_SUPP;
395d1e879ecSMiri Korenblit 	if (mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
396d1e879ecSMiri Korenblit 		htc_flags |= IWL_HE_HTC_BQR_SUPP;
397d1e879ecSMiri Korenblit 
398d1e879ecSMiri Korenblit 	return htc_flags;
399d1e879ecSMiri Korenblit }
400d1e879ecSMiri Korenblit 
iwl_mld_send_sta_cmd(struct iwl_mld * mld,const struct iwl_sta_cfg_cmd * cmd)401d1e879ecSMiri Korenblit static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
402d1e879ecSMiri Korenblit 				const struct iwl_sta_cfg_cmd *cmd)
403d1e879ecSMiri Korenblit {
404d1e879ecSMiri Korenblit 	int ret = iwl_mld_send_cmd_pdu(mld,
405d1e879ecSMiri Korenblit 				       WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
406d1e879ecSMiri Korenblit 				       cmd);
407d1e879ecSMiri Korenblit 	if (ret)
408d1e879ecSMiri Korenblit 		IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
409d1e879ecSMiri Korenblit 	return ret;
410d1e879ecSMiri Korenblit }
411d1e879ecSMiri Korenblit 
412d1e879ecSMiri Korenblit static int
iwl_mld_add_modify_sta_cmd(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta)413d1e879ecSMiri Korenblit iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
414d1e879ecSMiri Korenblit 			   struct ieee80211_link_sta *link_sta)
415d1e879ecSMiri Korenblit {
416d1e879ecSMiri Korenblit 	struct ieee80211_sta *sta = link_sta->sta;
417d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
418d1e879ecSMiri Korenblit 	struct ieee80211_bss_conf *link;
419d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link;
420d1e879ecSMiri Korenblit 	struct iwl_sta_cfg_cmd cmd = {};
421d1e879ecSMiri Korenblit 	int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
422d1e879ecSMiri Korenblit 
423d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
424d1e879ecSMiri Korenblit 
425d1e879ecSMiri Korenblit 	link = link_conf_dereference_protected(mld_sta->vif,
426d1e879ecSMiri Korenblit 					       link_sta->link_id);
427d1e879ecSMiri Korenblit 
428d1e879ecSMiri Korenblit 	mld_link = iwl_mld_link_from_mac80211(link);
429d1e879ecSMiri Korenblit 
430d1e879ecSMiri Korenblit 	if (WARN_ON(!link || !mld_link) || fw_id < 0)
431d1e879ecSMiri Korenblit 		return -EINVAL;
432d1e879ecSMiri Korenblit 
433d1e879ecSMiri Korenblit 	cmd.sta_id = cpu_to_le32(fw_id);
434d1e879ecSMiri Korenblit 	cmd.station_type = cpu_to_le32(mld_sta->sta_type);
435d1e879ecSMiri Korenblit 	cmd.link_id = cpu_to_le32(mld_link->fw_id);
436d1e879ecSMiri Korenblit 
437d1e879ecSMiri Korenblit 	memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
438d1e879ecSMiri Korenblit 	memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN);
439d1e879ecSMiri Korenblit 
440d1e879ecSMiri Korenblit 	if (mld_sta->sta_state >= IEEE80211_STA_ASSOC)
441d1e879ecSMiri Korenblit 		cmd.assoc_id = cpu_to_le32(sta->aid);
442d1e879ecSMiri Korenblit 
443d1e879ecSMiri Korenblit 	if (sta->mfp || mld_sta->sta_state < IEEE80211_STA_AUTHORIZED)
444d1e879ecSMiri Korenblit 		cmd.mfp = cpu_to_le32(1);
445d1e879ecSMiri Korenblit 
446d1e879ecSMiri Korenblit 	switch (link_sta->rx_nss) {
447d1e879ecSMiri Korenblit 	case 1:
448d1e879ecSMiri Korenblit 		cmd.mimo = cpu_to_le32(0);
449d1e879ecSMiri Korenblit 		break;
450d1e879ecSMiri Korenblit 	case 2 ... 8:
451d1e879ecSMiri Korenblit 		cmd.mimo = cpu_to_le32(1);
452d1e879ecSMiri Korenblit 		break;
453d1e879ecSMiri Korenblit 	}
454d1e879ecSMiri Korenblit 
455d1e879ecSMiri Korenblit 	switch (link_sta->smps_mode) {
456d1e879ecSMiri Korenblit 	case IEEE80211_SMPS_AUTOMATIC:
457d1e879ecSMiri Korenblit 	case IEEE80211_SMPS_NUM_MODES:
458d1e879ecSMiri Korenblit 		WARN_ON(1);
459d1e879ecSMiri Korenblit 		break;
460d1e879ecSMiri Korenblit 	case IEEE80211_SMPS_STATIC:
461d1e879ecSMiri Korenblit 		/* override NSS */
462d1e879ecSMiri Korenblit 		cmd.mimo = cpu_to_le32(0);
463d1e879ecSMiri Korenblit 		break;
464d1e879ecSMiri Korenblit 	case IEEE80211_SMPS_DYNAMIC:
465d1e879ecSMiri Korenblit 		cmd.mimo_protection = cpu_to_le32(1);
466d1e879ecSMiri Korenblit 		break;
467d1e879ecSMiri Korenblit 	case IEEE80211_SMPS_OFF:
468d1e879ecSMiri Korenblit 		/* nothing */
469d1e879ecSMiri Korenblit 		break;
470d1e879ecSMiri Korenblit 	}
471d1e879ecSMiri Korenblit 
472d1e879ecSMiri Korenblit 	iwl_mld_fill_ampdu_size_and_dens(link_sta, link,
473d1e879ecSMiri Korenblit 					 &cmd.tx_ampdu_max_size,
474d1e879ecSMiri Korenblit 					 &cmd.tx_ampdu_spacing);
475d1e879ecSMiri Korenblit 
476d1e879ecSMiri Korenblit 	if (sta->wme) {
477d1e879ecSMiri Korenblit 		cmd.sp_length =
478d1e879ecSMiri Korenblit 			cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128);
479d1e879ecSMiri Korenblit 		cmd.uapsd_acs = cpu_to_le32(iwl_mld_get_uapsd_acs(sta));
480d1e879ecSMiri Korenblit 	}
481d1e879ecSMiri Korenblit 
482d1e879ecSMiri Korenblit 	if (link_sta->he_cap.has_he) {
483d1e879ecSMiri Korenblit 		cmd.trig_rnd_alloc =
484d1e879ecSMiri Korenblit 			cpu_to_le32(link->uora_exists ? 1 : 0);
485d1e879ecSMiri Korenblit 
486d1e879ecSMiri Korenblit 		/* PPE Thresholds */
487d1e879ecSMiri Korenblit 		iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext);
488d1e879ecSMiri Korenblit 
489d1e879ecSMiri Korenblit 		/* HTC flags */
490d1e879ecSMiri Korenblit 		cmd.htc_flags =
491d1e879ecSMiri Korenblit 			cpu_to_le32(iwl_mld_get_htc_flags(link_sta));
492d1e879ecSMiri Korenblit 
493d1e879ecSMiri Korenblit 		if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] &
494d1e879ecSMiri Korenblit 		    IEEE80211_HE_MAC_CAP2_ACK_EN)
495d1e879ecSMiri Korenblit 			cmd.ack_enabled = cpu_to_le32(1);
496d1e879ecSMiri Korenblit 	}
497d1e879ecSMiri Korenblit 
498d1e879ecSMiri Korenblit 	return iwl_mld_send_sta_cmd(mld, &cmd);
499d1e879ecSMiri Korenblit }
500d1e879ecSMiri Korenblit 
IWL_MLD_ALLOC_FN(link_sta,link_sta)501d1e879ecSMiri Korenblit IWL_MLD_ALLOC_FN(link_sta, link_sta)
502d1e879ecSMiri Korenblit 
503d1e879ecSMiri Korenblit static int
504d1e879ecSMiri Korenblit iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
505d1e879ecSMiri Korenblit {
506d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
507d1e879ecSMiri Korenblit 	struct iwl_mld_link_sta *mld_link_sta;
508d1e879ecSMiri Korenblit 	int ret;
509d1e879ecSMiri Korenblit 	u8 fw_id;
510d1e879ecSMiri Korenblit 
511d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
512d1e879ecSMiri Korenblit 
513d1e879ecSMiri Korenblit 	/* We will fail to add it to the FW anyway */
514d1e879ecSMiri Korenblit 	if (iwl_mld_error_before_recovery(mld))
515d1e879ecSMiri Korenblit 		return -ENODEV;
516d1e879ecSMiri Korenblit 
517d1e879ecSMiri Korenblit 	mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
518d1e879ecSMiri Korenblit 
519d1e879ecSMiri Korenblit 	/* We need to preserve the fw sta ids during a restart, since the fw
520d1e879ecSMiri Korenblit 	 * will recover SN/PN for them, this is why the mld_link_sta exists.
521d1e879ecSMiri Korenblit 	 */
522d1e879ecSMiri Korenblit 	if (mld_link_sta) {
523d1e879ecSMiri Korenblit 		/* But if we are not restarting, this is not OK */
524d1e879ecSMiri Korenblit 		WARN_ON(!mld->fw_status.in_hw_restart);
525d1e879ecSMiri Korenblit 
526d1e879ecSMiri Korenblit 		/* Avoid adding a STA that is already in FW to avoid an assert */
527d1e879ecSMiri Korenblit 		if (WARN_ON(mld_link_sta->in_fw))
528d1e879ecSMiri Korenblit 			return -EINVAL;
529d1e879ecSMiri Korenblit 
530d1e879ecSMiri Korenblit 		fw_id = mld_link_sta->fw_id;
531d1e879ecSMiri Korenblit 		goto add_to_fw;
532d1e879ecSMiri Korenblit 	}
533d1e879ecSMiri Korenblit 
534d1e879ecSMiri Korenblit 	/* Allocate a fw id and map it to the link_sta */
535d1e879ecSMiri Korenblit 	ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta);
536d1e879ecSMiri Korenblit 	if (ret)
537d1e879ecSMiri Korenblit 		return ret;
538d1e879ecSMiri Korenblit 
539d1e879ecSMiri Korenblit 	if (link_sta == &link_sta->sta->deflink) {
540d1e879ecSMiri Korenblit 		mld_link_sta = &mld_sta->deflink;
541d1e879ecSMiri Korenblit 	} else {
542d1e879ecSMiri Korenblit 		mld_link_sta = kzalloc(sizeof(*mld_link_sta), GFP_KERNEL);
543d1e879ecSMiri Korenblit 		if (!mld_link_sta)
544d1e879ecSMiri Korenblit 			return -ENOMEM;
545d1e879ecSMiri Korenblit 	}
546d1e879ecSMiri Korenblit 
547d1e879ecSMiri Korenblit 	mld_link_sta->fw_id = fw_id;
548d1e879ecSMiri Korenblit 	rcu_assign_pointer(mld_sta->link[link_sta->link_id], mld_link_sta);
549d1e879ecSMiri Korenblit 
550d1e879ecSMiri Korenblit add_to_fw:
551d1e879ecSMiri Korenblit 	ret = iwl_mld_add_modify_sta_cmd(mld, link_sta);
552d1e879ecSMiri Korenblit 	if (ret) {
553d1e879ecSMiri Korenblit 		RCU_INIT_POINTER(mld->fw_id_to_link_sta[fw_id], NULL);
554d1e879ecSMiri Korenblit 		RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL);
555d1e879ecSMiri Korenblit 		if (link_sta != &link_sta->sta->deflink)
556d1e879ecSMiri Korenblit 			kfree(mld_link_sta);
557d1e879ecSMiri Korenblit 		return ret;
558d1e879ecSMiri Korenblit 	}
559d1e879ecSMiri Korenblit 	mld_link_sta->in_fw = true;
560d1e879ecSMiri Korenblit 
561d1e879ecSMiri Korenblit 	return 0;
562d1e879ecSMiri Korenblit }
563d1e879ecSMiri Korenblit 
iwl_mld_rm_sta_from_fw(struct iwl_mld * mld,u8 fw_sta_id)564d1e879ecSMiri Korenblit static int iwl_mld_rm_sta_from_fw(struct iwl_mld *mld, u8 fw_sta_id)
565d1e879ecSMiri Korenblit {
566d1e879ecSMiri Korenblit 	struct iwl_remove_sta_cmd cmd = {
567d1e879ecSMiri Korenblit 		.sta_id = cpu_to_le32(fw_sta_id),
568d1e879ecSMiri Korenblit 	};
569d1e879ecSMiri Korenblit 	int ret;
570d1e879ecSMiri Korenblit 
571d1e879ecSMiri Korenblit 	ret = iwl_mld_send_cmd_pdu(mld,
572d1e879ecSMiri Korenblit 				   WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD),
573d1e879ecSMiri Korenblit 				   &cmd);
574d1e879ecSMiri Korenblit 	if (ret)
575d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Failed to remove station. Id=%d\n", fw_sta_id);
576d1e879ecSMiri Korenblit 
577d1e879ecSMiri Korenblit 	return ret;
578d1e879ecSMiri Korenblit }
579d1e879ecSMiri Korenblit 
580d1e879ecSMiri Korenblit static void
iwl_mld_remove_link_sta(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta)581d1e879ecSMiri Korenblit iwl_mld_remove_link_sta(struct iwl_mld *mld,
582d1e879ecSMiri Korenblit 			struct ieee80211_link_sta *link_sta)
583d1e879ecSMiri Korenblit {
584d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
585d1e879ecSMiri Korenblit 	struct iwl_mld_link_sta *mld_link_sta =
586d1e879ecSMiri Korenblit 		iwl_mld_link_sta_from_mac80211(link_sta);
587d1e879ecSMiri Korenblit 
588d1e879ecSMiri Korenblit 	if (WARN_ON(!mld_link_sta))
589d1e879ecSMiri Korenblit 		return;
590d1e879ecSMiri Korenblit 
591d1e879ecSMiri Korenblit 	iwl_mld_rm_sta_from_fw(mld, mld_link_sta->fw_id);
592d1e879ecSMiri Korenblit 	mld_link_sta->in_fw = false;
593d1e879ecSMiri Korenblit 
594d1e879ecSMiri Korenblit 	/* Now that the STA doesn't exist in FW, we don't expect any new
595d1e879ecSMiri Korenblit 	 * notifications for it. Cancel the ones that are already pending
596d1e879ecSMiri Korenblit 	 */
597d1e879ecSMiri Korenblit 	iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_STA,
598d1e879ecSMiri Korenblit 					       mld_link_sta->fw_id);
599d1e879ecSMiri Korenblit 
600d1e879ecSMiri Korenblit 	/* This will not be done upon reconfig, so do it also when
601d1e879ecSMiri Korenblit 	 * failed to remove from fw
602d1e879ecSMiri Korenblit 	 */
603d1e879ecSMiri Korenblit 	RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], NULL);
604d1e879ecSMiri Korenblit 	RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL);
605d1e879ecSMiri Korenblit 	if (mld_link_sta != &mld_sta->deflink)
606d1e879ecSMiri Korenblit 		kfree_rcu(mld_link_sta, rcu_head);
607d1e879ecSMiri Korenblit }
608d1e879ecSMiri Korenblit 
iwl_mld_set_max_amsdu_len(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta)609*36b79cb0SIlan Peer static void iwl_mld_set_max_amsdu_len(struct iwl_mld *mld,
610*36b79cb0SIlan Peer 				      struct ieee80211_link_sta *link_sta)
611*36b79cb0SIlan Peer {
612*36b79cb0SIlan Peer 	const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
613*36b79cb0SIlan Peer 
614*36b79cb0SIlan Peer 	/* For EHT, HE and VHT we can use the value as it was calculated by
615*36b79cb0SIlan Peer 	 * mac80211. For HT, mac80211 doesn't enforce to 4095, so force it
616*36b79cb0SIlan Peer 	 * here
617*36b79cb0SIlan Peer 	 */
618*36b79cb0SIlan Peer 	if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he ||
619*36b79cb0SIlan Peer 	    link_sta->vht_cap.vht_supported ||
620*36b79cb0SIlan Peer 	    !ht_cap->ht_supported ||
621*36b79cb0SIlan Peer 	    !(ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU))
622*36b79cb0SIlan Peer 		return;
623*36b79cb0SIlan Peer 
624*36b79cb0SIlan Peer 	link_sta->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
625*36b79cb0SIlan Peer 	ieee80211_sta_recalc_aggregates(link_sta->sta);
626*36b79cb0SIlan Peer }
627*36b79cb0SIlan Peer 
iwl_mld_update_all_link_stations(struct iwl_mld * mld,struct ieee80211_sta * sta)628d1e879ecSMiri Korenblit int iwl_mld_update_all_link_stations(struct iwl_mld *mld,
629d1e879ecSMiri Korenblit 				     struct ieee80211_sta *sta)
630d1e879ecSMiri Korenblit {
631d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
632d1e879ecSMiri Korenblit 	struct ieee80211_link_sta *link_sta;
633d1e879ecSMiri Korenblit 	int link_id;
634d1e879ecSMiri Korenblit 
635d1e879ecSMiri Korenblit 	for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
636d1e879ecSMiri Korenblit 		int ret = iwl_mld_add_modify_sta_cmd(mld, link_sta);
637d1e879ecSMiri Korenblit 
638d1e879ecSMiri Korenblit 		if (ret)
639d1e879ecSMiri Korenblit 			return ret;
640*36b79cb0SIlan Peer 
641*36b79cb0SIlan Peer 		if (mld_sta->sta_state == IEEE80211_STA_ASSOC)
642*36b79cb0SIlan Peer 			iwl_mld_set_max_amsdu_len(mld, link_sta);
643d1e879ecSMiri Korenblit 	}
644d1e879ecSMiri Korenblit 	return 0;
645d1e879ecSMiri Korenblit }
646d1e879ecSMiri Korenblit 
iwl_mld_destroy_sta(struct ieee80211_sta * sta)647d1e879ecSMiri Korenblit static void iwl_mld_destroy_sta(struct ieee80211_sta *sta)
648d1e879ecSMiri Korenblit {
649d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
650d1e879ecSMiri Korenblit 
651d1e879ecSMiri Korenblit 	kfree(mld_sta->dup_data);
652d1e879ecSMiri Korenblit 	kfree(mld_sta->mpdu_counters);
653d1e879ecSMiri Korenblit }
654d1e879ecSMiri Korenblit 
655d1e879ecSMiri Korenblit static int
iwl_mld_alloc_dup_data(struct iwl_mld * mld,struct iwl_mld_sta * mld_sta)656d1e879ecSMiri Korenblit iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta)
657d1e879ecSMiri Korenblit {
658d1e879ecSMiri Korenblit 	struct iwl_mld_rxq_dup_data *dup_data;
659d1e879ecSMiri Korenblit 
660d1e879ecSMiri Korenblit 	if (mld->fw_status.in_hw_restart)
661d1e879ecSMiri Korenblit 		return 0;
662d1e879ecSMiri Korenblit 
663d1e879ecSMiri Korenblit 	dup_data = kcalloc(mld->trans->num_rx_queues, sizeof(*dup_data),
664d1e879ecSMiri Korenblit 			   GFP_KERNEL);
665d1e879ecSMiri Korenblit 	if (!dup_data)
666d1e879ecSMiri Korenblit 		return -ENOMEM;
667d1e879ecSMiri Korenblit 
668d1e879ecSMiri Korenblit 	/* Initialize all the last_seq values to 0xffff which can never
669d1e879ecSMiri Korenblit 	 * compare equal to the frame's seq_ctrl in the check in
670d1e879ecSMiri Korenblit 	 * iwl_mld_is_dup() since the lower 4 bits are the fragment
671d1e879ecSMiri Korenblit 	 * number and fragmented packets don't reach that function.
672d1e879ecSMiri Korenblit 	 *
673d1e879ecSMiri Korenblit 	 * This thus allows receiving a packet with seqno 0 and the
674d1e879ecSMiri Korenblit 	 * retry bit set as the very first packet on a new TID.
675d1e879ecSMiri Korenblit 	 */
676d1e879ecSMiri Korenblit 	for (int q = 0; q < mld->trans->num_rx_queues; q++)
677d1e879ecSMiri Korenblit 		memset(dup_data[q].last_seq, 0xff,
678d1e879ecSMiri Korenblit 		       sizeof(dup_data[q].last_seq));
679d1e879ecSMiri Korenblit 	mld_sta->dup_data = dup_data;
680d1e879ecSMiri Korenblit 
681d1e879ecSMiri Korenblit 	return 0;
682d1e879ecSMiri Korenblit }
683d1e879ecSMiri Korenblit 
iwl_mld_alloc_mpdu_counters(struct iwl_mld * mld,struct ieee80211_sta * sta)684d1e879ecSMiri Korenblit static void iwl_mld_alloc_mpdu_counters(struct iwl_mld *mld,
685d1e879ecSMiri Korenblit 					struct ieee80211_sta *sta)
686d1e879ecSMiri Korenblit {
687d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
688d1e879ecSMiri Korenblit 	struct ieee80211_vif *vif = mld_sta->vif;
689d1e879ecSMiri Korenblit 
690d1e879ecSMiri Korenblit 	if (mld->fw_status.in_hw_restart)
691d1e879ecSMiri Korenblit 		return;
692d1e879ecSMiri Korenblit 
693d1e879ecSMiri Korenblit 	/* MPDUs are counted only when EMLSR is possible */
694d1e879ecSMiri Korenblit 	if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION ||
695d1e879ecSMiri Korenblit 	    sta->tdls || !ieee80211_vif_is_mld(vif))
696d1e879ecSMiri Korenblit 		return;
697d1e879ecSMiri Korenblit 
698d1e879ecSMiri Korenblit 	mld_sta->mpdu_counters = kcalloc(mld->trans->num_rx_queues,
699d1e879ecSMiri Korenblit 					 sizeof(*mld_sta->mpdu_counters),
700d1e879ecSMiri Korenblit 					 GFP_KERNEL);
701d1e879ecSMiri Korenblit 	if (!mld_sta->mpdu_counters)
702d1e879ecSMiri Korenblit 		return;
703d1e879ecSMiri Korenblit 
704d1e879ecSMiri Korenblit 	for (int q = 0; q < mld->trans->num_rx_queues; q++)
705d1e879ecSMiri Korenblit 		spin_lock_init(&mld_sta->mpdu_counters[q].lock);
706d1e879ecSMiri Korenblit }
707d1e879ecSMiri Korenblit 
708d1e879ecSMiri Korenblit static int
iwl_mld_init_sta(struct iwl_mld * mld,struct ieee80211_sta * sta,struct ieee80211_vif * vif,enum iwl_fw_sta_type type)709d1e879ecSMiri Korenblit iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
710d1e879ecSMiri Korenblit 		 struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
711d1e879ecSMiri Korenblit {
712d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
713d1e879ecSMiri Korenblit 
714d1e879ecSMiri Korenblit 	mld_sta->vif = vif;
715d1e879ecSMiri Korenblit 	mld_sta->sta_type = type;
716d1e879ecSMiri Korenblit 	mld_sta->mld = mld;
717d1e879ecSMiri Korenblit 
718d1e879ecSMiri Korenblit 	if (!mld->fw_status.in_hw_restart)
719d1e879ecSMiri Korenblit 		for (int i = 0; i < ARRAY_SIZE(sta->txq); i++)
720d1e879ecSMiri Korenblit 			iwl_mld_init_txq(iwl_mld_txq_from_mac80211(sta->txq[i]));
721d1e879ecSMiri Korenblit 
722d1e879ecSMiri Korenblit 	iwl_mld_alloc_mpdu_counters(mld, sta);
723d1e879ecSMiri Korenblit 
724d1e879ecSMiri Korenblit 	iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant);
725d1e879ecSMiri Korenblit 
726d1e879ecSMiri Korenblit 	return iwl_mld_alloc_dup_data(mld, mld_sta);
727d1e879ecSMiri Korenblit }
728d1e879ecSMiri Korenblit 
iwl_mld_add_sta(struct iwl_mld * mld,struct ieee80211_sta * sta,struct ieee80211_vif * vif,enum iwl_fw_sta_type type)729d1e879ecSMiri Korenblit int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
730d1e879ecSMiri Korenblit 		    struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
731d1e879ecSMiri Korenblit {
732d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
733d1e879ecSMiri Korenblit 	struct ieee80211_link_sta *link_sta;
734d1e879ecSMiri Korenblit 	int link_id;
735d1e879ecSMiri Korenblit 	int ret;
736d1e879ecSMiri Korenblit 
737d1e879ecSMiri Korenblit 	ret = iwl_mld_init_sta(mld, sta, vif, type);
738d1e879ecSMiri Korenblit 	if (ret)
739d1e879ecSMiri Korenblit 		return ret;
740d1e879ecSMiri Korenblit 
741d1e879ecSMiri Korenblit 	/* We could have add only the deflink link_sta, but it will not work
742d1e879ecSMiri Korenblit 	 * in the restart case if the single link that is active during
743d1e879ecSMiri Korenblit 	 * reconfig is not the deflink one.
744d1e879ecSMiri Korenblit 	 */
745d1e879ecSMiri Korenblit 	for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
746d1e879ecSMiri Korenblit 		ret = iwl_mld_add_link_sta(mld, link_sta);
747d1e879ecSMiri Korenblit 		if (ret)
748d1e879ecSMiri Korenblit 			goto destroy_sta;
749d1e879ecSMiri Korenblit 	}
750d1e879ecSMiri Korenblit 
751d1e879ecSMiri Korenblit 	return 0;
752d1e879ecSMiri Korenblit 
753d1e879ecSMiri Korenblit destroy_sta:
754d1e879ecSMiri Korenblit 	iwl_mld_destroy_sta(sta);
755d1e879ecSMiri Korenblit 
756d1e879ecSMiri Korenblit 	return ret;
757d1e879ecSMiri Korenblit }
758d1e879ecSMiri Korenblit 
iwl_mld_flush_sta_txqs(struct iwl_mld * mld,struct ieee80211_sta * sta)759d1e879ecSMiri Korenblit void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta)
760d1e879ecSMiri Korenblit {
761d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
762d1e879ecSMiri Korenblit 	struct ieee80211_link_sta *link_sta;
763d1e879ecSMiri Korenblit 	int link_id;
764d1e879ecSMiri Korenblit 
765d1e879ecSMiri Korenblit 	for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
766d1e879ecSMiri Korenblit 		int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
767d1e879ecSMiri Korenblit 
768d1e879ecSMiri Korenblit 		if (fw_sta_id < 0)
769d1e879ecSMiri Korenblit 			continue;
770d1e879ecSMiri Korenblit 
771d1e879ecSMiri Korenblit 		iwl_mld_flush_link_sta_txqs(mld, fw_sta_id);
772d1e879ecSMiri Korenblit 	}
773d1e879ecSMiri Korenblit }
774d1e879ecSMiri Korenblit 
iwl_mld_wait_sta_txqs_empty(struct iwl_mld * mld,struct ieee80211_sta * sta)775d1e879ecSMiri Korenblit void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, struct ieee80211_sta *sta)
776d1e879ecSMiri Korenblit {
777d1e879ecSMiri Korenblit 	/* Avoid a warning in iwl_trans_wait_txq_empty if are anyway on the way
778d1e879ecSMiri Korenblit 	 * to a restart.
779d1e879ecSMiri Korenblit 	 */
780d1e879ecSMiri Korenblit 	if (iwl_mld_error_before_recovery(mld))
781d1e879ecSMiri Korenblit 		return;
782d1e879ecSMiri Korenblit 
783d1e879ecSMiri Korenblit 	for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) {
784d1e879ecSMiri Korenblit 		struct iwl_mld_txq *mld_txq =
785d1e879ecSMiri Korenblit 			iwl_mld_txq_from_mac80211(sta->txq[i]);
786d1e879ecSMiri Korenblit 
787d1e879ecSMiri Korenblit 		if (!mld_txq->status.allocated)
788d1e879ecSMiri Korenblit 			continue;
789d1e879ecSMiri Korenblit 
790d1e879ecSMiri Korenblit 		iwl_trans_wait_txq_empty(mld->trans, mld_txq->fw_id);
791d1e879ecSMiri Korenblit 	}
792d1e879ecSMiri Korenblit }
793d1e879ecSMiri Korenblit 
iwl_mld_remove_sta(struct iwl_mld * mld,struct ieee80211_sta * sta)794d1e879ecSMiri Korenblit void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta)
795d1e879ecSMiri Korenblit {
796d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
797d1e879ecSMiri Korenblit 	struct ieee80211_vif *vif = mld_sta->vif;
798d1e879ecSMiri Korenblit 	struct ieee80211_link_sta *link_sta;
799d1e879ecSMiri Korenblit 	u8 link_id;
800d1e879ecSMiri Korenblit 
801d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
802d1e879ecSMiri Korenblit 
803d1e879ecSMiri Korenblit 	/* Tell the HW to flush the queues */
804d1e879ecSMiri Korenblit 	iwl_mld_flush_sta_txqs(mld, sta);
805d1e879ecSMiri Korenblit 
806d1e879ecSMiri Korenblit 	/* Wait for trans to empty its queues */
807d1e879ecSMiri Korenblit 	iwl_mld_wait_sta_txqs_empty(mld, sta);
808d1e879ecSMiri Korenblit 
809d1e879ecSMiri Korenblit 	/* Now we can remove the queues */
810d1e879ecSMiri Korenblit 	for (int i = 0; i < ARRAY_SIZE(sta->txq); i++)
811d1e879ecSMiri Korenblit 		iwl_mld_remove_txq(mld, sta->txq[i]);
812d1e879ecSMiri Korenblit 
813d1e879ecSMiri Korenblit 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
814d1e879ecSMiri Korenblit 		/* Mac8011 will remove the groupwise keys after the sta is
815d1e879ecSMiri Korenblit 		 * removed, but FW expects all the keys to be removed before
816d1e879ecSMiri Korenblit 		 * the STA is, so remove them all here.
817d1e879ecSMiri Korenblit 		 */
81867128af0SJohannes Berg 		if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
819d1e879ecSMiri Korenblit 			iwl_mld_remove_ap_keys(mld, vif, sta, link_id);
820d1e879ecSMiri Korenblit 
821d1e879ecSMiri Korenblit 		/* Remove the link_sta */
822d1e879ecSMiri Korenblit 		iwl_mld_remove_link_sta(mld, link_sta);
823d1e879ecSMiri Korenblit 	}
824d1e879ecSMiri Korenblit 
825d1e879ecSMiri Korenblit 	iwl_mld_destroy_sta(sta);
826d1e879ecSMiri Korenblit }
827d1e879ecSMiri Korenblit 
iwl_mld_fw_sta_id_mask(struct iwl_mld * mld,struct ieee80211_sta * sta)828d1e879ecSMiri Korenblit u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta)
829d1e879ecSMiri Korenblit {
830d1e879ecSMiri Korenblit 	struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif;
831d1e879ecSMiri Korenblit 	struct ieee80211_link_sta *link_sta;
832d1e879ecSMiri Korenblit 	unsigned int link_id;
833d1e879ecSMiri Korenblit 	u32 result = 0;
834d1e879ecSMiri Korenblit 
835d1e879ecSMiri Korenblit 	KUNIT_STATIC_STUB_REDIRECT(iwl_mld_fw_sta_id_mask, mld, sta);
836d1e879ecSMiri Korenblit 
837d1e879ecSMiri Korenblit 	/* This function should only be used with the wiphy lock held,
838d1e879ecSMiri Korenblit 	 * In other cases, it is not guaranteed that the link_sta will exist
839d1e879ecSMiri Korenblit 	 * in the driver too, and it is checked in
840d1e879ecSMiri Korenblit 	 * iwl_mld_fw_sta_id_from_link_sta.
841d1e879ecSMiri Korenblit 	 */
842d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
843d1e879ecSMiri Korenblit 
844d1e879ecSMiri Korenblit 	for_each_sta_active_link(vif, sta, link_sta, link_id) {
845d1e879ecSMiri Korenblit 		int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
846d1e879ecSMiri Korenblit 
847d1e879ecSMiri Korenblit 		if (!(fw_id < 0))
848d1e879ecSMiri Korenblit 			result |= BIT(fw_id);
849d1e879ecSMiri Korenblit 	}
850d1e879ecSMiri Korenblit 
851d1e879ecSMiri Korenblit 	return result;
852d1e879ecSMiri Korenblit }
853d1e879ecSMiri Korenblit EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_fw_sta_id_mask);
854d1e879ecSMiri Korenblit 
iwl_mld_count_mpdu(struct ieee80211_link_sta * link_sta,int queue,u32 count,bool tx)855d1e879ecSMiri Korenblit static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue,
856d1e879ecSMiri Korenblit 			       u32 count, bool tx)
857d1e879ecSMiri Korenblit {
858d1e879ecSMiri Korenblit 	struct iwl_mld_per_q_mpdu_counter *queue_counter;
859d1e879ecSMiri Korenblit 	struct iwl_mld_per_link_mpdu_counter *link_counter;
860d1e879ecSMiri Korenblit 	struct iwl_mld_vif *mld_vif;
861d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta;
862d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link;
863d1e879ecSMiri Korenblit 	struct iwl_mld *mld;
864d1e879ecSMiri Korenblit 	int total_mpdus = 0;
865d1e879ecSMiri Korenblit 
866d1e879ecSMiri Korenblit 	if (WARN_ON(!link_sta))
867d1e879ecSMiri Korenblit 		return;
868d1e879ecSMiri Korenblit 
869d1e879ecSMiri Korenblit 	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
870d1e879ecSMiri Korenblit 	if (!mld_sta->mpdu_counters)
871d1e879ecSMiri Korenblit 		return;
872d1e879ecSMiri Korenblit 
873d1e879ecSMiri Korenblit 	mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
874d1e879ecSMiri Korenblit 	mld_link = iwl_mld_link_dereference_check(mld_vif, link_sta->link_id);
875d1e879ecSMiri Korenblit 
876d1e879ecSMiri Korenblit 	if (WARN_ON_ONCE(!mld_link))
877d1e879ecSMiri Korenblit 		return;
878d1e879ecSMiri Korenblit 
879d1e879ecSMiri Korenblit 	queue_counter = &mld_sta->mpdu_counters[queue];
880d1e879ecSMiri Korenblit 
881d1e879ecSMiri Korenblit 	mld = mld_vif->mld;
882d1e879ecSMiri Korenblit 
883d1e879ecSMiri Korenblit 	/* If it the window is over, first clear the counters.
884d1e879ecSMiri Korenblit 	 * When we are not blocked by TPT, the window is managed by check_tpt_wk
885d1e879ecSMiri Korenblit 	 */
886d1e879ecSMiri Korenblit 	if ((mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT) &&
887d1e879ecSMiri Korenblit 	    time_is_before_jiffies(queue_counter->window_start_time +
888d1e879ecSMiri Korenblit 					IWL_MLD_TPT_COUNT_WINDOW)) {
889d1e879ecSMiri Korenblit 		memset(queue_counter->per_link, 0,
890d1e879ecSMiri Korenblit 		       sizeof(queue_counter->per_link));
891d1e879ecSMiri Korenblit 		queue_counter->window_start_time = jiffies;
892d1e879ecSMiri Korenblit 
893d1e879ecSMiri Korenblit 		IWL_DEBUG_INFO(mld, "MPDU counters are cleared\n");
894d1e879ecSMiri Korenblit 	}
895d1e879ecSMiri Korenblit 
896d1e879ecSMiri Korenblit 	link_counter = &queue_counter->per_link[mld_link->fw_id];
897d1e879ecSMiri Korenblit 
898d1e879ecSMiri Korenblit 	spin_lock_bh(&queue_counter->lock);
899d1e879ecSMiri Korenblit 
900d1e879ecSMiri Korenblit 	/* Update the statistics for this TPT measurement window */
901d1e879ecSMiri Korenblit 	if (tx)
902d1e879ecSMiri Korenblit 		link_counter->tx += count;
903d1e879ecSMiri Korenblit 	else
904d1e879ecSMiri Korenblit 		link_counter->rx += count;
905d1e879ecSMiri Korenblit 
906d1e879ecSMiri Korenblit 	/*
907d1e879ecSMiri Korenblit 	 * Next, evaluate whether we should queue an unblock,
908d1e879ecSMiri Korenblit 	 * skip this if we are not blocked due to low throughput.
909d1e879ecSMiri Korenblit 	 */
910d1e879ecSMiri Korenblit 	if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
911d1e879ecSMiri Korenblit 		goto unlock;
912d1e879ecSMiri Korenblit 
913d1e879ecSMiri Korenblit 	for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++)
914d1e879ecSMiri Korenblit 		total_mpdus += tx ? queue_counter->per_link[i].tx :
915d1e879ecSMiri Korenblit 				    queue_counter->per_link[i].rx;
916d1e879ecSMiri Korenblit 
917d1e879ecSMiri Korenblit 	/* Unblock is already queued if the threshold was reached before */
918d1e879ecSMiri Korenblit 	if (total_mpdus - count >= IWL_MLD_ENTER_EMLSR_TPT_THRESH)
919d1e879ecSMiri Korenblit 		goto unlock;
920d1e879ecSMiri Korenblit 
921d1e879ecSMiri Korenblit 	if (total_mpdus >= IWL_MLD_ENTER_EMLSR_TPT_THRESH)
922d1e879ecSMiri Korenblit 		wiphy_work_queue(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk);
923d1e879ecSMiri Korenblit 
924d1e879ecSMiri Korenblit unlock:
925d1e879ecSMiri Korenblit 	spin_unlock_bh(&queue_counter->lock);
926d1e879ecSMiri Korenblit }
927d1e879ecSMiri Korenblit 
928d1e879ecSMiri Korenblit /* must be called under rcu_read_lock() */
iwl_mld_count_mpdu_rx(struct ieee80211_link_sta * link_sta,int queue,u32 count)929d1e879ecSMiri Korenblit void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue,
930d1e879ecSMiri Korenblit 			   u32 count)
931d1e879ecSMiri Korenblit {
932d1e879ecSMiri Korenblit 	iwl_mld_count_mpdu(link_sta, queue, count, false);
933d1e879ecSMiri Korenblit }
934d1e879ecSMiri Korenblit 
935d1e879ecSMiri Korenblit /* must be called under rcu_read_lock() */
iwl_mld_count_mpdu_tx(struct ieee80211_link_sta * link_sta,u32 count)936d1e879ecSMiri Korenblit void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count)
937d1e879ecSMiri Korenblit {
938d1e879ecSMiri Korenblit 	/* use queue 0 for all TX */
939d1e879ecSMiri Korenblit 	iwl_mld_count_mpdu(link_sta, 0, count, true);
940d1e879ecSMiri Korenblit }
941d1e879ecSMiri Korenblit 
iwl_mld_allocate_internal_txq(struct iwl_mld * mld,struct iwl_mld_int_sta * internal_sta,u8 tid)942d1e879ecSMiri Korenblit static int iwl_mld_allocate_internal_txq(struct iwl_mld *mld,
943d1e879ecSMiri Korenblit 					 struct iwl_mld_int_sta *internal_sta,
944d1e879ecSMiri Korenblit 					 u8 tid)
945d1e879ecSMiri Korenblit {
946d1e879ecSMiri Korenblit 	u32 sta_mask = BIT(internal_sta->sta_id);
947d1e879ecSMiri Korenblit 	int queue, size;
948d1e879ecSMiri Korenblit 
949d1e879ecSMiri Korenblit 	size = max_t(u32, IWL_MGMT_QUEUE_SIZE,
950d1e879ecSMiri Korenblit 		     mld->trans->cfg->min_txq_size);
951d1e879ecSMiri Korenblit 
952d1e879ecSMiri Korenblit 	queue = iwl_trans_txq_alloc(mld->trans, 0, sta_mask, tid, size,
953d1e879ecSMiri Korenblit 				    IWL_WATCHDOG_DISABLED);
954d1e879ecSMiri Korenblit 
955d1e879ecSMiri Korenblit 	if (queue >= 0)
956d1e879ecSMiri Korenblit 		IWL_DEBUG_TX_QUEUES(mld,
957d1e879ecSMiri Korenblit 				    "Enabling TXQ #%d for sta mask 0x%x tid %d\n",
958d1e879ecSMiri Korenblit 				    queue, sta_mask, tid);
959d1e879ecSMiri Korenblit 	return queue;
960d1e879ecSMiri Korenblit }
961d1e879ecSMiri Korenblit 
iwl_mld_send_aux_sta_cmd(struct iwl_mld * mld,const struct iwl_mld_int_sta * internal_sta)962d1e879ecSMiri Korenblit static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld,
963d1e879ecSMiri Korenblit 				    const struct iwl_mld_int_sta *internal_sta)
964d1e879ecSMiri Korenblit {
965d1e879ecSMiri Korenblit 	struct iwl_aux_sta_cmd cmd = {
966d1e879ecSMiri Korenblit 		.sta_id = cpu_to_le32(internal_sta->sta_id),
967d1e879ecSMiri Korenblit 		/* TODO: CDB - properly set the lmac_id */
968d1e879ecSMiri Korenblit 		.lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX),
969d1e879ecSMiri Korenblit 	};
970d1e879ecSMiri Korenblit 
971d1e879ecSMiri Korenblit 	return iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD),
972d1e879ecSMiri Korenblit 				    &cmd);
973d1e879ecSMiri Korenblit }
974d1e879ecSMiri Korenblit 
975d1e879ecSMiri Korenblit static int
iwl_mld_add_internal_sta_to_fw(struct iwl_mld * mld,const struct iwl_mld_int_sta * internal_sta,u8 fw_link_id,const u8 * addr)976d1e879ecSMiri Korenblit iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
977d1e879ecSMiri Korenblit 			       const struct iwl_mld_int_sta *internal_sta,
978d1e879ecSMiri Korenblit 			       u8 fw_link_id,
979d1e879ecSMiri Korenblit 			       const u8 *addr)
980d1e879ecSMiri Korenblit {
981d1e879ecSMiri Korenblit 	struct iwl_sta_cfg_cmd cmd = {};
982d1e879ecSMiri Korenblit 
983d1e879ecSMiri Korenblit 	if (internal_sta->sta_type == STATION_TYPE_AUX)
984d1e879ecSMiri Korenblit 		return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
985d1e879ecSMiri Korenblit 
986d1e879ecSMiri Korenblit 	cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
987d1e879ecSMiri Korenblit 	cmd.link_id = cpu_to_le32(fw_link_id);
988d1e879ecSMiri Korenblit 	cmd.station_type = cpu_to_le32(internal_sta->sta_type);
989d1e879ecSMiri Korenblit 
990d1e879ecSMiri Korenblit 	/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
991d1e879ecSMiri Korenblit 	 * On the other hand, FW will never check this flag during RX since
992d1e879ecSMiri Korenblit 	 * an AP/GO doesn't receive protected broadcast management frames.
993d1e879ecSMiri Korenblit 	 * So, we can set it unconditionally.
994d1e879ecSMiri Korenblit 	 */
995d1e879ecSMiri Korenblit 	if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT)
996d1e879ecSMiri Korenblit 		cmd.mfp = cpu_to_le32(1);
997d1e879ecSMiri Korenblit 
998d1e879ecSMiri Korenblit 	if (addr) {
999d1e879ecSMiri Korenblit 		memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
1000d1e879ecSMiri Korenblit 		memcpy(cmd.peer_link_address, addr, ETH_ALEN);
1001d1e879ecSMiri Korenblit 	}
1002d1e879ecSMiri Korenblit 
1003d1e879ecSMiri Korenblit 	return iwl_mld_send_sta_cmd(mld, &cmd);
1004d1e879ecSMiri Korenblit }
1005d1e879ecSMiri Korenblit 
iwl_mld_add_internal_sta(struct iwl_mld * mld,struct iwl_mld_int_sta * internal_sta,enum iwl_fw_sta_type sta_type,u8 fw_link_id,const u8 * addr,u8 tid)1006d1e879ecSMiri Korenblit static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
1007d1e879ecSMiri Korenblit 				    struct iwl_mld_int_sta *internal_sta,
1008d1e879ecSMiri Korenblit 				    enum iwl_fw_sta_type sta_type,
1009d1e879ecSMiri Korenblit 				    u8 fw_link_id, const u8 *addr, u8 tid)
1010d1e879ecSMiri Korenblit {
1011d1e879ecSMiri Korenblit 	int ret, queue_id;
1012d1e879ecSMiri Korenblit 
1013d1e879ecSMiri Korenblit 	ret = iwl_mld_allocate_link_sta_fw_id(mld,
1014d1e879ecSMiri Korenblit 					      &internal_sta->sta_id,
1015d1e879ecSMiri Korenblit 					      ERR_PTR(-EINVAL));
1016d1e879ecSMiri Korenblit 	if (ret)
1017d1e879ecSMiri Korenblit 		return ret;
1018d1e879ecSMiri Korenblit 
1019d1e879ecSMiri Korenblit 	internal_sta->sta_type = sta_type;
1020d1e879ecSMiri Korenblit 
1021d1e879ecSMiri Korenblit 	ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id,
1022d1e879ecSMiri Korenblit 					     addr);
1023d1e879ecSMiri Korenblit 	if (ret)
1024d1e879ecSMiri Korenblit 		goto err;
1025d1e879ecSMiri Korenblit 
1026d1e879ecSMiri Korenblit 	queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid);
1027d1e879ecSMiri Korenblit 	if (queue_id < 0) {
1028d1e879ecSMiri Korenblit 		iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
1029d1e879ecSMiri Korenblit 		ret = queue_id;
1030d1e879ecSMiri Korenblit 		goto err;
1031d1e879ecSMiri Korenblit 	}
1032d1e879ecSMiri Korenblit 
1033d1e879ecSMiri Korenblit 	internal_sta->queue_id = queue_id;
1034d1e879ecSMiri Korenblit 
1035d1e879ecSMiri Korenblit 	return 0;
1036d1e879ecSMiri Korenblit err:
1037d1e879ecSMiri Korenblit 	iwl_mld_free_internal_sta(mld, internal_sta);
1038d1e879ecSMiri Korenblit 	return ret;
1039d1e879ecSMiri Korenblit }
1040d1e879ecSMiri Korenblit 
iwl_mld_add_bcast_sta(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)1041d1e879ecSMiri Korenblit int iwl_mld_add_bcast_sta(struct iwl_mld *mld,
1042d1e879ecSMiri Korenblit 			  struct ieee80211_vif *vif,
1043d1e879ecSMiri Korenblit 			  struct ieee80211_bss_conf *link)
1044d1e879ecSMiri Korenblit {
1045d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
1046d1e879ecSMiri Korenblit 	const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1047d1e879ecSMiri Korenblit 	const u8 *addr;
1048d1e879ecSMiri Korenblit 
1049d1e879ecSMiri Korenblit 	if (WARN_ON(!mld_link))
1050d1e879ecSMiri Korenblit 		return -EINVAL;
1051d1e879ecSMiri Korenblit 
1052d1e879ecSMiri Korenblit 	if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
1053d1e879ecSMiri Korenblit 		    vif->type != NL80211_IFTYPE_ADHOC))
1054d1e879ecSMiri Korenblit 		return -EINVAL;
1055d1e879ecSMiri Korenblit 
1056d1e879ecSMiri Korenblit 	addr = vif->type == NL80211_IFTYPE_ADHOC ? link->bssid : bcast_addr;
1057d1e879ecSMiri Korenblit 
1058d1e879ecSMiri Korenblit 	return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta,
1059d1e879ecSMiri Korenblit 					STATION_TYPE_BCAST_MGMT,
1060d1e879ecSMiri Korenblit 					mld_link->fw_id, addr,
1061d1e879ecSMiri Korenblit 					IWL_MGMT_TID);
1062d1e879ecSMiri Korenblit }
1063d1e879ecSMiri Korenblit 
iwl_mld_add_mcast_sta(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)1064d1e879ecSMiri Korenblit int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
1065d1e879ecSMiri Korenblit 			  struct ieee80211_vif *vif,
1066d1e879ecSMiri Korenblit 			  struct ieee80211_bss_conf *link)
1067d1e879ecSMiri Korenblit {
1068d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
1069d1e879ecSMiri Korenblit 	const u8 mcast_addr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
1070d1e879ecSMiri Korenblit 
1071d1e879ecSMiri Korenblit 	if (WARN_ON(!mld_link))
1072d1e879ecSMiri Korenblit 		return -EINVAL;
1073d1e879ecSMiri Korenblit 
1074d1e879ecSMiri Korenblit 	if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
1075d1e879ecSMiri Korenblit 		    vif->type != NL80211_IFTYPE_ADHOC))
1076d1e879ecSMiri Korenblit 		return -EINVAL;
1077d1e879ecSMiri Korenblit 
1078d1e879ecSMiri Korenblit 	return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta,
1079d1e879ecSMiri Korenblit 					STATION_TYPE_MCAST,
1080d1e879ecSMiri Korenblit 					mld_link->fw_id, mcast_addr, 0);
1081d1e879ecSMiri Korenblit }
1082d1e879ecSMiri Korenblit 
iwl_mld_add_aux_sta(struct iwl_mld * mld,struct iwl_mld_int_sta * internal_sta)1083d1e879ecSMiri Korenblit int iwl_mld_add_aux_sta(struct iwl_mld *mld,
1084d1e879ecSMiri Korenblit 			struct iwl_mld_int_sta *internal_sta)
1085d1e879ecSMiri Korenblit {
1086d1e879ecSMiri Korenblit 	return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX,
1087d1e879ecSMiri Korenblit 					0, NULL, IWL_MAX_TID_COUNT);
1088d1e879ecSMiri Korenblit }
1089d1e879ecSMiri Korenblit 
iwl_mld_remove_internal_sta(struct iwl_mld * mld,struct iwl_mld_int_sta * internal_sta,bool flush,u8 tid)1090d1e879ecSMiri Korenblit static void iwl_mld_remove_internal_sta(struct iwl_mld *mld,
1091d1e879ecSMiri Korenblit 					struct iwl_mld_int_sta *internal_sta,
1092d1e879ecSMiri Korenblit 					bool flush, u8 tid)
1093d1e879ecSMiri Korenblit {
1094d1e879ecSMiri Korenblit 	if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA ||
1095d1e879ecSMiri Korenblit 			 internal_sta->queue_id == IWL_MLD_INVALID_QUEUE))
1096d1e879ecSMiri Korenblit 		return;
1097d1e879ecSMiri Korenblit 
1098d1e879ecSMiri Korenblit 	if (flush)
1099d1e879ecSMiri Korenblit 		iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id);
1100d1e879ecSMiri Korenblit 
1101d1e879ecSMiri Korenblit 	iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
1102d1e879ecSMiri Korenblit 			 tid, internal_sta->queue_id);
1103d1e879ecSMiri Korenblit 
1104d1e879ecSMiri Korenblit 	iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
1105d1e879ecSMiri Korenblit 
1106d1e879ecSMiri Korenblit 	iwl_mld_free_internal_sta(mld, internal_sta);
1107d1e879ecSMiri Korenblit }
1108d1e879ecSMiri Korenblit 
iwl_mld_remove_bcast_sta(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)1109d1e879ecSMiri Korenblit void iwl_mld_remove_bcast_sta(struct iwl_mld *mld,
1110d1e879ecSMiri Korenblit 			      struct ieee80211_vif *vif,
1111d1e879ecSMiri Korenblit 			      struct ieee80211_bss_conf *link)
1112d1e879ecSMiri Korenblit {
1113d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
1114d1e879ecSMiri Korenblit 
1115d1e879ecSMiri Korenblit 	if (WARN_ON(!mld_link))
1116d1e879ecSMiri Korenblit 		return;
1117d1e879ecSMiri Korenblit 
1118d1e879ecSMiri Korenblit 	if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
1119d1e879ecSMiri Korenblit 		    vif->type != NL80211_IFTYPE_ADHOC))
1120d1e879ecSMiri Korenblit 		return;
1121d1e879ecSMiri Korenblit 
1122d1e879ecSMiri Korenblit 	iwl_mld_remove_internal_sta(mld, &mld_link->bcast_sta, true,
1123d1e879ecSMiri Korenblit 				    IWL_MGMT_TID);
1124d1e879ecSMiri Korenblit }
1125d1e879ecSMiri Korenblit 
iwl_mld_remove_mcast_sta(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)1126d1e879ecSMiri Korenblit void iwl_mld_remove_mcast_sta(struct iwl_mld *mld,
1127d1e879ecSMiri Korenblit 			      struct ieee80211_vif *vif,
1128d1e879ecSMiri Korenblit 			      struct ieee80211_bss_conf *link)
1129d1e879ecSMiri Korenblit {
1130d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
1131d1e879ecSMiri Korenblit 
1132d1e879ecSMiri Korenblit 	if (WARN_ON(!mld_link))
1133d1e879ecSMiri Korenblit 		return;
1134d1e879ecSMiri Korenblit 
1135d1e879ecSMiri Korenblit 	if (WARN_ON(vif->type != NL80211_IFTYPE_AP &&
1136d1e879ecSMiri Korenblit 		    vif->type != NL80211_IFTYPE_ADHOC))
1137d1e879ecSMiri Korenblit 		return;
1138d1e879ecSMiri Korenblit 
1139d1e879ecSMiri Korenblit 	iwl_mld_remove_internal_sta(mld, &mld_link->mcast_sta, true, 0);
1140d1e879ecSMiri Korenblit }
1141d1e879ecSMiri Korenblit 
iwl_mld_remove_aux_sta(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)1142d1e879ecSMiri Korenblit void iwl_mld_remove_aux_sta(struct iwl_mld *mld,
1143d1e879ecSMiri Korenblit 			    struct ieee80211_vif *vif,
1144d1e879ecSMiri Korenblit 			    struct ieee80211_bss_conf *link)
1145d1e879ecSMiri Korenblit {
1146d1e879ecSMiri Korenblit 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
1147d1e879ecSMiri Korenblit 
1148d1e879ecSMiri Korenblit 	if (WARN_ON(!mld_link))
1149d1e879ecSMiri Korenblit 		return;
1150d1e879ecSMiri Korenblit 
1151d1e879ecSMiri Korenblit 	/* TODO: Hotspot 2.0 */
1152d1e879ecSMiri Korenblit 	if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE))
1153d1e879ecSMiri Korenblit 		return;
1154d1e879ecSMiri Korenblit 
1155d1e879ecSMiri Korenblit 	iwl_mld_remove_internal_sta(mld, &mld_link->aux_sta, false,
1156d1e879ecSMiri Korenblit 				    IWL_MAX_TID_COUNT);
1157d1e879ecSMiri Korenblit }
1158d1e879ecSMiri Korenblit 
iwl_mld_update_sta_resources(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u32 old_sta_mask,u32 new_sta_mask)1159d1e879ecSMiri Korenblit static int iwl_mld_update_sta_resources(struct iwl_mld *mld,
1160d1e879ecSMiri Korenblit 					struct ieee80211_vif *vif,
1161d1e879ecSMiri Korenblit 					struct ieee80211_sta *sta,
1162d1e879ecSMiri Korenblit 					u32 old_sta_mask,
1163d1e879ecSMiri Korenblit 					u32 new_sta_mask)
1164d1e879ecSMiri Korenblit {
1165d1e879ecSMiri Korenblit 	int ret;
1166d1e879ecSMiri Korenblit 
1167d1e879ecSMiri Korenblit 	ret = iwl_mld_update_sta_txqs(mld, sta, old_sta_mask, new_sta_mask);
1168d1e879ecSMiri Korenblit 	if (ret)
1169d1e879ecSMiri Korenblit 		return ret;
1170d1e879ecSMiri Korenblit 
1171d1e879ecSMiri Korenblit 	ret = iwl_mld_update_sta_keys(mld, vif, sta, old_sta_mask, new_sta_mask);
1172d1e879ecSMiri Korenblit 	if (ret)
1173d1e879ecSMiri Korenblit 		return ret;
1174d1e879ecSMiri Korenblit 
1175d1e879ecSMiri Korenblit 	return iwl_mld_update_sta_baids(mld, old_sta_mask, new_sta_mask);
1176d1e879ecSMiri Korenblit }
1177d1e879ecSMiri Korenblit 
iwl_mld_update_link_stas(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u16 old_links,u16 new_links)1178d1e879ecSMiri Korenblit int iwl_mld_update_link_stas(struct iwl_mld *mld,
1179d1e879ecSMiri Korenblit 			     struct ieee80211_vif *vif,
1180d1e879ecSMiri Korenblit 			     struct ieee80211_sta *sta,
1181d1e879ecSMiri Korenblit 			     u16 old_links, u16 new_links)
1182d1e879ecSMiri Korenblit {
1183d1e879ecSMiri Korenblit 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
1184d1e879ecSMiri Korenblit 	struct iwl_mld_link_sta *mld_link_sta;
1185d1e879ecSMiri Korenblit 	unsigned long links_to_add = ~old_links & new_links;
1186d1e879ecSMiri Korenblit 	unsigned long links_to_rem = old_links & ~new_links;
1187d1e879ecSMiri Korenblit 	unsigned long old_links_long = old_links;
1188d1e879ecSMiri Korenblit 	unsigned long sta_mask_added = 0;
1189d1e879ecSMiri Korenblit 	u32 current_sta_mask = 0, sta_mask_to_rem = 0;
1190d1e879ecSMiri Korenblit 	unsigned int link_id, sta_id;
1191d1e879ecSMiri Korenblit 	int ret;
1192d1e879ecSMiri Korenblit 
1193d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
1194d1e879ecSMiri Korenblit 
1195d1e879ecSMiri Korenblit 	for_each_set_bit(link_id, &old_links_long,
1196d1e879ecSMiri Korenblit 			 IEEE80211_MLD_MAX_NUM_LINKS) {
1197d1e879ecSMiri Korenblit 		mld_link_sta =
1198d1e879ecSMiri Korenblit 			iwl_mld_link_sta_dereference_check(mld_sta, link_id);
1199d1e879ecSMiri Korenblit 
1200d1e879ecSMiri Korenblit 		if (WARN_ON(!mld_link_sta))
1201d1e879ecSMiri Korenblit 			return -EINVAL;
1202d1e879ecSMiri Korenblit 
1203d1e879ecSMiri Korenblit 		current_sta_mask |= BIT(mld_link_sta->fw_id);
1204d1e879ecSMiri Korenblit 		if (links_to_rem & BIT(link_id))
1205d1e879ecSMiri Korenblit 			sta_mask_to_rem |= BIT(mld_link_sta->fw_id);
1206d1e879ecSMiri Korenblit 	}
1207d1e879ecSMiri Korenblit 
1208d1e879ecSMiri Korenblit 	if (sta_mask_to_rem) {
1209d1e879ecSMiri Korenblit 		ret = iwl_mld_update_sta_resources(mld, vif, sta,
1210d1e879ecSMiri Korenblit 						   current_sta_mask,
1211d1e879ecSMiri Korenblit 						   current_sta_mask &
1212d1e879ecSMiri Korenblit 							~sta_mask_to_rem);
1213d1e879ecSMiri Korenblit 		if (ret)
1214d1e879ecSMiri Korenblit 			return ret;
1215d1e879ecSMiri Korenblit 
1216d1e879ecSMiri Korenblit 		current_sta_mask &= ~sta_mask_to_rem;
1217d1e879ecSMiri Korenblit 	}
1218d1e879ecSMiri Korenblit 
1219d1e879ecSMiri Korenblit 	for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
1220d1e879ecSMiri Korenblit 		struct ieee80211_link_sta *link_sta =
1221d1e879ecSMiri Korenblit 			link_sta_dereference_protected(sta, link_id);
1222d1e879ecSMiri Korenblit 
1223d1e879ecSMiri Korenblit 		if (WARN_ON(!link_sta))
1224d1e879ecSMiri Korenblit 			return -EINVAL;
1225d1e879ecSMiri Korenblit 
1226d1e879ecSMiri Korenblit 		iwl_mld_remove_link_sta(mld, link_sta);
1227d1e879ecSMiri Korenblit 	}
1228d1e879ecSMiri Korenblit 
1229d1e879ecSMiri Korenblit 	for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
1230d1e879ecSMiri Korenblit 		struct ieee80211_link_sta *link_sta =
1231d1e879ecSMiri Korenblit 			link_sta_dereference_protected(sta, link_id);
1232d1e879ecSMiri Korenblit 		struct ieee80211_bss_conf *link;
1233d1e879ecSMiri Korenblit 
1234d1e879ecSMiri Korenblit 		if (WARN_ON(!link_sta))
1235d1e879ecSMiri Korenblit 			return -EINVAL;
1236d1e879ecSMiri Korenblit 
1237d1e879ecSMiri Korenblit 		ret = iwl_mld_add_link_sta(mld, link_sta);
1238d1e879ecSMiri Korenblit 		if (ret)
1239d1e879ecSMiri Korenblit 			goto remove_added_link_stas;
1240d1e879ecSMiri Korenblit 
1241d1e879ecSMiri Korenblit 		mld_link_sta =
1242d1e879ecSMiri Korenblit 			iwl_mld_link_sta_dereference_check(mld_sta,
1243d1e879ecSMiri Korenblit 							   link_id);
1244d1e879ecSMiri Korenblit 
1245d1e879ecSMiri Korenblit 		link = link_conf_dereference_protected(mld_sta->vif,
1246d1e879ecSMiri Korenblit 						       link_sta->link_id);
1247*36b79cb0SIlan Peer 
1248*36b79cb0SIlan Peer 		iwl_mld_set_max_amsdu_len(mld, link_sta);
1249d1e879ecSMiri Korenblit 		iwl_mld_config_tlc_link(mld, vif, link, link_sta);
1250d1e879ecSMiri Korenblit 
1251d1e879ecSMiri Korenblit 		sta_mask_added |= BIT(mld_link_sta->fw_id);
1252d1e879ecSMiri Korenblit 	}
1253d1e879ecSMiri Korenblit 
1254d1e879ecSMiri Korenblit 	if (sta_mask_added) {
1255d1e879ecSMiri Korenblit 		ret = iwl_mld_update_sta_resources(mld, vif, sta,
1256d1e879ecSMiri Korenblit 						   current_sta_mask,
1257d1e879ecSMiri Korenblit 						   current_sta_mask |
1258d1e879ecSMiri Korenblit 							sta_mask_added);
1259d1e879ecSMiri Korenblit 		if (ret)
1260d1e879ecSMiri Korenblit 			goto remove_added_link_stas;
1261d1e879ecSMiri Korenblit 	}
1262d1e879ecSMiri Korenblit 
1263d1e879ecSMiri Korenblit 	/* We couldn't activate the links before it has a STA. Now we can */
1264d1e879ecSMiri Korenblit 	for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
1265d1e879ecSMiri Korenblit 		struct ieee80211_bss_conf *link =
1266d1e879ecSMiri Korenblit 			link_conf_dereference_protected(mld_sta->vif, link_id);
1267d1e879ecSMiri Korenblit 
1268d1e879ecSMiri Korenblit 		if (WARN_ON(!link))
1269d1e879ecSMiri Korenblit 			continue;
1270d1e879ecSMiri Korenblit 
1271d1e879ecSMiri Korenblit 		iwl_mld_activate_link(mld, link);
1272d1e879ecSMiri Korenblit 	}
1273d1e879ecSMiri Korenblit 
1274d1e879ecSMiri Korenblit 	return 0;
1275d1e879ecSMiri Korenblit 
1276d1e879ecSMiri Korenblit remove_added_link_stas:
1277d1e879ecSMiri Korenblit 	for_each_set_bit(sta_id, &sta_mask_added, mld->fw->ucode_capa.num_stations) {
1278d1e879ecSMiri Korenblit 		struct ieee80211_link_sta *link_sta =
1279d1e879ecSMiri Korenblit 			wiphy_dereference(mld->wiphy,
1280d1e879ecSMiri Korenblit 					  mld->fw_id_to_link_sta[sta_id]);
1281d1e879ecSMiri Korenblit 
1282d1e879ecSMiri Korenblit 		if (WARN_ON(!link_sta))
1283d1e879ecSMiri Korenblit 			continue;
1284d1e879ecSMiri Korenblit 
1285d1e879ecSMiri Korenblit 		iwl_mld_remove_link_sta(mld, link_sta);
1286d1e879ecSMiri Korenblit 	}
1287d1e879ecSMiri Korenblit 
1288d1e879ecSMiri Korenblit 	return ret;
1289d1e879ecSMiri Korenblit }
1290