xref: /linux/drivers/net/wireless/intel/iwlwifi/mvm/link.c (revision cbc7afffc5ec581d3781c49fe9c8e8c661e5217b)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2022 - 2024 Intel Corporation
4  */
5 #include "mvm.h"
6 #include "time-event.h"
7 
8 static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm,
9 				       struct iwl_mvm_vif *mvm_vif)
10 {
11 	u32 link_id;
12 
13 	lockdep_assert_held(&mvm->mutex);
14 
15 	link_id = ffz(mvm->fw_link_ids_map);
16 
17 	/* this case can happen if there're deactivated but not removed links */
18 	if (link_id > IWL_MVM_FW_MAX_LINK_ID)
19 		return IWL_MVM_FW_LINK_ID_INVALID;
20 
21 	mvm->fw_link_ids_map |= BIT(link_id);
22 	return link_id;
23 }
24 
25 static void iwl_mvm_release_fw_link_id(struct iwl_mvm *mvm, u32 link_id)
26 {
27 	lockdep_assert_held(&mvm->mutex);
28 
29 	if (!WARN_ON(link_id > IWL_MVM_FW_MAX_LINK_ID))
30 		mvm->fw_link_ids_map &= ~BIT(link_id);
31 }
32 
33 static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm,
34 				 struct iwl_link_config_cmd *cmd,
35 				 enum iwl_ctxt_action action)
36 {
37 	int ret;
38 
39 	cmd->action = cpu_to_le32(action);
40 	ret = iwl_mvm_send_cmd_pdu(mvm,
41 				   WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0,
42 				   sizeof(*cmd), cmd);
43 	if (ret)
44 		IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
45 			action, ret);
46 	return ret;
47 }
48 
49 int iwl_mvm_set_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
50 			     struct ieee80211_bss_conf *link_conf)
51 {
52 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
53 	struct iwl_mvm_vif_link_info *link_info =
54 		mvmvif->link[link_conf->link_id];
55 
56 	if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
57 		link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
58 								    mvmvif);
59 		if (link_info->fw_link_id >=
60 		    ARRAY_SIZE(mvm->link_id_to_link_conf))
61 			return -EINVAL;
62 
63 		rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id],
64 				   link_conf);
65 	}
66 
67 	return 0;
68 }
69 
70 int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
71 		     struct ieee80211_bss_conf *link_conf)
72 {
73 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
74 	unsigned int link_id = link_conf->link_id;
75 	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
76 	struct iwl_link_config_cmd cmd = {};
77 	unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
78 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
79 	int ret;
80 
81 	if (WARN_ON_ONCE(!link_info))
82 		return -EINVAL;
83 
84 	ret = iwl_mvm_set_link_mapping(mvm, vif, link_conf);
85 	if (ret)
86 		return ret;
87 
88 	/* Update SF - Disable if needed. if this fails, SF might still be on
89 	 * while many macs are bound, which is forbidden - so fail the binding.
90 	 */
91 	if (iwl_mvm_sf_update(mvm, vif, false))
92 		return -EINVAL;
93 
94 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
95 	cmd.mac_id = cpu_to_le32(mvmvif->id);
96 	cmd.spec_link_id = link_conf->link_id;
97 	WARN_ON_ONCE(link_info->phy_ctxt);
98 	cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
99 
100 	memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
101 
102 	if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
103 		memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
104 
105 	if (cmd_ver < 2)
106 		cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
107 
108 	return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
109 }
110 
111 int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
112 			 struct ieee80211_bss_conf *link_conf,
113 			 u32 changes, bool active)
114 {
115 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
116 	unsigned int link_id = link_conf->link_id;
117 	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
118 	struct iwl_mvm_phy_ctxt *phyctxt;
119 	struct iwl_link_config_cmd cmd = {};
120 	u32 ht_flag, flags = 0, flags_mask = 0;
121 	int ret;
122 	unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
123 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
124 
125 	if (WARN_ON_ONCE(!link_info ||
126 			 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
127 		return -EINVAL;
128 
129 	if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
130 		/* When activating a link, phy context should be valid;
131 		 * when deactivating a link, it also should be valid since
132 		 * the link was active before. So, do nothing in this case.
133 		 * Since a link is added first with FW_CTXT_INVALID, then we
134 		 * can get here in case it's removed before it was activated.
135 		 */
136 		if (!link_info->phy_ctxt)
137 			return 0;
138 
139 		/* Catch early if driver tries to activate or deactivate a link
140 		 * twice.
141 		 */
142 		WARN_ON_ONCE(active == link_info->active);
143 
144 		/* When deactivating a link session protection should
145 		 * be stopped
146 		 */
147 		if (!active && vif->type == NL80211_IFTYPE_STATION)
148 			iwl_mvm_stop_session_protection(mvm, vif);
149 	}
150 
151 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
152 
153 	/* The phy_id, link address and listen_lmac can be modified only until
154 	 * the link becomes active, otherwise they will be ignored.
155 	 */
156 	phyctxt = link_info->phy_ctxt;
157 	if (phyctxt)
158 		cmd.phy_id = cpu_to_le32(phyctxt->id);
159 	else
160 		cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
161 	cmd.mac_id = cpu_to_le32(mvmvif->id);
162 
163 	memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
164 
165 	cmd.active = cpu_to_le32(active);
166 
167 	if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
168 		memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
169 
170 	iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf,
171 				   &cmd.cck_rates, &cmd.ofdm_rates);
172 
173 	cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble);
174 	cmd.short_slot = cpu_to_le32(link_conf->use_short_slot);
175 
176 	/* The fw does not distinguish between ht and fat */
177 	ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
178 	iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
179 					&cmd.protection_flags,
180 					ht_flag, LINK_PROT_FLG_TGG_PROTECT);
181 
182 	iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac,
183 				  &cmd.qos_flags);
184 
185 
186 	cmd.bi = cpu_to_le32(link_conf->beacon_int);
187 	cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int *
188 					link_conf->dtim_period);
189 
190 	if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax ||
191 	    (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
192 		changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
193 		goto send_cmd;
194 	}
195 
196 	cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext;
197 
198 	if (link_conf->uora_exists) {
199 		cmd.rand_alloc_ecwmin =
200 			link_conf->uora_ocw_range & 0x7;
201 		cmd.rand_alloc_ecwmax =
202 			(link_conf->uora_ocw_range >> 3) & 0x7;
203 	}
204 
205 	/* TODO  how to set ndp_fdbk_buff_th_exp? */
206 
207 	if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id],
208 					  &cmd.trig_based_txf[0])) {
209 		flags |= LINK_FLG_MU_EDCA_CW;
210 		flags_mask |= LINK_FLG_MU_EDCA_CW;
211 	}
212 
213 	if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) {
214 		struct ieee80211_chanctx_conf *ctx;
215 		struct cfg80211_chan_def *def = NULL;
216 
217 		rcu_read_lock();
218 		ctx = rcu_dereference(link_conf->chanctx_conf);
219 		if (ctx)
220 			def = iwl_mvm_chanctx_def(mvm, ctx);
221 
222 		if (iwlwifi_mod_params.disable_11be ||
223 		    !link_conf->eht_support || !def ||
224 		    iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) >= 6)
225 			changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
226 		else
227 			cmd.puncture_mask = cpu_to_le16(def->punctured);
228 		rcu_read_unlock();
229 	}
230 
231 	cmd.bss_color = link_conf->he_bss_color.color;
232 
233 	if (!link_conf->he_bss_color.enabled) {
234 		flags |= LINK_FLG_BSS_COLOR_DIS;
235 		flags_mask |= LINK_FLG_BSS_COLOR_DIS;
236 	}
237 
238 	cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
239 
240 	/* Block 26-tone RU OFDMA transmissions */
241 	if (link_info->he_ru_2mhz_block) {
242 		flags |= LINK_FLG_RU_2MHZ_BLOCK;
243 		flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
244 	}
245 
246 	if (link_conf->nontransmitted) {
247 		ether_addr_copy(cmd.ref_bssid_addr,
248 				link_conf->transmitter_bssid);
249 		cmd.bssid_index = link_conf->bssid_index;
250 	}
251 
252 send_cmd:
253 	cmd.modify_mask = cpu_to_le32(changes);
254 	cmd.flags = cpu_to_le32(flags);
255 	cmd.flags_mask = cpu_to_le32(flags_mask);
256 	cmd.spec_link_id = link_conf->link_id;
257 	if (cmd_ver < 2)
258 		cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
259 
260 	ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
261 	if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
262 		link_info->active = active;
263 
264 	return ret;
265 }
266 
267 int iwl_mvm_unset_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
268 			       struct ieee80211_bss_conf *link_conf)
269 {
270 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
271 	struct iwl_mvm_vif_link_info *link_info =
272 		mvmvif->link[link_conf->link_id];
273 
274 	/* mac80211 thought we have the link, but it was never configured */
275 	if (WARN_ON(!link_info ||
276 		    link_info->fw_link_id >=
277 		    ARRAY_SIZE(mvm->link_id_to_link_conf)))
278 		return -EINVAL;
279 
280 	RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
281 			 NULL);
282 	iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
283 	return 0;
284 }
285 
286 int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
287 			struct ieee80211_bss_conf *link_conf)
288 {
289 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
290 	unsigned int link_id = link_conf->link_id;
291 	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
292 	struct iwl_link_config_cmd cmd = {};
293 	int ret;
294 
295 	ret = iwl_mvm_unset_link_mapping(mvm, vif, link_conf);
296 	if (ret)
297 		return 0;
298 
299 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
300 	link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
301 	cmd.spec_link_id = link_conf->link_id;
302 	cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
303 
304 	ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
305 
306 	if (!ret)
307 		if (iwl_mvm_sf_update(mvm, vif, true))
308 			IWL_ERR(mvm, "Failed to update SF state\n");
309 
310 	return ret;
311 }
312 
313 /* link should be deactivated before removal, so in most cases we need to
314  * perform these two operations together
315  */
316 int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
317 			 struct ieee80211_bss_conf *link_conf)
318 {
319 	int ret;
320 
321 	ret = iwl_mvm_link_changed(mvm, vif, link_conf,
322 				   LINK_CONTEXT_MODIFY_ACTIVE, false);
323 	if (ret)
324 		return ret;
325 
326 	ret = iwl_mvm_remove_link(mvm, vif, link_conf);
327 	if (ret)
328 		return ret;
329 
330 	return ret;
331 }
332 
333 struct iwl_mvm_rssi_to_grade {
334 	s8 rssi[2];
335 	u16 grade;
336 };
337 
338 #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
339 	{ \
340 		.rssi = {_lb, _hb_uhb}, \
341 		.grade = _grade \
342 	}
343 
344 /*
345  * This array must be sorted by increasing RSSI for proper functionality.
346  * The grades are actually estimated throughput, represented as fixed-point
347  * with a scale factor of 1/10.
348  */
349 static const struct iwl_mvm_rssi_to_grade rssi_to_grade_map[] = {
350 	RSSI_TO_GRADE_LINE(-85, -89, 177),
351 	RSSI_TO_GRADE_LINE(-83, -86, 344),
352 	RSSI_TO_GRADE_LINE(-82, -85, 516),
353 	RSSI_TO_GRADE_LINE(-80, -83, 688),
354 	RSSI_TO_GRADE_LINE(-77, -79, 1032),
355 	RSSI_TO_GRADE_LINE(-73, -76, 1376),
356 	RSSI_TO_GRADE_LINE(-70, -74, 1548),
357 	RSSI_TO_GRADE_LINE(-69, -72, 1750),
358 	RSSI_TO_GRADE_LINE(-65, -68, 2064),
359 	RSSI_TO_GRADE_LINE(-61, -66, 2294),
360 	RSSI_TO_GRADE_LINE(-58, -61, 2580),
361 	RSSI_TO_GRADE_LINE(-55, -58, 2868),
362 	RSSI_TO_GRADE_LINE(-46, -55, 3098),
363 	RSSI_TO_GRADE_LINE(-43, -54, 3442)
364 };
365 
366 #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
367 
368 #define DEFAULT_CHAN_LOAD_LB	30
369 #define DEFAULT_CHAN_LOAD_HB	15
370 #define DEFAULT_CHAN_LOAD_UHB	0
371 
372 /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
373 #define SCALE_FACTOR 256
374 
375 /* Convert a percentage from [0,100] to [0,255] */
376 #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * SCALE_FACTOR / 100)
377 
378 static unsigned int
379 iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf *link_conf)
380 {
381 	enum nl80211_chan_width chan_width =
382 		link_conf->chanreq.oper.width;
383 	int mhz = nl80211_chan_width_to_mhz(chan_width);
384 	unsigned int n_subchannels, n_punctured, puncturing_penalty;
385 
386 	if (WARN_ONCE(mhz < 20 || mhz > 320,
387 		      "Invalid channel width : (%d)\n", mhz))
388 		return SCALE_FACTOR;
389 
390 	/* No puncturing, no penalty */
391 	if (mhz < 80)
392 		return SCALE_FACTOR;
393 
394 	/* total number of subchannels */
395 	n_subchannels = mhz / 20;
396 	/* how many of these are punctured */
397 	n_punctured = hweight16(link_conf->chanreq.oper.punctured);
398 
399 	puncturing_penalty = n_punctured * SCALE_FACTOR / n_subchannels;
400 	return SCALE_FACTOR - puncturing_penalty;
401 }
402 
403 static unsigned int
404 iwl_mvm_get_chan_load(struct ieee80211_bss_conf *link_conf)
405 {
406 	struct iwl_mvm_vif_link_info *mvm_link =
407 		iwl_mvm_vif_from_mac80211(link_conf->vif)->link[link_conf->link_id];
408 	const struct element *bss_load_elem;
409 	const struct ieee80211_bss_load_elem *bss_load;
410 	enum nl80211_band band = link_conf->chanreq.oper.chan->band;
411 	unsigned int chan_load;
412 	u32 chan_load_by_us;
413 
414 	rcu_read_lock();
415 	bss_load_elem = ieee80211_bss_get_elem(link_conf->bss,
416 					       WLAN_EID_QBSS_LOAD);
417 
418 	/* If there isn't BSS Load element, take the defaults */
419 	if (!bss_load_elem ||
420 	    bss_load_elem->datalen != sizeof(*bss_load)) {
421 		rcu_read_unlock();
422 		switch (band) {
423 		case NL80211_BAND_2GHZ:
424 			chan_load = DEFAULT_CHAN_LOAD_LB;
425 			break;
426 		case NL80211_BAND_5GHZ:
427 			chan_load = DEFAULT_CHAN_LOAD_HB;
428 			break;
429 		case NL80211_BAND_6GHZ:
430 			chan_load = DEFAULT_CHAN_LOAD_UHB;
431 			break;
432 		default:
433 			chan_load = 0;
434 			break;
435 		}
436 		/* The defaults are given in percentage */
437 		return NORMALIZE_PERCENT_TO_255(chan_load);
438 	}
439 
440 	bss_load = (const void *)bss_load_elem->data;
441 	/* Channel util is in range 0-255 */
442 	chan_load = bss_load->channel_util;
443 	rcu_read_unlock();
444 
445 	if (!mvm_link || !mvm_link->active)
446 		return chan_load;
447 
448 	if (WARN_ONCE(!mvm_link->phy_ctxt,
449 		      "Active link (%u) without phy ctxt assigned!\n",
450 		      link_conf->link_id))
451 		return chan_load;
452 
453 	/* channel load by us is given in percentage */
454 	chan_load_by_us =
455 		NORMALIZE_PERCENT_TO_255(mvm_link->phy_ctxt->channel_load_by_us);
456 
457 	/* Use only values that firmware sends that can possibly be valid */
458 	if (chan_load_by_us <= chan_load)
459 		chan_load -= chan_load_by_us;
460 
461 	return chan_load;
462 }
463 
464 static unsigned int
465 iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf)
466 {
467 	return SCALE_FACTOR - iwl_mvm_get_chan_load(link_conf);
468 }
469 
470 /* This function calculates the grade of a link. Returns 0 in error case */
471 VISIBLE_IF_IWLWIFI_KUNIT
472 unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf)
473 {
474 	enum nl80211_band band;
475 	int i, rssi_idx;
476 	s32 link_rssi;
477 	unsigned int grade = MAX_GRADE;
478 
479 	if (WARN_ON_ONCE(!link_conf))
480 		return 0;
481 
482 	band = link_conf->chanreq.oper.chan->band;
483 	if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
484 		      band != NL80211_BAND_5GHZ &&
485 		      band != NL80211_BAND_6GHZ,
486 		      "Invalid band (%u)\n", band))
487 		return 0;
488 
489 	link_rssi = MBM_TO_DBM(link_conf->bss->signal);
490 	/*
491 	 * For 6 GHz the RSSI of the beacons is lower than
492 	 * the RSSI of the data.
493 	 */
494 	if (band == NL80211_BAND_6GHZ)
495 		link_rssi += 4;
496 
497 	rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
498 
499 	/* No valid RSSI - take the lowest grade */
500 	if (!link_rssi)
501 		link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
502 
503 	/* Get grade based on RSSI */
504 	for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
505 		const struct iwl_mvm_rssi_to_grade *line =
506 			&rssi_to_grade_map[i];
507 
508 		if (link_rssi > line->rssi[rssi_idx])
509 			continue;
510 		grade = line->grade;
511 		break;
512 	}
513 
514 	/* apply the channel load and puncturing factors */
515 	grade = grade * iwl_mvm_get_chan_load_factor(link_conf) / SCALE_FACTOR;
516 	grade = grade * iwl_mvm_get_puncturing_factor(link_conf) / SCALE_FACTOR;
517 	return grade;
518 }
519 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade);
520 
521 static
522 u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
523 				   struct iwl_mvm_link_sel_data *data,
524 				   unsigned long usable_links,
525 				   u8 *best_link_idx)
526 {
527 	u8 n_data = 0;
528 	u16 max_grade = 0;
529 	unsigned long link_id;
530 
531 	/* TODO: don't select links that weren't discovered in the last scan */
532 	for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
533 		struct ieee80211_bss_conf *link_conf =
534 			link_conf_dereference_protected(vif, link_id);
535 
536 		if (WARN_ON_ONCE(!link_conf))
537 			continue;
538 
539 		data[n_data].link_id = link_id;
540 		data[n_data].chandef = &link_conf->chanreq.oper;
541 		data[n_data].signal = link_conf->bss->signal / 100;
542 		data[n_data].grade = iwl_mvm_get_link_grade(link_conf);
543 
544 		if (data[n_data].grade > max_grade) {
545 			max_grade = data[n_data].grade;
546 			*best_link_idx = n_data;
547 		}
548 		n_data++;
549 	}
550 
551 	return n_data;
552 }
553 
554 struct iwl_mvm_bw_to_rssi_threshs {
555 	s8 low;
556 	s8 high;
557 };
558 
559 #define BW_TO_RSSI_THRESHOLDS(_bw)				\
560 	[IWL_PHY_CHANNEL_MODE ## _bw] = {			\
561 		.low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ,	\
562 		.high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ	\
563 	}
564 
565 s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
566 			       const struct cfg80211_chan_def *chandef,
567 			       bool low)
568 {
569 	const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map[] = {
570 		BW_TO_RSSI_THRESHOLDS(20),
571 		BW_TO_RSSI_THRESHOLDS(40),
572 		BW_TO_RSSI_THRESHOLDS(80),
573 		BW_TO_RSSI_THRESHOLDS(160)
574 		/* 320 MHz has the same thresholds as 20 MHz */
575 	};
576 	const struct iwl_mvm_bw_to_rssi_threshs *threshs;
577 	u8 chan_width = iwl_mvm_get_channel_width(chandef);
578 
579 	if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
580 		    chandef->chan->band != NL80211_BAND_5GHZ &&
581 		    chandef->chan->band != NL80211_BAND_6GHZ))
582 		return S8_MAX;
583 
584 	/* 6 GHz will always use 20 MHz thresholds, regardless of the BW */
585 	if (chan_width == IWL_PHY_CHANNEL_MODE320)
586 		chan_width = IWL_PHY_CHANNEL_MODE20;
587 
588 	threshs = &bw_to_rssi_threshs_map[chan_width];
589 
590 	return low ? threshs->low : threshs->high;
591 }
592 
593 static u32
594 iwl_mvm_esr_disallowed_with_link(struct ieee80211_vif *vif,
595 				 const struct iwl_mvm_link_sel_data *link)
596 {
597 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
598 	struct iwl_mvm *mvm = mvmvif->mvm;
599 	enum iwl_mvm_esr_state ret = 0;
600 	s8 thresh;
601 
602 	/* BT Coex effects eSR mode only if one of the links is on LB */
603 	if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
604 	    mvmvif->esr_disable_reason & IWL_MVM_ESR_BLOCKED_COEX)
605 		ret |= IWL_MVM_ESR_BLOCKED_COEX;
606 	thresh = iwl_mvm_get_esr_rssi_thresh(mvm, link->chandef,
607 					     false);
608 
609 	if (link->signal < thresh)
610 		ret |= IWL_MVM_ESR_EXIT_LOW_RSSI;
611 
612 	if (ret)
613 		IWL_DEBUG_INFO(mvm,
614 			       "Link %d is not allowed for esr. Reason: 0x%x\n",
615 			       link->link_id, ret);
616 	return ret;
617 }
618 
619 VISIBLE_IF_IWLWIFI_KUNIT
620 bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
621 				 const struct iwl_mvm_link_sel_data *a,
622 				 const struct iwl_mvm_link_sel_data *b)
623 {
624 	/* Per-link considerations */
625 	if (iwl_mvm_esr_disallowed_with_link(vif, a) ||
626 	    iwl_mvm_esr_disallowed_with_link(vif, b))
627 		return false;
628 
629 	/* Per-combination considerations */
630 	return a->chandef->chan->band != b->chandef->chan->band;
631 }
632 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair);
633 
634 /*
635  * Returns the combined eSR grade of two given links.
636  * Returns 0 if eSR is not allowed with these 2 links.
637  */
638 static
639 unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif *vif,
640 				   const struct iwl_mvm_link_sel_data *a,
641 				   const struct iwl_mvm_link_sel_data *b,
642 				   u8 *primary_id)
643 {
644 	struct ieee80211_bss_conf *primary_conf;
645 	struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
646 	unsigned int primary_load;
647 
648 	lockdep_assert_wiphy(wiphy);
649 
650 	/* a is always primary, b is always secondary */
651 	if (b->grade > a->grade)
652 		swap(a, b);
653 
654 	*primary_id = a->link_id;
655 
656 	if (!iwl_mvm_mld_valid_link_pair(vif, a, b))
657 		return 0;
658 
659 	primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
660 
661 	if (WARN_ON_ONCE(!primary_conf))
662 		return 0;
663 
664 	primary_load = iwl_mvm_get_chan_load(primary_conf);
665 
666 	return a->grade +
667 		((b->grade * primary_load) / SCALE_FACTOR);
668 }
669 
670 void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
671 {
672 	struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
673 	struct iwl_mvm_link_sel_data *best_link;
674 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
675 	u32 max_active_links = iwl_mvm_max_active_links(mvm, vif);
676 	u16 usable_links = ieee80211_vif_usable_links(vif);
677 	u8 best, primary_link, best_in_pair, n_data;
678 	u16 max_esr_grade = 0, new_active_links;
679 
680 	lockdep_assert_wiphy(mvm->hw->wiphy);
681 
682 	if (!mvmvif->authorized || !ieee80211_vif_is_mld(vif))
683 		return;
684 
685 	if (!IWL_MVM_AUTO_EML_ENABLE)
686 		return;
687 
688 	/* The logic below is a simple version that doesn't suit more than 2
689 	 * links
690 	 */
691 	WARN_ON_ONCE(max_active_links > 2);
692 
693 	n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links,
694 						 &best);
695 
696 	if (WARN(!n_data, "Couldn't find a valid grade for any link!\n"))
697 		return;
698 
699 	best_link = &data[best];
700 	primary_link = best_link->link_id;
701 	new_active_links = BIT(best_link->link_id);
702 
703 	/* eSR is not supported/allowed, or only one usable link */
704 	if (max_active_links == 1 || !iwl_mvm_esr_allowed_on_vif(mvm, vif) ||
705 	    n_data == 1)
706 		goto set_active;
707 
708 	for (u8 a = 0; a < n_data; a++)
709 		for (u8 b = a + 1; b < n_data; b++) {
710 			u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a],
711 							      &data[b],
712 							      &best_in_pair);
713 
714 			if (esr_grade <= max_esr_grade)
715 				continue;
716 
717 			max_esr_grade = esr_grade;
718 			primary_link = best_in_pair;
719 			new_active_links = BIT(data[a].link_id) |
720 					   BIT(data[b].link_id);
721 		}
722 
723 	/* No valid pair was found, go with the best link */
724 	if (hweight16(new_active_links) <= 1)
725 		goto set_active;
726 
727 	/* prefer single link over marginal eSR improvement */
728 	if (best_link->grade * 110 / 100 >= max_esr_grade) {
729 		primary_link = best_link->link_id;
730 		new_active_links = BIT(best_link->link_id);
731 	}
732 set_active:
733 	IWL_DEBUG_INFO(mvm, "Link selection result: 0x%x. Primary = %d\n",
734 		       new_active_links, primary_link);
735 	ieee80211_set_active_links_async(vif, new_active_links);
736 	mvmvif->link_selection_res = new_active_links;
737 	mvmvif->link_selection_primary = primary_link;
738 }
739 
740 u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif)
741 {
742 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
743 
744 	/* relevant data is written with both locks held, so read with either */
745 	lockdep_assert(lockdep_is_held(&mvmvif->mvm->mutex) ||
746 		       lockdep_is_held(&mvmvif->mvm->hw->wiphy->mtx));
747 
748 	if (!ieee80211_vif_is_mld(vif))
749 		return 0;
750 
751 	/* In AP mode, there is no primary link */
752 	if (vif->type == NL80211_IFTYPE_AP)
753 		return __ffs(vif->active_links);
754 
755 	if (mvmvif->esr_active &&
756 	    !WARN_ON(!(BIT(mvmvif->primary_link) & vif->active_links)))
757 		return mvmvif->primary_link;
758 
759 	return __ffs(vif->active_links);
760 }
761 
762 /*
763  * For non-MLO/single link, this will return the deflink/single active link,
764  * respectively
765  */
766 u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id)
767 {
768 	switch (hweight16(vif->active_links)) {
769 	case 0:
770 		return 0;
771 	default:
772 		WARN_ON(1);
773 		fallthrough;
774 	case 1:
775 		return __ffs(vif->active_links);
776 	case 2:
777 		return __ffs(vif->active_links & ~BIT(link_id));
778 	}
779 }
780 
781 /* Reasons that can cause esr prevention */
782 #define IWL_MVM_ESR_PREVENT_REASONS	IWL_MVM_ESR_EXIT_MISSED_BEACON
783 #define IWL_MVM_PREVENT_ESR_TIMEOUT	(HZ * 400)
784 #define IWL_MVM_ESR_PREVENT_SHORT	(HZ * 300)
785 #define IWL_MVM_ESR_PREVENT_LONG	(HZ * 600)
786 
787 static void iwl_mvm_recalc_esr_prevention(struct iwl_mvm *mvm,
788 					  struct iwl_mvm_vif *mvmvif,
789 					  enum iwl_mvm_esr_state reason)
790 {
791 	unsigned long now = jiffies;
792 	unsigned long delay;
793 	bool timeout_expired =
794 		time_after(now, mvmvif->last_esr_exit.ts +
795 				IWL_MVM_PREVENT_ESR_TIMEOUT);
796 
797 	if (WARN_ON(!(IWL_MVM_ESR_PREVENT_REASONS & reason)))
798 		return;
799 
800 	lockdep_assert_held(&mvm->mutex);
801 
802 	mvmvif->last_esr_exit.ts = now;
803 
804 	if (timeout_expired ||
805 	    mvmvif->last_esr_exit.reason != reason) {
806 		mvmvif->last_esr_exit.reason = reason;
807 		mvmvif->exit_same_reason_count = 1;
808 		return;
809 	}
810 
811 	mvmvif->exit_same_reason_count++;
812 	if (WARN_ON(mvmvif->exit_same_reason_count < 2 ||
813 		    mvmvif->exit_same_reason_count > 3))
814 		return;
815 
816 	mvmvif->esr_disable_reason |= IWL_MVM_ESR_BLOCKED_PREVENTION;
817 
818 	delay = mvmvif->exit_same_reason_count == 2 ?
819 		IWL_MVM_ESR_PREVENT_SHORT :
820 		IWL_MVM_ESR_PREVENT_LONG;
821 
822 	IWL_DEBUG_INFO(mvm,
823 		       "Preventing EMLSR for %ld seconds due to %u exits with the reason 0x%x\n",
824 		       delay / HZ, mvmvif->exit_same_reason_count, reason);
825 
826 	wiphy_delayed_work_queue(mvm->hw->wiphy,
827 				 &mvmvif->prevent_esr_done_wk, delay);
828 }
829 
830 /* API to exit eSR mode */
831 void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
832 		      enum iwl_mvm_esr_state reason,
833 		      u8 link_to_keep)
834 {
835 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
836 	u16 new_active_links;
837 
838 	lockdep_assert_held(&mvm->mutex);
839 
840 	/* Nothing to do */
841 	if (!mvmvif->esr_active)
842 		return;
843 
844 	if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mvmvif->authorized))
845 		return;
846 
847 	if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
848 		link_to_keep = __ffs(vif->active_links);
849 
850 	new_active_links = BIT(link_to_keep);
851 	IWL_DEBUG_INFO(mvm,
852 		       "Exiting EMLSR. Reason = 0x%x. Current active links=0x%x, new active links = 0x%x\n",
853 		       reason, vif->active_links, new_active_links);
854 
855 	ieee80211_set_active_links_async(vif, new_active_links);
856 
857 	if (IWL_MVM_ESR_PREVENT_REASONS & reason)
858 		iwl_mvm_recalc_esr_prevention(mvm, mvmvif, reason);
859 }
860 
861 void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
862 		       enum iwl_mvm_esr_state reason,
863 		       u8 link_to_keep)
864 {
865 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
866 
867 	lockdep_assert_held(&mvm->mutex);
868 
869 	/* This should be called only with disable reasons */
870 	if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
871 		return;
872 
873 	if (!(mvmvif->esr_disable_reason & reason))
874 		IWL_DEBUG_INFO(mvm, "Blocking EMSLR mode. reason = 0x%x\n",
875 			       reason);
876 
877 	mvmvif->esr_disable_reason |= reason;
878 
879 	iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
880 }
881 
882 void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
883 			 enum iwl_mvm_esr_state reason)
884 {
885 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
886 
887 	lockdep_assert_held(&mvm->mutex);
888 
889 	/* This should be called only with disable reasons */
890 	if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
891 		return;
892 
893 	if (mvmvif->esr_disable_reason & reason)
894 		IWL_DEBUG_INFO(mvm, "Unblocking EMSLR mode. reason = 0x%x\n",
895 			       reason);
896 
897 	mvmvif->esr_disable_reason &= ~reason;
898 }
899