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 #define HANDLE_ESR_REASONS(HOW) \
9 HOW(BLOCKED_PREVENTION) \
10 HOW(BLOCKED_WOWLAN) \
11 HOW(BLOCKED_TPT) \
12 HOW(BLOCKED_FW) \
13 HOW(BLOCKED_NON_BSS) \
14 HOW(EXIT_MISSED_BEACON) \
15 HOW(EXIT_LOW_RSSI) \
16 HOW(EXIT_COEX) \
17 HOW(EXIT_BANDWIDTH) \
18 HOW(EXIT_CSA) \
19 HOW(EXIT_LINK_USAGE)
20
21 static const char *const iwl_mvm_esr_states_names[] = {
22 #define NAME_ENTRY(x) [ilog2(IWL_MVM_ESR_##x)] = #x,
23 HANDLE_ESR_REASONS(NAME_ENTRY)
24 };
25
iwl_get_esr_state_string(enum iwl_mvm_esr_state state)26 const char *iwl_get_esr_state_string(enum iwl_mvm_esr_state state)
27 {
28 int offs = ilog2(state);
29
30 if (offs >= ARRAY_SIZE(iwl_mvm_esr_states_names) ||
31 !iwl_mvm_esr_states_names[offs])
32 return "UNKNOWN";
33
34 return iwl_mvm_esr_states_names[offs];
35 }
36
iwl_mvm_print_esr_state(struct iwl_mvm * mvm,u32 mask)37 static void iwl_mvm_print_esr_state(struct iwl_mvm *mvm, u32 mask)
38 {
39 #define NAME_FMT(x) "%s"
40 #define NAME_PR(x) (mask & IWL_MVM_ESR_##x) ? "[" #x "]" : "",
41 IWL_DEBUG_INFO(mvm,
42 "EMLSR state = " HANDLE_ESR_REASONS(NAME_FMT)
43 " (0x%x)\n",
44 HANDLE_ESR_REASONS(NAME_PR)
45 mask);
46 #undef NAME_FMT
47 #undef NAME_PR
48 }
49
iwl_mvm_get_free_fw_link_id(struct iwl_mvm * mvm,struct iwl_mvm_vif * mvm_vif)50 static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm,
51 struct iwl_mvm_vif *mvm_vif)
52 {
53 u32 link_id;
54
55 lockdep_assert_held(&mvm->mutex);
56
57 link_id = ffz(mvm->fw_link_ids_map);
58
59 /* this case can happen if there're deactivated but not removed links */
60 if (link_id > IWL_MVM_FW_MAX_LINK_ID)
61 return IWL_MVM_FW_LINK_ID_INVALID;
62
63 mvm->fw_link_ids_map |= BIT(link_id);
64 return link_id;
65 }
66
iwl_mvm_release_fw_link_id(struct iwl_mvm * mvm,u32 link_id)67 static void iwl_mvm_release_fw_link_id(struct iwl_mvm *mvm, u32 link_id)
68 {
69 lockdep_assert_held(&mvm->mutex);
70
71 if (!WARN_ON(link_id > IWL_MVM_FW_MAX_LINK_ID))
72 mvm->fw_link_ids_map &= ~BIT(link_id);
73 }
74
iwl_mvm_link_cmd_send(struct iwl_mvm * mvm,struct iwl_link_config_cmd * cmd,enum iwl_ctxt_action action)75 static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm,
76 struct iwl_link_config_cmd *cmd,
77 enum iwl_ctxt_action action)
78 {
79 int ret;
80
81 cmd->action = cpu_to_le32(action);
82 ret = iwl_mvm_send_cmd_pdu(mvm,
83 WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0,
84 sizeof(*cmd), cmd);
85 if (ret)
86 IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
87 action, ret);
88 return ret;
89 }
90
iwl_mvm_set_link_mapping(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)91 int iwl_mvm_set_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
92 struct ieee80211_bss_conf *link_conf)
93 {
94 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
95 struct iwl_mvm_vif_link_info *link_info =
96 mvmvif->link[link_conf->link_id];
97
98 if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
99 link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
100 mvmvif);
101 if (link_info->fw_link_id >=
102 ARRAY_SIZE(mvm->link_id_to_link_conf))
103 return -EINVAL;
104
105 rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id],
106 link_conf);
107 }
108
109 return 0;
110 }
111
iwl_mvm_add_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)112 int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
113 struct ieee80211_bss_conf *link_conf)
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_link_config_cmd cmd = {};
119 unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
120 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
121 int ret;
122
123 if (WARN_ON_ONCE(!link_info))
124 return -EINVAL;
125
126 ret = iwl_mvm_set_link_mapping(mvm, vif, link_conf);
127 if (ret)
128 return ret;
129
130 /* Update SF - Disable if needed. if this fails, SF might still be on
131 * while many macs are bound, which is forbidden - so fail the binding.
132 */
133 if (iwl_mvm_sf_update(mvm, vif, false))
134 return -EINVAL;
135
136 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
137 cmd.mac_id = cpu_to_le32(mvmvif->id);
138 cmd.spec_link_id = link_conf->link_id;
139 WARN_ON_ONCE(link_info->phy_ctxt);
140 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
141
142 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
143
144 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
145 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
146
147 if (cmd_ver < 2)
148 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
149
150 return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
151 }
152
153 struct iwl_mvm_esr_iter_data {
154 struct ieee80211_vif *vif;
155 unsigned int link_id;
156 bool lift_block;
157 };
158
iwl_mvm_esr_vif_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)159 static void iwl_mvm_esr_vif_iterator(void *_data, u8 *mac,
160 struct ieee80211_vif *vif)
161 {
162 struct iwl_mvm_esr_iter_data *data = _data;
163 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
164 int link_id;
165
166 if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
167 return;
168
169 for_each_mvm_vif_valid_link(mvmvif, link_id) {
170 struct iwl_mvm_vif_link_info *link_info =
171 mvmvif->link[link_id];
172 if (vif == data->vif && link_id == data->link_id)
173 continue;
174 if (link_info->active)
175 data->lift_block = false;
176 }
177 }
178
iwl_mvm_esr_non_bss_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,unsigned int link_id,bool active)179 int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
180 unsigned int link_id, bool active)
181 {
182 /* An active link of a non-station vif blocks EMLSR. Upon activation
183 * block EMLSR on the bss vif. Upon deactivation, check if this link
184 * was the last non-station link active, and if so unblock the bss vif
185 */
186 struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
187 struct iwl_mvm_esr_iter_data data = {
188 .vif = vif,
189 .link_id = link_id,
190 .lift_block = true,
191 };
192
193 if (IS_ERR_OR_NULL(bss_vif))
194 return 0;
195
196 if (active)
197 return iwl_mvm_block_esr_sync(mvm, bss_vif,
198 IWL_MVM_ESR_BLOCKED_NON_BSS);
199
200 ieee80211_iterate_active_interfaces(mvm->hw,
201 IEEE80211_IFACE_ITER_NORMAL,
202 iwl_mvm_esr_vif_iterator, &data);
203 if (data.lift_block) {
204 mutex_lock(&mvm->mutex);
205 iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_NON_BSS);
206 mutex_unlock(&mvm->mutex);
207 }
208
209 return 0;
210 }
211
iwl_mvm_link_changed(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf,u32 changes,bool active)212 int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
213 struct ieee80211_bss_conf *link_conf,
214 u32 changes, bool active)
215 {
216 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
217 unsigned int link_id = link_conf->link_id;
218 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
219 struct iwl_mvm_phy_ctxt *phyctxt;
220 struct iwl_link_config_cmd cmd = {};
221 u32 ht_flag, flags = 0, flags_mask = 0;
222 int ret;
223 unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
224 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
225
226 if (WARN_ON_ONCE(!link_info ||
227 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
228 return -EINVAL;
229
230 if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
231 /* When activating a link, phy context should be valid;
232 * when deactivating a link, it also should be valid since
233 * the link was active before. So, do nothing in this case.
234 * Since a link is added first with FW_CTXT_INVALID, then we
235 * can get here in case it's removed before it was activated.
236 */
237 if (!link_info->phy_ctxt)
238 return 0;
239
240 /* Catch early if driver tries to activate or deactivate a link
241 * twice.
242 */
243 WARN_ON_ONCE(active == link_info->active);
244
245 /* When deactivating a link session protection should
246 * be stopped
247 */
248 if (!active && vif->type == NL80211_IFTYPE_STATION)
249 iwl_mvm_stop_session_protection(mvm, vif);
250 }
251
252 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
253
254 /* The phy_id, link address and listen_lmac can be modified only until
255 * the link becomes active, otherwise they will be ignored.
256 */
257 phyctxt = link_info->phy_ctxt;
258 if (phyctxt)
259 cmd.phy_id = cpu_to_le32(phyctxt->id);
260 else
261 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
262 cmd.mac_id = cpu_to_le32(mvmvif->id);
263
264 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
265
266 cmd.active = cpu_to_le32(active);
267
268 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
269 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
270
271 iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf,
272 &cmd.cck_rates, &cmd.ofdm_rates);
273
274 cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble);
275 cmd.short_slot = cpu_to_le32(link_conf->use_short_slot);
276
277 /* The fw does not distinguish between ht and fat */
278 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
279 iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
280 &cmd.protection_flags,
281 ht_flag, LINK_PROT_FLG_TGG_PROTECT);
282
283 iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac,
284 &cmd.qos_flags);
285
286
287 cmd.bi = cpu_to_le32(link_conf->beacon_int);
288 cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int *
289 link_conf->dtim_period);
290
291 if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax ||
292 (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
293 changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
294 goto send_cmd;
295 }
296
297 cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext;
298
299 if (link_conf->uora_exists) {
300 cmd.rand_alloc_ecwmin =
301 link_conf->uora_ocw_range & 0x7;
302 cmd.rand_alloc_ecwmax =
303 (link_conf->uora_ocw_range >> 3) & 0x7;
304 }
305
306 /* TODO how to set ndp_fdbk_buff_th_exp? */
307
308 if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id],
309 &cmd.trig_based_txf[0])) {
310 flags |= LINK_FLG_MU_EDCA_CW;
311 flags_mask |= LINK_FLG_MU_EDCA_CW;
312 }
313
314 if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) {
315 struct ieee80211_chanctx_conf *ctx;
316 struct cfg80211_chan_def *def = NULL;
317
318 rcu_read_lock();
319 ctx = rcu_dereference(link_conf->chanctx_conf);
320 if (ctx)
321 def = iwl_mvm_chanctx_def(mvm, ctx);
322
323 if (iwlwifi_mod_params.disable_11be ||
324 !link_conf->eht_support || !def ||
325 iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) >= 6)
326 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
327 else
328 cmd.puncture_mask = cpu_to_le16(def->punctured);
329 rcu_read_unlock();
330 }
331
332 cmd.bss_color = link_conf->he_bss_color.color;
333
334 if (!link_conf->he_bss_color.enabled) {
335 flags |= LINK_FLG_BSS_COLOR_DIS;
336 flags_mask |= LINK_FLG_BSS_COLOR_DIS;
337 }
338
339 cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
340
341 /* Block 26-tone RU OFDMA transmissions */
342 if (link_info->he_ru_2mhz_block) {
343 flags |= LINK_FLG_RU_2MHZ_BLOCK;
344 flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
345 }
346
347 if (link_conf->nontransmitted) {
348 ether_addr_copy(cmd.ref_bssid_addr,
349 link_conf->transmitter_bssid);
350 cmd.bssid_index = link_conf->bssid_index;
351 }
352
353 send_cmd:
354 cmd.modify_mask = cpu_to_le32(changes);
355 cmd.flags = cpu_to_le32(flags);
356 cmd.flags_mask = cpu_to_le32(flags_mask);
357 cmd.spec_link_id = link_conf->link_id;
358 if (cmd_ver < 2)
359 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
360
361 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
362 if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
363 link_info->active = active;
364
365 return ret;
366 }
367
iwl_mvm_unset_link_mapping(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)368 int iwl_mvm_unset_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
369 struct ieee80211_bss_conf *link_conf)
370 {
371 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
372 struct iwl_mvm_vif_link_info *link_info =
373 mvmvif->link[link_conf->link_id];
374
375 /* mac80211 thought we have the link, but it was never configured */
376 if (WARN_ON(!link_info ||
377 link_info->fw_link_id >=
378 ARRAY_SIZE(mvm->link_id_to_link_conf)))
379 return -EINVAL;
380
381 RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
382 NULL);
383 iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
384 return 0;
385 }
386
iwl_mvm_remove_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)387 int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
388 struct ieee80211_bss_conf *link_conf)
389 {
390 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
391 unsigned int link_id = link_conf->link_id;
392 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
393 struct iwl_link_config_cmd cmd = {};
394 int ret;
395
396 ret = iwl_mvm_unset_link_mapping(mvm, vif, link_conf);
397 if (ret)
398 return 0;
399
400 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
401 link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
402 cmd.spec_link_id = link_conf->link_id;
403 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
404
405 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
406
407 if (!ret)
408 if (iwl_mvm_sf_update(mvm, vif, true))
409 IWL_ERR(mvm, "Failed to update SF state\n");
410
411 return ret;
412 }
413
414 /* link should be deactivated before removal, so in most cases we need to
415 * perform these two operations together
416 */
iwl_mvm_disable_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)417 int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
418 struct ieee80211_bss_conf *link_conf)
419 {
420 int ret;
421
422 ret = iwl_mvm_link_changed(mvm, vif, link_conf,
423 LINK_CONTEXT_MODIFY_ACTIVE, false);
424 if (ret)
425 return ret;
426
427 ret = iwl_mvm_remove_link(mvm, vif, link_conf);
428 if (ret)
429 return ret;
430
431 return ret;
432 }
433
434 struct iwl_mvm_rssi_to_grade {
435 s8 rssi[2];
436 u16 grade;
437 };
438
439 #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
440 { \
441 .rssi = {_lb, _hb_uhb}, \
442 .grade = _grade \
443 }
444
445 /*
446 * This array must be sorted by increasing RSSI for proper functionality.
447 * The grades are actually estimated throughput, represented as fixed-point
448 * with a scale factor of 1/10.
449 */
450 static const struct iwl_mvm_rssi_to_grade rssi_to_grade_map[] = {
451 RSSI_TO_GRADE_LINE(-85, -89, 177),
452 RSSI_TO_GRADE_LINE(-83, -86, 344),
453 RSSI_TO_GRADE_LINE(-82, -85, 516),
454 RSSI_TO_GRADE_LINE(-80, -83, 688),
455 RSSI_TO_GRADE_LINE(-77, -79, 1032),
456 RSSI_TO_GRADE_LINE(-73, -76, 1376),
457 RSSI_TO_GRADE_LINE(-70, -74, 1548),
458 RSSI_TO_GRADE_LINE(-69, -72, 1750),
459 RSSI_TO_GRADE_LINE(-65, -68, 2064),
460 RSSI_TO_GRADE_LINE(-61, -66, 2294),
461 RSSI_TO_GRADE_LINE(-58, -61, 2580),
462 RSSI_TO_GRADE_LINE(-55, -58, 2868),
463 RSSI_TO_GRADE_LINE(-46, -55, 3098),
464 RSSI_TO_GRADE_LINE(-43, -54, 3442)
465 };
466
467 #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
468
469 #define DEFAULT_CHAN_LOAD_LB 30
470 #define DEFAULT_CHAN_LOAD_HB 15
471 #define DEFAULT_CHAN_LOAD_UHB 0
472
473 /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
474 #define SCALE_FACTOR 256
475
476 /* Convert a percentage from [0,100] to [0,255] */
477 #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * SCALE_FACTOR / 100)
478
479 static unsigned int
iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf * link_conf)480 iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf *link_conf)
481 {
482 enum nl80211_chan_width chan_width =
483 link_conf->chanreq.oper.width;
484 int mhz = nl80211_chan_width_to_mhz(chan_width);
485 unsigned int n_subchannels, n_punctured, puncturing_penalty;
486
487 if (WARN_ONCE(mhz < 20 || mhz > 320,
488 "Invalid channel width : (%d)\n", mhz))
489 return SCALE_FACTOR;
490
491 /* No puncturing, no penalty */
492 if (mhz < 80)
493 return SCALE_FACTOR;
494
495 /* total number of subchannels */
496 n_subchannels = mhz / 20;
497 /* how many of these are punctured */
498 n_punctured = hweight16(link_conf->chanreq.oper.punctured);
499
500 puncturing_penalty = n_punctured * SCALE_FACTOR / n_subchannels;
501 return SCALE_FACTOR - puncturing_penalty;
502 }
503
504 static unsigned int
iwl_mvm_get_chan_load(struct ieee80211_bss_conf * link_conf)505 iwl_mvm_get_chan_load(struct ieee80211_bss_conf *link_conf)
506 {
507 struct iwl_mvm_vif_link_info *mvm_link =
508 iwl_mvm_vif_from_mac80211(link_conf->vif)->link[link_conf->link_id];
509 const struct element *bss_load_elem;
510 const struct ieee80211_bss_load_elem *bss_load;
511 enum nl80211_band band = link_conf->chanreq.oper.chan->band;
512 unsigned int chan_load;
513 u32 chan_load_by_us;
514
515 rcu_read_lock();
516 bss_load_elem = ieee80211_bss_get_elem(link_conf->bss,
517 WLAN_EID_QBSS_LOAD);
518
519 /* If there isn't BSS Load element, take the defaults */
520 if (!bss_load_elem ||
521 bss_load_elem->datalen != sizeof(*bss_load)) {
522 rcu_read_unlock();
523 switch (band) {
524 case NL80211_BAND_2GHZ:
525 chan_load = DEFAULT_CHAN_LOAD_LB;
526 break;
527 case NL80211_BAND_5GHZ:
528 chan_load = DEFAULT_CHAN_LOAD_HB;
529 break;
530 case NL80211_BAND_6GHZ:
531 chan_load = DEFAULT_CHAN_LOAD_UHB;
532 break;
533 default:
534 chan_load = 0;
535 break;
536 }
537 /* The defaults are given in percentage */
538 return NORMALIZE_PERCENT_TO_255(chan_load);
539 }
540
541 bss_load = (const void *)bss_load_elem->data;
542 /* Channel util is in range 0-255 */
543 chan_load = bss_load->channel_util;
544 rcu_read_unlock();
545
546 if (!mvm_link || !mvm_link->active)
547 return chan_load;
548
549 if (WARN_ONCE(!mvm_link->phy_ctxt,
550 "Active link (%u) without phy ctxt assigned!\n",
551 link_conf->link_id))
552 return chan_load;
553
554 /* channel load by us is given in percentage */
555 chan_load_by_us =
556 NORMALIZE_PERCENT_TO_255(mvm_link->phy_ctxt->channel_load_by_us);
557
558 /* Use only values that firmware sends that can possibly be valid */
559 if (chan_load_by_us <= chan_load)
560 chan_load -= chan_load_by_us;
561
562 return chan_load;
563 }
564
565 static unsigned int
iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf * link_conf)566 iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf)
567 {
568 return SCALE_FACTOR - iwl_mvm_get_chan_load(link_conf);
569 }
570
571 /* This function calculates the grade of a link. Returns 0 in error case */
572 VISIBLE_IF_IWLWIFI_KUNIT
iwl_mvm_get_link_grade(struct ieee80211_bss_conf * link_conf)573 unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf)
574 {
575 enum nl80211_band band;
576 int i, rssi_idx;
577 s32 link_rssi;
578 unsigned int grade = MAX_GRADE;
579
580 if (WARN_ON_ONCE(!link_conf))
581 return 0;
582
583 band = link_conf->chanreq.oper.chan->band;
584 if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
585 band != NL80211_BAND_5GHZ &&
586 band != NL80211_BAND_6GHZ,
587 "Invalid band (%u)\n", band))
588 return 0;
589
590 link_rssi = MBM_TO_DBM(link_conf->bss->signal);
591 /*
592 * For 6 GHz the RSSI of the beacons is lower than
593 * the RSSI of the data.
594 */
595 if (band == NL80211_BAND_6GHZ)
596 link_rssi += 4;
597
598 rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
599
600 /* No valid RSSI - take the lowest grade */
601 if (!link_rssi)
602 link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
603
604 /* Get grade based on RSSI */
605 for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
606 const struct iwl_mvm_rssi_to_grade *line =
607 &rssi_to_grade_map[i];
608
609 if (link_rssi > line->rssi[rssi_idx])
610 continue;
611 grade = line->grade;
612 break;
613 }
614
615 /* apply the channel load and puncturing factors */
616 grade = grade * iwl_mvm_get_chan_load_factor(link_conf) / SCALE_FACTOR;
617 grade = grade * iwl_mvm_get_puncturing_factor(link_conf) / SCALE_FACTOR;
618 return grade;
619 }
620 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade);
621
622 static
iwl_mvm_set_link_selection_data(struct ieee80211_vif * vif,struct iwl_mvm_link_sel_data * data,unsigned long usable_links,u8 * best_link_idx)623 u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
624 struct iwl_mvm_link_sel_data *data,
625 unsigned long usable_links,
626 u8 *best_link_idx)
627 {
628 u8 n_data = 0;
629 u16 max_grade = 0;
630 unsigned long link_id;
631
632 /* TODO: don't select links that weren't discovered in the last scan */
633 for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
634 struct ieee80211_bss_conf *link_conf =
635 link_conf_dereference_protected(vif, link_id);
636
637 if (WARN_ON_ONCE(!link_conf))
638 continue;
639
640 data[n_data].link_id = link_id;
641 data[n_data].chandef = &link_conf->chanreq.oper;
642 data[n_data].signal = link_conf->bss->signal / 100;
643 data[n_data].grade = iwl_mvm_get_link_grade(link_conf);
644
645 if (data[n_data].grade > max_grade) {
646 max_grade = data[n_data].grade;
647 *best_link_idx = n_data;
648 }
649 n_data++;
650 }
651
652 return n_data;
653 }
654
655 struct iwl_mvm_bw_to_rssi_threshs {
656 s8 low;
657 s8 high;
658 };
659
660 #define BW_TO_RSSI_THRESHOLDS(_bw) \
661 [IWL_PHY_CHANNEL_MODE ## _bw] = { \
662 .low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ, \
663 .high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ \
664 }
665
iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm * mvm,const struct cfg80211_chan_def * chandef,bool low)666 s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
667 const struct cfg80211_chan_def *chandef,
668 bool low)
669 {
670 const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map[] = {
671 BW_TO_RSSI_THRESHOLDS(20),
672 BW_TO_RSSI_THRESHOLDS(40),
673 BW_TO_RSSI_THRESHOLDS(80),
674 BW_TO_RSSI_THRESHOLDS(160)
675 /* 320 MHz has the same thresholds as 20 MHz */
676 };
677 const struct iwl_mvm_bw_to_rssi_threshs *threshs;
678 u8 chan_width = iwl_mvm_get_channel_width(chandef);
679
680 if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
681 chandef->chan->band != NL80211_BAND_5GHZ &&
682 chandef->chan->band != NL80211_BAND_6GHZ))
683 return S8_MAX;
684
685 /* 6 GHz will always use 20 MHz thresholds, regardless of the BW */
686 if (chan_width == IWL_PHY_CHANNEL_MODE320)
687 chan_width = IWL_PHY_CHANNEL_MODE20;
688
689 threshs = &bw_to_rssi_threshs_map[chan_width];
690
691 return low ? threshs->low : threshs->high;
692 }
693
694 static u32
iwl_mvm_esr_disallowed_with_link(struct iwl_mvm * mvm,struct ieee80211_vif * vif,const struct iwl_mvm_link_sel_data * link,bool primary)695 iwl_mvm_esr_disallowed_with_link(struct iwl_mvm *mvm,
696 struct ieee80211_vif *vif,
697 const struct iwl_mvm_link_sel_data *link,
698 bool primary)
699 {
700 struct wiphy *wiphy = mvm->hw->wiphy;
701 struct ieee80211_bss_conf *conf;
702 enum iwl_mvm_esr_state ret = 0;
703 s8 thresh;
704
705 conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]);
706 if (WARN_ON_ONCE(!conf))
707 return false;
708
709 /* BT Coex effects eSR mode only if one of the links is on LB */
710 if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
711 (!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link->signal,
712 primary)))
713 ret |= IWL_MVM_ESR_EXIT_COEX;
714
715 thresh = iwl_mvm_get_esr_rssi_thresh(mvm, link->chandef,
716 false);
717
718 if (link->signal < thresh)
719 ret |= IWL_MVM_ESR_EXIT_LOW_RSSI;
720
721 if (conf->csa_active)
722 ret |= IWL_MVM_ESR_EXIT_CSA;
723
724 if (ret) {
725 IWL_DEBUG_INFO(mvm,
726 "Link %d is not allowed for esr\n",
727 link->link_id);
728 iwl_mvm_print_esr_state(mvm, ret);
729 }
730 return ret;
731 }
732
733 VISIBLE_IF_IWLWIFI_KUNIT
iwl_mvm_mld_valid_link_pair(struct ieee80211_vif * vif,const struct iwl_mvm_link_sel_data * a,const struct iwl_mvm_link_sel_data * b)734 bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
735 const struct iwl_mvm_link_sel_data *a,
736 const struct iwl_mvm_link_sel_data *b)
737 {
738 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
739 struct iwl_mvm *mvm = mvmvif->mvm;
740 enum iwl_mvm_esr_state ret = 0;
741
742 /* Per-link considerations */
743 if (iwl_mvm_esr_disallowed_with_link(mvm, vif, a, true) ||
744 iwl_mvm_esr_disallowed_with_link(mvm, vif, b, false))
745 return false;
746
747 if (a->chandef->width != b->chandef->width ||
748 !(a->chandef->chan->band == NL80211_BAND_6GHZ &&
749 b->chandef->chan->band == NL80211_BAND_5GHZ))
750 ret |= IWL_MVM_ESR_EXIT_BANDWIDTH;
751
752 if (ret) {
753 IWL_DEBUG_INFO(mvm,
754 "Links %d and %d are not a valid pair for EMLSR\n",
755 a->link_id, b->link_id);
756 iwl_mvm_print_esr_state(mvm, ret);
757 return false;
758 }
759
760 return true;
761
762 }
763 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair);
764
765 /*
766 * Returns the combined eSR grade of two given links.
767 * Returns 0 if eSR is not allowed with these 2 links.
768 */
769 static
iwl_mvm_get_esr_grade(struct ieee80211_vif * vif,const struct iwl_mvm_link_sel_data * a,const struct iwl_mvm_link_sel_data * b,u8 * primary_id)770 unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif *vif,
771 const struct iwl_mvm_link_sel_data *a,
772 const struct iwl_mvm_link_sel_data *b,
773 u8 *primary_id)
774 {
775 struct ieee80211_bss_conf *primary_conf;
776 struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
777 unsigned int primary_load;
778
779 lockdep_assert_wiphy(wiphy);
780
781 /* a is always primary, b is always secondary */
782 if (b->grade > a->grade)
783 swap(a, b);
784
785 *primary_id = a->link_id;
786
787 if (!iwl_mvm_mld_valid_link_pair(vif, a, b))
788 return 0;
789
790 primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
791
792 if (WARN_ON_ONCE(!primary_conf))
793 return 0;
794
795 primary_load = iwl_mvm_get_chan_load(primary_conf);
796
797 return a->grade +
798 ((b->grade * primary_load) / SCALE_FACTOR);
799 }
800
iwl_mvm_select_links(struct iwl_mvm * mvm,struct ieee80211_vif * vif)801 void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
802 {
803 struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
804 struct iwl_mvm_link_sel_data *best_link;
805 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
806 u32 max_active_links = iwl_mvm_max_active_links(mvm, vif);
807 u16 usable_links = ieee80211_vif_usable_links(vif);
808 u8 best, primary_link, best_in_pair, n_data;
809 u16 max_esr_grade = 0, new_active_links;
810
811 lockdep_assert_wiphy(mvm->hw->wiphy);
812
813 if (!mvmvif->authorized || !ieee80211_vif_is_mld(vif))
814 return;
815
816 if (!IWL_MVM_AUTO_EML_ENABLE)
817 return;
818
819 /* The logic below is a simple version that doesn't suit more than 2
820 * links
821 */
822 WARN_ON_ONCE(max_active_links > 2);
823
824 n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links,
825 &best);
826
827 if (WARN(!n_data, "Couldn't find a valid grade for any link!\n"))
828 return;
829
830 best_link = &data[best];
831 primary_link = best_link->link_id;
832 new_active_links = BIT(best_link->link_id);
833
834 /* eSR is not supported/blocked, or only one usable link */
835 if (max_active_links == 1 || !iwl_mvm_vif_has_esr_cap(mvm, vif) ||
836 mvmvif->esr_disable_reason || n_data == 1)
837 goto set_active;
838
839 for (u8 a = 0; a < n_data; a++)
840 for (u8 b = a + 1; b < n_data; b++) {
841 u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a],
842 &data[b],
843 &best_in_pair);
844
845 if (esr_grade <= max_esr_grade)
846 continue;
847
848 max_esr_grade = esr_grade;
849 primary_link = best_in_pair;
850 new_active_links = BIT(data[a].link_id) |
851 BIT(data[b].link_id);
852 }
853
854 /* No valid pair was found, go with the best link */
855 if (hweight16(new_active_links) <= 1)
856 goto set_active;
857
858 /* For equal grade - prefer EMLSR */
859 if (best_link->grade > max_esr_grade) {
860 primary_link = best_link->link_id;
861 new_active_links = BIT(best_link->link_id);
862 }
863 set_active:
864 IWL_DEBUG_INFO(mvm, "Link selection result: 0x%x. Primary = %d\n",
865 new_active_links, primary_link);
866 ieee80211_set_active_links_async(vif, new_active_links);
867 mvmvif->link_selection_res = new_active_links;
868 mvmvif->link_selection_primary = primary_link;
869 }
870
iwl_mvm_get_primary_link(struct ieee80211_vif * vif)871 u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif)
872 {
873 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
874
875 /* relevant data is written with both locks held, so read with either */
876 lockdep_assert(lockdep_is_held(&mvmvif->mvm->mutex) ||
877 lockdep_is_held(&mvmvif->mvm->hw->wiphy->mtx));
878
879 if (!ieee80211_vif_is_mld(vif))
880 return 0;
881
882 /* In AP mode, there is no primary link */
883 if (vif->type == NL80211_IFTYPE_AP)
884 return __ffs(vif->active_links);
885
886 if (mvmvif->esr_active &&
887 !WARN_ON(!(BIT(mvmvif->primary_link) & vif->active_links)))
888 return mvmvif->primary_link;
889
890 return __ffs(vif->active_links);
891 }
892
893 /*
894 * For non-MLO/single link, this will return the deflink/single active link,
895 * respectively
896 */
iwl_mvm_get_other_link(struct ieee80211_vif * vif,u8 link_id)897 u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id)
898 {
899 switch (hweight16(vif->active_links)) {
900 case 0:
901 return 0;
902 default:
903 WARN_ON(1);
904 fallthrough;
905 case 1:
906 return __ffs(vif->active_links);
907 case 2:
908 return __ffs(vif->active_links & ~BIT(link_id));
909 }
910 }
911
912 /* Reasons that can cause esr prevention */
913 #define IWL_MVM_ESR_PREVENT_REASONS IWL_MVM_ESR_EXIT_MISSED_BEACON
914 #define IWL_MVM_PREVENT_ESR_TIMEOUT (HZ * 400)
915 #define IWL_MVM_ESR_PREVENT_SHORT (HZ * 300)
916 #define IWL_MVM_ESR_PREVENT_LONG (HZ * 600)
917
iwl_mvm_check_esr_prevention(struct iwl_mvm * mvm,struct iwl_mvm_vif * mvmvif,enum iwl_mvm_esr_state reason)918 static bool iwl_mvm_check_esr_prevention(struct iwl_mvm *mvm,
919 struct iwl_mvm_vif *mvmvif,
920 enum iwl_mvm_esr_state reason)
921 {
922 bool timeout_expired = time_after(jiffies,
923 mvmvif->last_esr_exit.ts +
924 IWL_MVM_PREVENT_ESR_TIMEOUT);
925 unsigned long delay;
926
927 lockdep_assert_held(&mvm->mutex);
928
929 /* Only handle reasons that can cause prevention */
930 if (!(reason & IWL_MVM_ESR_PREVENT_REASONS))
931 return false;
932
933 /*
934 * Reset the counter if more than 400 seconds have passed between one
935 * exit and the other, or if we exited due to a different reason.
936 * Will also reset the counter after the long prevention is done.
937 */
938 if (timeout_expired || mvmvif->last_esr_exit.reason != reason) {
939 mvmvif->exit_same_reason_count = 1;
940 return false;
941 }
942
943 mvmvif->exit_same_reason_count++;
944 if (WARN_ON(mvmvif->exit_same_reason_count < 2 ||
945 mvmvif->exit_same_reason_count > 3))
946 return false;
947
948 mvmvif->esr_disable_reason |= IWL_MVM_ESR_BLOCKED_PREVENTION;
949
950 /*
951 * For the second exit, use a short prevention, and for the third one,
952 * use a long prevention.
953 */
954 delay = mvmvif->exit_same_reason_count == 2 ?
955 IWL_MVM_ESR_PREVENT_SHORT :
956 IWL_MVM_ESR_PREVENT_LONG;
957
958 IWL_DEBUG_INFO(mvm,
959 "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
960 delay / HZ, mvmvif->exit_same_reason_count,
961 iwl_get_esr_state_string(reason), reason);
962
963 wiphy_delayed_work_queue(mvm->hw->wiphy,
964 &mvmvif->prevent_esr_done_wk, delay);
965 return true;
966 }
967
968 #define IWL_MVM_TRIGGER_LINK_SEL_TIME (IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC * HZ)
969
970 /* API to exit eSR mode */
iwl_mvm_exit_esr(struct iwl_mvm * mvm,struct ieee80211_vif * vif,enum iwl_mvm_esr_state reason,u8 link_to_keep)971 void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
972 enum iwl_mvm_esr_state reason,
973 u8 link_to_keep)
974 {
975 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
976 u16 new_active_links;
977 bool prevented;
978
979 lockdep_assert_held(&mvm->mutex);
980
981 /* Nothing to do */
982 if (!mvmvif->esr_active)
983 return;
984
985 if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mvmvif->authorized))
986 return;
987
988 if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
989 link_to_keep = __ffs(vif->active_links);
990
991 new_active_links = BIT(link_to_keep);
992 IWL_DEBUG_INFO(mvm,
993 "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
994 iwl_get_esr_state_string(reason), reason,
995 vif->active_links, new_active_links);
996
997 ieee80211_set_active_links_async(vif, new_active_links);
998
999 /* Prevent EMLSR if needed */
1000 prevented = iwl_mvm_check_esr_prevention(mvm, mvmvif, reason);
1001
1002 /* Remember why and when we exited EMLSR */
1003 mvmvif->last_esr_exit.ts = jiffies;
1004 mvmvif->last_esr_exit.reason = reason;
1005
1006 /*
1007 * If EMLSR is prevented now - don't try to get back to EMLSR.
1008 * If we exited due to a blocking event, we will try to get back to
1009 * EMLSR when the corresponding unblocking event will happen.
1010 */
1011 if (prevented || reason & IWL_MVM_BLOCK_ESR_REASONS)
1012 return;
1013
1014 /* If EMLSR is not blocked - try enabling it again in 30 seconds */
1015 wiphy_delayed_work_queue(mvm->hw->wiphy,
1016 &mvmvif->mlo_int_scan_wk,
1017 round_jiffies_relative(IWL_MVM_TRIGGER_LINK_SEL_TIME));
1018 }
1019
iwl_mvm_block_esr(struct iwl_mvm * mvm,struct ieee80211_vif * vif,enum iwl_mvm_esr_state reason,u8 link_to_keep)1020 void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1021 enum iwl_mvm_esr_state reason,
1022 u8 link_to_keep)
1023 {
1024 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1025
1026 lockdep_assert_held(&mvm->mutex);
1027
1028 /* This should be called only with disable reasons */
1029 if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
1030 return;
1031
1032 if (!(mvmvif->esr_disable_reason & reason)) {
1033 IWL_DEBUG_INFO(mvm,
1034 "Blocking EMLSR mode. reason = %s (0x%x)\n",
1035 iwl_get_esr_state_string(reason), reason);
1036 iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason);
1037 }
1038
1039 mvmvif->esr_disable_reason |= reason;
1040
1041 iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
1042 }
1043
iwl_mvm_block_esr_sync(struct iwl_mvm * mvm,struct ieee80211_vif * vif,enum iwl_mvm_esr_state reason)1044 int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1045 enum iwl_mvm_esr_state reason)
1046 {
1047 int primary_link = iwl_mvm_get_primary_link(vif);
1048 int ret;
1049
1050 if (!IWL_MVM_AUTO_EML_ENABLE || !ieee80211_vif_is_mld(vif))
1051 return 0;
1052
1053 /* This should be called only with blocking reasons */
1054 if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
1055 return 0;
1056
1057 /* leave ESR immediately, not only async with iwl_mvm_block_esr() */
1058 ret = ieee80211_set_active_links(vif, BIT(primary_link));
1059 if (ret)
1060 return ret;
1061
1062 mutex_lock(&mvm->mutex);
1063 /* only additionally block for consistency and to avoid concurrency */
1064 iwl_mvm_block_esr(mvm, vif, reason, primary_link);
1065 mutex_unlock(&mvm->mutex);
1066
1067 return 0;
1068 }
1069
iwl_mvm_esr_unblocked(struct iwl_mvm * mvm,struct ieee80211_vif * vif)1070 static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm,
1071 struct ieee80211_vif *vif)
1072 {
1073 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1074 bool need_new_sel = time_after(jiffies, mvmvif->last_esr_exit.ts +
1075 IWL_MVM_TRIGGER_LINK_SEL_TIME);
1076
1077 lockdep_assert_held(&mvm->mutex);
1078
1079 if (!ieee80211_vif_is_mld(vif) || !mvmvif->authorized ||
1080 mvmvif->esr_active)
1081 return;
1082
1083 IWL_DEBUG_INFO(mvm, "EMLSR is unblocked\n");
1084
1085 /*
1086 * If EMLSR was blocked for more than 30 seconds, or the last link
1087 * selection decided to not enter EMLSR, trigger a new scan.
1088 */
1089 if (need_new_sel || hweight16(mvmvif->link_selection_res) < 2) {
1090 IWL_DEBUG_INFO(mvm, "Trigger MLO scan\n");
1091 wiphy_delayed_work_queue(mvm->hw->wiphy,
1092 &mvmvif->mlo_int_scan_wk, 0);
1093 /*
1094 * If EMLSR was blocked for less than 30 seconds, and the last link
1095 * selection decided to use EMLSR, activate EMLSR using the previous
1096 * link selection result.
1097 */
1098 } else {
1099 IWL_DEBUG_INFO(mvm,
1100 "Use the latest link selection result: 0x%x\n",
1101 mvmvif->link_selection_res);
1102 ieee80211_set_active_links_async(vif,
1103 mvmvif->link_selection_res);
1104 }
1105 }
1106
iwl_mvm_unblock_esr(struct iwl_mvm * mvm,struct ieee80211_vif * vif,enum iwl_mvm_esr_state reason)1107 void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1108 enum iwl_mvm_esr_state reason)
1109 {
1110 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1111
1112 lockdep_assert_held(&mvm->mutex);
1113
1114 /* This should be called only with disable reasons */
1115 if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
1116 return;
1117
1118 /* No Change */
1119 if (!(mvmvif->esr_disable_reason & reason))
1120 return;
1121
1122 mvmvif->esr_disable_reason &= ~reason;
1123
1124 IWL_DEBUG_INFO(mvm,
1125 "Unblocking EMLSR mode. reason = %s (0x%x)\n",
1126 iwl_get_esr_state_string(reason), reason);
1127 iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason);
1128
1129 if (!mvmvif->esr_disable_reason)
1130 iwl_mvm_esr_unblocked(mvm, vif);
1131 }
1132