1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
3 * Copyright (C) 2022 - 2024 Intel Corporation
6 #include "time-event.h"
8 #define HANDLE_ESR_REASONS(HOW) \
9 HOW(BLOCKED_PREVENTION) \
13 HOW(BLOCKED_NON_BSS) \
15 HOW(BLOCKED_TMP_NON_BSS) \
16 HOW(EXIT_MISSED_BEACON) \
21 HOW(EXIT_LINK_USAGE) \
24 static const char *const iwl_mvm_esr_states_names
[] = {
25 #define NAME_ENTRY(x) [ilog2(IWL_MVM_ESR_##x)] = #x,
26 HANDLE_ESR_REASONS(NAME_ENTRY
)
29 const char *iwl_get_esr_state_string(enum iwl_mvm_esr_state state
)
31 int offs
= ilog2(state
);
33 if (offs
>= ARRAY_SIZE(iwl_mvm_esr_states_names
) ||
34 !iwl_mvm_esr_states_names
[offs
])
37 return iwl_mvm_esr_states_names
[offs
];
40 static void iwl_mvm_print_esr_state(struct iwl_mvm
*mvm
, u32 mask
)
42 #define NAME_FMT(x) "%s"
43 #define NAME_PR(x) (mask & IWL_MVM_ESR_##x) ? "[" #x "]" : "",
45 "EMLSR state = " HANDLE_ESR_REASONS(NAME_FMT
)
47 HANDLE_ESR_REASONS(NAME_PR
)
53 static u32
iwl_mvm_get_free_fw_link_id(struct iwl_mvm
*mvm
,
54 struct iwl_mvm_vif
*mvm_vif
)
58 lockdep_assert_held(&mvm
->mutex
);
60 for (i
= 0; i
< ARRAY_SIZE(mvm
->link_id_to_link_conf
); i
++)
61 if (!rcu_access_pointer(mvm
->link_id_to_link_conf
[i
]))
64 return IWL_MVM_FW_LINK_ID_INVALID
;
67 static int iwl_mvm_link_cmd_send(struct iwl_mvm
*mvm
,
68 struct iwl_link_config_cmd
*cmd
,
69 enum iwl_ctxt_action action
)
73 cmd
->action
= cpu_to_le32(action
);
74 ret
= iwl_mvm_send_cmd_pdu(mvm
,
75 WIDE_ID(MAC_CONF_GROUP
, LINK_CONFIG_CMD
), 0,
78 IWL_ERR(mvm
, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
83 int iwl_mvm_set_link_mapping(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
84 struct ieee80211_bss_conf
*link_conf
)
86 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
87 struct iwl_mvm_vif_link_info
*link_info
=
88 mvmvif
->link
[link_conf
->link_id
];
90 if (link_info
->fw_link_id
== IWL_MVM_FW_LINK_ID_INVALID
) {
91 link_info
->fw_link_id
= iwl_mvm_get_free_fw_link_id(mvm
,
93 if (link_info
->fw_link_id
>=
94 ARRAY_SIZE(mvm
->link_id_to_link_conf
))
97 rcu_assign_pointer(mvm
->link_id_to_link_conf
[link_info
->fw_link_id
],
104 int iwl_mvm_add_link(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
105 struct ieee80211_bss_conf
*link_conf
)
107 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
108 unsigned int link_id
= link_conf
->link_id
;
109 struct iwl_mvm_vif_link_info
*link_info
= mvmvif
->link
[link_id
];
110 struct iwl_link_config_cmd cmd
= {};
111 unsigned int cmd_id
= WIDE_ID(MAC_CONF_GROUP
, LINK_CONFIG_CMD
);
112 u8 cmd_ver
= iwl_fw_lookup_cmd_ver(mvm
->fw
, cmd_id
, 1);
115 if (WARN_ON_ONCE(!link_info
))
118 ret
= iwl_mvm_set_link_mapping(mvm
, vif
, link_conf
);
122 /* Update SF - Disable if needed. if this fails, SF might still be on
123 * while many macs are bound, which is forbidden - so fail the binding.
125 if (iwl_mvm_sf_update(mvm
, vif
, false))
128 cmd
.link_id
= cpu_to_le32(link_info
->fw_link_id
);
129 cmd
.mac_id
= cpu_to_le32(mvmvif
->id
);
130 cmd
.spec_link_id
= link_conf
->link_id
;
131 WARN_ON_ONCE(link_info
->phy_ctxt
);
132 cmd
.phy_id
= cpu_to_le32(FW_CTXT_INVALID
);
134 memcpy(cmd
.local_link_addr
, link_conf
->addr
, ETH_ALEN
);
136 if (vif
->type
== NL80211_IFTYPE_ADHOC
&& link_conf
->bssid
)
137 memcpy(cmd
.ibss_bssid_addr
, link_conf
->bssid
, ETH_ALEN
);
140 cmd
.listen_lmac
= cpu_to_le32(link_info
->listen_lmac
);
142 return iwl_mvm_link_cmd_send(mvm
, &cmd
, FW_CTXT_ACTION_ADD
);
145 struct iwl_mvm_esr_iter_data
{
146 struct ieee80211_vif
*vif
;
147 unsigned int link_id
;
151 static void iwl_mvm_esr_vif_iterator(void *_data
, u8
*mac
,
152 struct ieee80211_vif
*vif
)
154 struct iwl_mvm_esr_iter_data
*data
= _data
;
155 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
158 if (ieee80211_vif_type_p2p(vif
) == NL80211_IFTYPE_STATION
)
161 for_each_mvm_vif_valid_link(mvmvif
, link_id
) {
162 struct iwl_mvm_vif_link_info
*link_info
=
163 mvmvif
->link
[link_id
];
164 if (vif
== data
->vif
&& link_id
== data
->link_id
)
166 if (link_info
->active
)
167 data
->lift_block
= false;
171 int iwl_mvm_esr_non_bss_link(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
172 unsigned int link_id
, bool active
)
174 /* An active link of a non-station vif blocks EMLSR. Upon activation
175 * block EMLSR on the bss vif. Upon deactivation, check if this link
176 * was the last non-station link active, and if so unblock the bss vif
178 struct ieee80211_vif
*bss_vif
= iwl_mvm_get_bss_vif(mvm
);
179 struct iwl_mvm_esr_iter_data data
= {
185 if (IS_ERR_OR_NULL(bss_vif
))
189 return iwl_mvm_block_esr_sync(mvm
, bss_vif
,
190 IWL_MVM_ESR_BLOCKED_NON_BSS
);
192 ieee80211_iterate_active_interfaces(mvm
->hw
,
193 IEEE80211_IFACE_ITER_NORMAL
,
194 iwl_mvm_esr_vif_iterator
, &data
);
195 if (data
.lift_block
) {
196 mutex_lock(&mvm
->mutex
);
197 iwl_mvm_unblock_esr(mvm
, bss_vif
, IWL_MVM_ESR_BLOCKED_NON_BSS
);
198 mutex_unlock(&mvm
->mutex
);
204 int iwl_mvm_link_changed(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
205 struct ieee80211_bss_conf
*link_conf
,
206 u32 changes
, bool active
)
208 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
209 unsigned int link_id
= link_conf
->link_id
;
210 struct iwl_mvm_vif_link_info
*link_info
= mvmvif
->link
[link_id
];
211 struct iwl_mvm_phy_ctxt
*phyctxt
;
212 struct iwl_link_config_cmd cmd
= {};
213 u32 ht_flag
, flags
= 0, flags_mask
= 0;
215 unsigned int cmd_id
= WIDE_ID(MAC_CONF_GROUP
, LINK_CONFIG_CMD
);
216 u8 cmd_ver
= iwl_fw_lookup_cmd_ver(mvm
->fw
, cmd_id
, 1);
218 if (WARN_ON_ONCE(!link_info
||
219 link_info
->fw_link_id
== IWL_MVM_FW_LINK_ID_INVALID
))
222 if (changes
& LINK_CONTEXT_MODIFY_ACTIVE
) {
223 /* When activating a link, phy context should be valid;
224 * when deactivating a link, it also should be valid since
225 * the link was active before. So, do nothing in this case.
226 * Since a link is added first with FW_CTXT_INVALID, then we
227 * can get here in case it's removed before it was activated.
229 if (!link_info
->phy_ctxt
)
232 /* Catch early if driver tries to activate or deactivate a link
235 WARN_ON_ONCE(active
== link_info
->active
);
237 /* When deactivating a link session protection should
238 * be stopped. Also let the firmware know if we can't Tx.
240 if (!active
&& vif
->type
== NL80211_IFTYPE_STATION
) {
241 iwl_mvm_stop_session_protection(mvm
, vif
);
242 if (link_info
->csa_block_tx
) {
244 link_info
->csa_block_tx
= false;
249 cmd
.link_id
= cpu_to_le32(link_info
->fw_link_id
);
251 /* The phy_id, link address and listen_lmac can be modified only until
252 * the link becomes active, otherwise they will be ignored.
254 phyctxt
= link_info
->phy_ctxt
;
256 cmd
.phy_id
= cpu_to_le32(phyctxt
->id
);
258 cmd
.phy_id
= cpu_to_le32(FW_CTXT_INVALID
);
259 cmd
.mac_id
= cpu_to_le32(mvmvif
->id
);
261 memcpy(cmd
.local_link_addr
, link_conf
->addr
, ETH_ALEN
);
263 cmd
.active
= cpu_to_le32(active
);
265 if (vif
->type
== NL80211_IFTYPE_ADHOC
&& link_conf
->bssid
)
266 memcpy(cmd
.ibss_bssid_addr
, link_conf
->bssid
, ETH_ALEN
);
268 iwl_mvm_set_fw_basic_rates(mvm
, vif
, link_info
,
269 &cmd
.cck_rates
, &cmd
.ofdm_rates
);
271 cmd
.cck_short_preamble
= cpu_to_le32(link_conf
->use_short_preamble
);
272 cmd
.short_slot
= cpu_to_le32(link_conf
->use_short_slot
);
274 /* The fw does not distinguish between ht and fat */
275 ht_flag
= LINK_PROT_FLG_HT_PROT
| LINK_PROT_FLG_FAT_PROT
;
276 iwl_mvm_set_fw_protection_flags(mvm
, vif
, link_conf
,
277 &cmd
.protection_flags
,
278 ht_flag
, LINK_PROT_FLG_TGG_PROTECT
);
280 iwl_mvm_set_fw_qos_params(mvm
, vif
, link_conf
, cmd
.ac
,
284 cmd
.bi
= cpu_to_le32(link_conf
->beacon_int
);
285 cmd
.dtim_interval
= cpu_to_le32(link_conf
->beacon_int
*
286 link_conf
->dtim_period
);
288 if (!link_conf
->he_support
|| iwlwifi_mod_params
.disable_11ax
||
289 (vif
->type
== NL80211_IFTYPE_STATION
&& !vif
->cfg
.assoc
)) {
290 changes
&= ~LINK_CONTEXT_MODIFY_HE_PARAMS
;
294 cmd
.htc_trig_based_pkt_ext
= link_conf
->htc_trig_based_pkt_ext
;
296 if (link_conf
->uora_exists
) {
297 cmd
.rand_alloc_ecwmin
=
298 link_conf
->uora_ocw_range
& 0x7;
299 cmd
.rand_alloc_ecwmax
=
300 (link_conf
->uora_ocw_range
>> 3) & 0x7;
303 /* ap_sta may be NULL if we're disconnecting */
304 if (changes
& LINK_CONTEXT_MODIFY_HE_PARAMS
&& mvmvif
->ap_sta
) {
305 struct ieee80211_link_sta
*link_sta
=
306 link_sta_dereference_check(mvmvif
->ap_sta
, link_id
);
308 if (!WARN_ON(!link_sta
) && link_sta
->he_cap
.has_he
&&
309 link_sta
->he_cap
.he_cap_elem
.mac_cap_info
[5] &
310 IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX
)
311 cmd
.ul_mu_data_disable
= 1;
314 /* TODO how to set ndp_fdbk_buff_th_exp? */
316 if (iwl_mvm_set_fw_mu_edca_params(mvm
, mvmvif
->link
[link_id
],
317 &cmd
.trig_based_txf
[0])) {
318 flags
|= LINK_FLG_MU_EDCA_CW
;
319 flags_mask
|= LINK_FLG_MU_EDCA_CW
;
322 if (changes
& LINK_CONTEXT_MODIFY_EHT_PARAMS
) {
323 struct ieee80211_chanctx_conf
*ctx
;
324 struct cfg80211_chan_def
*def
= NULL
;
327 ctx
= rcu_dereference(link_conf
->chanctx_conf
);
329 def
= iwl_mvm_chanctx_def(mvm
, ctx
);
331 if (iwlwifi_mod_params
.disable_11be
||
332 !link_conf
->eht_support
|| !def
||
333 iwl_fw_lookup_cmd_ver(mvm
->fw
, PHY_CONTEXT_CMD
, 1) >= 6)
334 changes
&= ~LINK_CONTEXT_MODIFY_EHT_PARAMS
;
336 cmd
.puncture_mask
= cpu_to_le16(def
->punctured
);
340 cmd
.bss_color
= link_conf
->he_bss_color
.color
;
342 if (!link_conf
->he_bss_color
.enabled
) {
343 flags
|= LINK_FLG_BSS_COLOR_DIS
;
344 flags_mask
|= LINK_FLG_BSS_COLOR_DIS
;
347 cmd
.frame_time_rts_th
= cpu_to_le16(link_conf
->frame_time_rts_th
);
349 /* Block 26-tone RU OFDMA transmissions */
350 if (link_info
->he_ru_2mhz_block
) {
351 flags
|= LINK_FLG_RU_2MHZ_BLOCK
;
352 flags_mask
|= LINK_FLG_RU_2MHZ_BLOCK
;
355 if (link_conf
->nontransmitted
) {
356 ether_addr_copy(cmd
.ref_bssid_addr
,
357 link_conf
->transmitter_bssid
);
358 cmd
.bssid_index
= link_conf
->bssid_index
;
362 cmd
.modify_mask
= cpu_to_le32(changes
);
363 cmd
.flags
= cpu_to_le32(flags
);
365 cmd
.flags_mask
= cpu_to_le32(flags_mask
);
366 cmd
.spec_link_id
= link_conf
->link_id
;
368 cmd
.listen_lmac
= cpu_to_le32(link_info
->listen_lmac
);
370 ret
= iwl_mvm_link_cmd_send(mvm
, &cmd
, FW_CTXT_ACTION_MODIFY
);
371 if (!ret
&& (changes
& LINK_CONTEXT_MODIFY_ACTIVE
))
372 link_info
->active
= active
;
377 int iwl_mvm_unset_link_mapping(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
378 struct ieee80211_bss_conf
*link_conf
)
380 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
381 struct iwl_mvm_vif_link_info
*link_info
=
382 mvmvif
->link
[link_conf
->link_id
];
384 /* mac80211 thought we have the link, but it was never configured */
385 if (WARN_ON(!link_info
||
386 link_info
->fw_link_id
>=
387 ARRAY_SIZE(mvm
->link_id_to_link_conf
)))
390 RCU_INIT_POINTER(mvm
->link_id_to_link_conf
[link_info
->fw_link_id
],
395 int iwl_mvm_remove_link(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
396 struct ieee80211_bss_conf
*link_conf
)
398 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
399 unsigned int link_id
= link_conf
->link_id
;
400 struct iwl_mvm_vif_link_info
*link_info
= mvmvif
->link
[link_id
];
401 struct iwl_link_config_cmd cmd
= {};
404 ret
= iwl_mvm_unset_link_mapping(mvm
, vif
, link_conf
);
408 cmd
.link_id
= cpu_to_le32(link_info
->fw_link_id
);
409 link_info
->fw_link_id
= IWL_MVM_FW_LINK_ID_INVALID
;
410 cmd
.spec_link_id
= link_conf
->link_id
;
411 cmd
.phy_id
= cpu_to_le32(FW_CTXT_INVALID
);
413 ret
= iwl_mvm_link_cmd_send(mvm
, &cmd
, FW_CTXT_ACTION_REMOVE
);
416 if (iwl_mvm_sf_update(mvm
, vif
, true))
417 IWL_ERR(mvm
, "Failed to update SF state\n");
422 /* link should be deactivated before removal, so in most cases we need to
423 * perform these two operations together
425 int iwl_mvm_disable_link(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
426 struct ieee80211_bss_conf
*link_conf
)
430 ret
= iwl_mvm_link_changed(mvm
, vif
, link_conf
,
431 LINK_CONTEXT_MODIFY_ACTIVE
, false);
435 ret
= iwl_mvm_remove_link(mvm
, vif
, link_conf
);
442 struct iwl_mvm_rssi_to_grade
{
447 #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
449 .rssi = {_lb, _hb_uhb}, \
454 * This array must be sorted by increasing RSSI for proper functionality.
455 * The grades are actually estimated throughput, represented as fixed-point
456 * with a scale factor of 1/10.
458 static const struct iwl_mvm_rssi_to_grade rssi_to_grade_map
[] = {
459 RSSI_TO_GRADE_LINE(-85, -89, 177),
460 RSSI_TO_GRADE_LINE(-83, -86, 344),
461 RSSI_TO_GRADE_LINE(-82, -85, 516),
462 RSSI_TO_GRADE_LINE(-80, -83, 688),
463 RSSI_TO_GRADE_LINE(-77, -79, 1032),
464 RSSI_TO_GRADE_LINE(-73, -76, 1376),
465 RSSI_TO_GRADE_LINE(-70, -74, 1548),
466 RSSI_TO_GRADE_LINE(-69, -72, 1750),
467 RSSI_TO_GRADE_LINE(-65, -68, 2064),
468 RSSI_TO_GRADE_LINE(-61, -66, 2294),
469 RSSI_TO_GRADE_LINE(-58, -61, 2580),
470 RSSI_TO_GRADE_LINE(-55, -58, 2868),
471 RSSI_TO_GRADE_LINE(-46, -55, 3098),
472 RSSI_TO_GRADE_LINE(-43, -54, 3442)
475 #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
477 #define DEFAULT_CHAN_LOAD_LB 30
478 #define DEFAULT_CHAN_LOAD_HB 15
479 #define DEFAULT_CHAN_LOAD_UHB 0
481 /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
482 #define SCALE_FACTOR 256
484 /* Convert a percentage from [0,100] to [0,255] */
485 #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * SCALE_FACTOR / 100)
488 iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf
*link_conf
)
490 enum nl80211_chan_width chan_width
=
491 link_conf
->chanreq
.oper
.width
;
492 int mhz
= nl80211_chan_width_to_mhz(chan_width
);
493 unsigned int n_subchannels
, n_punctured
, puncturing_penalty
;
495 if (WARN_ONCE(mhz
< 20 || mhz
> 320,
496 "Invalid channel width : (%d)\n", mhz
))
499 /* No puncturing, no penalty */
503 /* total number of subchannels */
504 n_subchannels
= mhz
/ 20;
505 /* how many of these are punctured */
506 n_punctured
= hweight16(link_conf
->chanreq
.oper
.punctured
);
508 puncturing_penalty
= n_punctured
* SCALE_FACTOR
/ n_subchannels
;
509 return SCALE_FACTOR
- puncturing_penalty
;
513 iwl_mvm_get_chan_load(struct ieee80211_bss_conf
*link_conf
)
515 struct ieee80211_vif
*vif
= link_conf
->vif
;
516 struct iwl_mvm_vif_link_info
*mvm_link
=
517 iwl_mvm_vif_from_mac80211(link_conf
->vif
)->link
[link_conf
->link_id
];
518 const struct element
*bss_load_elem
;
519 const struct ieee80211_bss_load_elem
*bss_load
;
520 enum nl80211_band band
= link_conf
->chanreq
.oper
.chan
->band
;
521 const struct cfg80211_bss_ies
*ies
;
522 unsigned int chan_load
;
526 if (ieee80211_vif_link_active(vif
, link_conf
->link_id
))
527 ies
= rcu_dereference(link_conf
->bss
->beacon_ies
);
529 ies
= rcu_dereference(link_conf
->bss
->ies
);
532 bss_load_elem
= cfg80211_find_elem(WLAN_EID_QBSS_LOAD
,
533 ies
->data
, ies
->len
);
535 bss_load_elem
= NULL
;
537 /* If there isn't BSS Load element, take the defaults */
538 if (!bss_load_elem
||
539 bss_load_elem
->datalen
!= sizeof(*bss_load
)) {
542 case NL80211_BAND_2GHZ
:
543 chan_load
= DEFAULT_CHAN_LOAD_LB
;
545 case NL80211_BAND_5GHZ
:
546 chan_load
= DEFAULT_CHAN_LOAD_HB
;
548 case NL80211_BAND_6GHZ
:
549 chan_load
= DEFAULT_CHAN_LOAD_UHB
;
555 /* The defaults are given in percentage */
556 return NORMALIZE_PERCENT_TO_255(chan_load
);
559 bss_load
= (const void *)bss_load_elem
->data
;
560 /* Channel util is in range 0-255 */
561 chan_load
= bss_load
->channel_util
;
564 if (!mvm_link
|| !mvm_link
->active
)
567 if (WARN_ONCE(!mvm_link
->phy_ctxt
,
568 "Active link (%u) without phy ctxt assigned!\n",
572 /* channel load by us is given in percentage */
574 NORMALIZE_PERCENT_TO_255(mvm_link
->phy_ctxt
->channel_load_by_us
);
576 /* Use only values that firmware sends that can possibly be valid */
577 if (chan_load_by_us
<= chan_load
)
578 chan_load
-= chan_load_by_us
;
584 iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf
*link_conf
)
586 return SCALE_FACTOR
- iwl_mvm_get_chan_load(link_conf
);
589 /* This function calculates the grade of a link. Returns 0 in error case */
590 VISIBLE_IF_IWLWIFI_KUNIT
591 unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf
*link_conf
)
593 enum nl80211_band band
;
596 unsigned int grade
= MAX_GRADE
;
598 if (WARN_ON_ONCE(!link_conf
))
601 band
= link_conf
->chanreq
.oper
.chan
->band
;
602 if (WARN_ONCE(band
!= NL80211_BAND_2GHZ
&&
603 band
!= NL80211_BAND_5GHZ
&&
604 band
!= NL80211_BAND_6GHZ
,
605 "Invalid band (%u)\n", band
))
608 link_rssi
= MBM_TO_DBM(link_conf
->bss
->signal
);
610 * For 6 GHz the RSSI of the beacons is lower than
611 * the RSSI of the data.
613 if (band
== NL80211_BAND_6GHZ
)
616 rssi_idx
= band
== NL80211_BAND_2GHZ
? 0 : 1;
618 /* No valid RSSI - take the lowest grade */
620 link_rssi
= rssi_to_grade_map
[0].rssi
[rssi_idx
];
622 /* Get grade based on RSSI */
623 for (i
= 0; i
< ARRAY_SIZE(rssi_to_grade_map
); i
++) {
624 const struct iwl_mvm_rssi_to_grade
*line
=
625 &rssi_to_grade_map
[i
];
627 if (link_rssi
> line
->rssi
[rssi_idx
])
633 /* apply the channel load and puncturing factors */
634 grade
= grade
* iwl_mvm_get_chan_load_factor(link_conf
) / SCALE_FACTOR
;
635 grade
= grade
* iwl_mvm_get_puncturing_factor(link_conf
) / SCALE_FACTOR
;
638 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade
);
641 u8
iwl_mvm_set_link_selection_data(struct ieee80211_vif
*vif
,
642 struct iwl_mvm_link_sel_data
*data
,
643 unsigned long usable_links
,
648 unsigned long link_id
;
650 /* TODO: don't select links that weren't discovered in the last scan */
651 for_each_set_bit(link_id
, &usable_links
, IEEE80211_MLD_MAX_NUM_LINKS
) {
652 struct ieee80211_bss_conf
*link_conf
=
653 link_conf_dereference_protected(vif
, link_id
);
655 if (WARN_ON_ONCE(!link_conf
))
658 data
[n_data
].link_id
= link_id
;
659 data
[n_data
].chandef
= &link_conf
->chanreq
.oper
;
660 data
[n_data
].signal
= link_conf
->bss
->signal
/ 100;
661 data
[n_data
].grade
= iwl_mvm_get_link_grade(link_conf
);
663 if (data
[n_data
].grade
> max_grade
) {
664 max_grade
= data
[n_data
].grade
;
665 *best_link_idx
= n_data
;
673 struct iwl_mvm_bw_to_rssi_threshs
{
678 #define BW_TO_RSSI_THRESHOLDS(_bw) \
679 [IWL_PHY_CHANNEL_MODE ## _bw] = { \
680 .low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ, \
681 .high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ \
684 s8
iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm
*mvm
,
685 const struct cfg80211_chan_def
*chandef
,
688 const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map
[] = {
689 BW_TO_RSSI_THRESHOLDS(20),
690 BW_TO_RSSI_THRESHOLDS(40),
691 BW_TO_RSSI_THRESHOLDS(80),
692 BW_TO_RSSI_THRESHOLDS(160)
693 /* 320 MHz has the same thresholds as 20 MHz */
695 const struct iwl_mvm_bw_to_rssi_threshs
*threshs
;
696 u8 chan_width
= iwl_mvm_get_channel_width(chandef
);
698 if (WARN_ON(chandef
->chan
->band
!= NL80211_BAND_2GHZ
&&
699 chandef
->chan
->band
!= NL80211_BAND_5GHZ
&&
700 chandef
->chan
->band
!= NL80211_BAND_6GHZ
))
703 /* 6 GHz will always use 20 MHz thresholds, regardless of the BW */
704 if (chan_width
== IWL_PHY_CHANNEL_MODE320
)
705 chan_width
= IWL_PHY_CHANNEL_MODE20
;
707 threshs
= &bw_to_rssi_threshs_map
[chan_width
];
709 return low
? threshs
->low
: threshs
->high
;
713 iwl_mvm_esr_disallowed_with_link(struct iwl_mvm
*mvm
,
714 struct ieee80211_vif
*vif
,
715 const struct iwl_mvm_link_sel_data
*link
,
718 struct wiphy
*wiphy
= mvm
->hw
->wiphy
;
719 struct ieee80211_bss_conf
*conf
;
720 enum iwl_mvm_esr_state ret
= 0;
723 conf
= wiphy_dereference(wiphy
, vif
->link_conf
[link
->link_id
]);
724 if (WARN_ON_ONCE(!conf
))
727 /* BT Coex effects eSR mode only if one of the links is on LB */
728 if (link
->chandef
->chan
->band
== NL80211_BAND_2GHZ
&&
729 (!iwl_mvm_bt_coex_calculate_esr_mode(mvm
, vif
, link
->signal
,
731 ret
|= IWL_MVM_ESR_EXIT_COEX
;
733 thresh
= iwl_mvm_get_esr_rssi_thresh(mvm
, link
->chandef
,
736 if (link
->signal
< thresh
)
737 ret
|= IWL_MVM_ESR_EXIT_LOW_RSSI
;
739 if (conf
->csa_active
)
740 ret
|= IWL_MVM_ESR_EXIT_CSA
;
744 "Link %d is not allowed for esr\n",
746 iwl_mvm_print_esr_state(mvm
, ret
);
751 VISIBLE_IF_IWLWIFI_KUNIT
752 bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif
*vif
,
753 const struct iwl_mvm_link_sel_data
*a
,
754 const struct iwl_mvm_link_sel_data
*b
)
756 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
757 struct iwl_mvm
*mvm
= mvmvif
->mvm
;
758 enum iwl_mvm_esr_state ret
= 0;
760 /* Per-link considerations */
761 if (iwl_mvm_esr_disallowed_with_link(mvm
, vif
, a
, true) ||
762 iwl_mvm_esr_disallowed_with_link(mvm
, vif
, b
, false))
765 if (a
->chandef
->width
!= b
->chandef
->width
||
766 !(a
->chandef
->chan
->band
== NL80211_BAND_6GHZ
&&
767 b
->chandef
->chan
->band
== NL80211_BAND_5GHZ
))
768 ret
|= IWL_MVM_ESR_EXIT_BANDWIDTH
;
772 "Links %d and %d are not a valid pair for EMLSR\n",
773 a
->link_id
, b
->link_id
);
774 iwl_mvm_print_esr_state(mvm
, ret
);
781 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair
);
784 * Returns the combined eSR grade of two given links.
785 * Returns 0 if eSR is not allowed with these 2 links.
788 unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif
*vif
,
789 const struct iwl_mvm_link_sel_data
*a
,
790 const struct iwl_mvm_link_sel_data
*b
,
793 struct ieee80211_bss_conf
*primary_conf
;
794 struct wiphy
*wiphy
= ieee80211_vif_to_wdev(vif
)->wiphy
;
795 unsigned int primary_load
;
797 lockdep_assert_wiphy(wiphy
);
799 /* a is always primary, b is always secondary */
800 if (b
->grade
> a
->grade
)
803 *primary_id
= a
->link_id
;
805 if (!iwl_mvm_mld_valid_link_pair(vif
, a
, b
))
808 primary_conf
= wiphy_dereference(wiphy
, vif
->link_conf
[*primary_id
]);
810 if (WARN_ON_ONCE(!primary_conf
))
813 primary_load
= iwl_mvm_get_chan_load(primary_conf
);
816 ((b
->grade
* primary_load
) / SCALE_FACTOR
);
819 void iwl_mvm_select_links(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
)
821 struct iwl_mvm_link_sel_data data
[IEEE80211_MLD_MAX_NUM_LINKS
];
822 struct iwl_mvm_link_sel_data
*best_link
;
823 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
824 u32 max_active_links
= iwl_mvm_max_active_links(mvm
, vif
);
825 u16 usable_links
= ieee80211_vif_usable_links(vif
);
826 u8 best
, primary_link
, best_in_pair
, n_data
;
827 u16 max_esr_grade
= 0, new_active_links
;
829 lockdep_assert_wiphy(mvm
->hw
->wiphy
);
831 if (!mvmvif
->authorized
|| !ieee80211_vif_is_mld(vif
))
834 if (!IWL_MVM_AUTO_EML_ENABLE
)
837 /* The logic below is a simple version that doesn't suit more than 2
840 WARN_ON_ONCE(max_active_links
> 2);
842 n_data
= iwl_mvm_set_link_selection_data(vif
, data
, usable_links
,
845 if (WARN(!n_data
, "Couldn't find a valid grade for any link!\n"))
848 best_link
= &data
[best
];
849 primary_link
= best_link
->link_id
;
850 new_active_links
= BIT(best_link
->link_id
);
852 /* eSR is not supported/blocked, or only one usable link */
853 if (max_active_links
== 1 || !iwl_mvm_vif_has_esr_cap(mvm
, vif
) ||
854 mvmvif
->esr_disable_reason
|| n_data
== 1)
857 for (u8 a
= 0; a
< n_data
; a
++)
858 for (u8 b
= a
+ 1; b
< n_data
; b
++) {
859 u16 esr_grade
= iwl_mvm_get_esr_grade(vif
, &data
[a
],
863 if (esr_grade
<= max_esr_grade
)
866 max_esr_grade
= esr_grade
;
867 primary_link
= best_in_pair
;
868 new_active_links
= BIT(data
[a
].link_id
) |
869 BIT(data
[b
].link_id
);
872 /* No valid pair was found, go with the best link */
873 if (hweight16(new_active_links
) <= 1)
876 /* For equal grade - prefer EMLSR */
877 if (best_link
->grade
> max_esr_grade
) {
878 primary_link
= best_link
->link_id
;
879 new_active_links
= BIT(best_link
->link_id
);
882 IWL_DEBUG_INFO(mvm
, "Link selection result: 0x%x. Primary = %d\n",
883 new_active_links
, primary_link
);
884 ieee80211_set_active_links_async(vif
, new_active_links
);
885 mvmvif
->link_selection_res
= new_active_links
;
886 mvmvif
->link_selection_primary
= primary_link
;
889 u8
iwl_mvm_get_primary_link(struct ieee80211_vif
*vif
)
891 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
893 /* relevant data is written with both locks held, so read with either */
894 lockdep_assert(lockdep_is_held(&mvmvif
->mvm
->mutex
) ||
895 lockdep_is_held(&mvmvif
->mvm
->hw
->wiphy
->mtx
));
897 if (!ieee80211_vif_is_mld(vif
))
900 /* In AP mode, there is no primary link */
901 if (vif
->type
== NL80211_IFTYPE_AP
)
902 return __ffs(vif
->active_links
);
904 if (mvmvif
->esr_active
&&
905 !WARN_ON(!(BIT(mvmvif
->primary_link
) & vif
->active_links
)))
906 return mvmvif
->primary_link
;
908 return __ffs(vif
->active_links
);
912 * For non-MLO/single link, this will return the deflink/single active link,
915 u8
iwl_mvm_get_other_link(struct ieee80211_vif
*vif
, u8 link_id
)
917 switch (hweight16(vif
->active_links
)) {
924 return __ffs(vif
->active_links
);
926 return __ffs(vif
->active_links
& ~BIT(link_id
));
930 /* Reasons that can cause esr prevention */
931 #define IWL_MVM_ESR_PREVENT_REASONS IWL_MVM_ESR_EXIT_MISSED_BEACON
932 #define IWL_MVM_PREVENT_ESR_TIMEOUT (HZ * 400)
933 #define IWL_MVM_ESR_PREVENT_SHORT (HZ * 300)
934 #define IWL_MVM_ESR_PREVENT_LONG (HZ * 600)
936 static bool iwl_mvm_check_esr_prevention(struct iwl_mvm
*mvm
,
937 struct iwl_mvm_vif
*mvmvif
,
938 enum iwl_mvm_esr_state reason
)
940 bool timeout_expired
= time_after(jiffies
,
941 mvmvif
->last_esr_exit
.ts
+
942 IWL_MVM_PREVENT_ESR_TIMEOUT
);
945 lockdep_assert_held(&mvm
->mutex
);
947 /* Only handle reasons that can cause prevention */
948 if (!(reason
& IWL_MVM_ESR_PREVENT_REASONS
))
952 * Reset the counter if more than 400 seconds have passed between one
953 * exit and the other, or if we exited due to a different reason.
954 * Will also reset the counter after the long prevention is done.
956 if (timeout_expired
|| mvmvif
->last_esr_exit
.reason
!= reason
) {
957 mvmvif
->exit_same_reason_count
= 1;
961 mvmvif
->exit_same_reason_count
++;
962 if (WARN_ON(mvmvif
->exit_same_reason_count
< 2 ||
963 mvmvif
->exit_same_reason_count
> 3))
966 mvmvif
->esr_disable_reason
|= IWL_MVM_ESR_BLOCKED_PREVENTION
;
969 * For the second exit, use a short prevention, and for the third one,
970 * use a long prevention.
972 delay
= mvmvif
->exit_same_reason_count
== 2 ?
973 IWL_MVM_ESR_PREVENT_SHORT
:
974 IWL_MVM_ESR_PREVENT_LONG
;
977 "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
978 delay
/ HZ
, mvmvif
->exit_same_reason_count
,
979 iwl_get_esr_state_string(reason
), reason
);
981 wiphy_delayed_work_queue(mvm
->hw
->wiphy
,
982 &mvmvif
->prevent_esr_done_wk
, delay
);
986 #define IWL_MVM_TRIGGER_LINK_SEL_TIME (IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC * HZ)
988 /* API to exit eSR mode */
989 void iwl_mvm_exit_esr(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
990 enum iwl_mvm_esr_state reason
,
993 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
994 u16 new_active_links
;
997 lockdep_assert_held(&mvm
->mutex
);
999 if (!IWL_MVM_AUTO_EML_ENABLE
)
1003 if (!mvmvif
->esr_active
)
1006 if (WARN_ON(!ieee80211_vif_is_mld(vif
) || !mvmvif
->authorized
))
1009 if (WARN_ON(!(vif
->active_links
& BIT(link_to_keep
))))
1010 link_to_keep
= __ffs(vif
->active_links
);
1012 new_active_links
= BIT(link_to_keep
);
1014 "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
1015 iwl_get_esr_state_string(reason
), reason
,
1016 vif
->active_links
, new_active_links
);
1018 ieee80211_set_active_links_async(vif
, new_active_links
);
1020 /* Prevent EMLSR if needed */
1021 prevented
= iwl_mvm_check_esr_prevention(mvm
, mvmvif
, reason
);
1023 /* Remember why and when we exited EMLSR */
1024 mvmvif
->last_esr_exit
.ts
= jiffies
;
1025 mvmvif
->last_esr_exit
.reason
= reason
;
1028 * If EMLSR is prevented now - don't try to get back to EMLSR.
1029 * If we exited due to a blocking event, we will try to get back to
1030 * EMLSR when the corresponding unblocking event will happen.
1032 if (prevented
|| reason
& IWL_MVM_BLOCK_ESR_REASONS
)
1035 /* If EMLSR is not blocked - try enabling it again in 30 seconds */
1036 wiphy_delayed_work_queue(mvm
->hw
->wiphy
,
1037 &mvmvif
->mlo_int_scan_wk
,
1038 round_jiffies_relative(IWL_MVM_TRIGGER_LINK_SEL_TIME
));
1041 void iwl_mvm_block_esr(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
1042 enum iwl_mvm_esr_state reason
,
1045 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1047 lockdep_assert_held(&mvm
->mutex
);
1049 if (!IWL_MVM_AUTO_EML_ENABLE
)
1052 /* This should be called only with disable reasons */
1053 if (WARN_ON(!(reason
& IWL_MVM_BLOCK_ESR_REASONS
)))
1056 if (mvmvif
->esr_disable_reason
& reason
)
1060 "Blocking EMLSR mode. reason = %s (0x%x)\n",
1061 iwl_get_esr_state_string(reason
), reason
);
1063 mvmvif
->esr_disable_reason
|= reason
;
1065 iwl_mvm_print_esr_state(mvm
, mvmvif
->esr_disable_reason
);
1067 iwl_mvm_exit_esr(mvm
, vif
, reason
, link_to_keep
);
1070 int iwl_mvm_block_esr_sync(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
1071 enum iwl_mvm_esr_state reason
)
1073 int primary_link
= iwl_mvm_get_primary_link(vif
);
1076 if (!IWL_MVM_AUTO_EML_ENABLE
|| !ieee80211_vif_is_mld(vif
))
1079 /* This should be called only with blocking reasons */
1080 if (WARN_ON(!(reason
& IWL_MVM_BLOCK_ESR_REASONS
)))
1083 /* leave ESR immediately, not only async with iwl_mvm_block_esr() */
1084 ret
= ieee80211_set_active_links(vif
, BIT(primary_link
));
1088 mutex_lock(&mvm
->mutex
);
1089 /* only additionally block for consistency and to avoid concurrency */
1090 iwl_mvm_block_esr(mvm
, vif
, reason
, primary_link
);
1091 mutex_unlock(&mvm
->mutex
);
1096 static void iwl_mvm_esr_unblocked(struct iwl_mvm
*mvm
,
1097 struct ieee80211_vif
*vif
)
1099 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1100 bool need_new_sel
= time_after(jiffies
, mvmvif
->last_esr_exit
.ts
+
1101 IWL_MVM_TRIGGER_LINK_SEL_TIME
);
1103 lockdep_assert_held(&mvm
->mutex
);
1105 if (!ieee80211_vif_is_mld(vif
) || !mvmvif
->authorized
||
1109 IWL_DEBUG_INFO(mvm
, "EMLSR is unblocked\n");
1111 /* If we exited due to an EXIT reason, and the exit was in less than
1112 * 30 seconds, then a MLO scan was scheduled already.
1114 if (!need_new_sel
&&
1115 !(mvmvif
->last_esr_exit
.reason
& IWL_MVM_BLOCK_ESR_REASONS
)) {
1116 IWL_DEBUG_INFO(mvm
, "Wait for MLO scan\n");
1121 * If EMLSR was blocked for more than 30 seconds, or the last link
1122 * selection decided to not enter EMLSR, trigger a new scan.
1124 if (need_new_sel
|| hweight16(mvmvif
->link_selection_res
) < 2) {
1125 IWL_DEBUG_INFO(mvm
, "Trigger MLO scan\n");
1126 wiphy_delayed_work_queue(mvm
->hw
->wiphy
,
1127 &mvmvif
->mlo_int_scan_wk
, 0);
1129 * If EMLSR was blocked for less than 30 seconds, and the last link
1130 * selection decided to use EMLSR, activate EMLSR using the previous
1131 * link selection result.
1135 "Use the latest link selection result: 0x%x\n",
1136 mvmvif
->link_selection_res
);
1137 ieee80211_set_active_links_async(vif
,
1138 mvmvif
->link_selection_res
);
1142 void iwl_mvm_unblock_esr(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
1143 enum iwl_mvm_esr_state reason
)
1145 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1147 lockdep_assert_held(&mvm
->mutex
);
1149 if (!IWL_MVM_AUTO_EML_ENABLE
)
1152 /* This should be called only with disable reasons */
1153 if (WARN_ON(!(reason
& IWL_MVM_BLOCK_ESR_REASONS
)))
1157 if (!(mvmvif
->esr_disable_reason
& reason
))
1160 mvmvif
->esr_disable_reason
&= ~reason
;
1163 "Unblocking EMLSR mode. reason = %s (0x%x)\n",
1164 iwl_get_esr_state_string(reason
), reason
);
1165 iwl_mvm_print_esr_state(mvm
, mvmvif
->esr_disable_reason
);
1167 if (!mvmvif
->esr_disable_reason
)
1168 iwl_mvm_esr_unblocked(mvm
, vif
);
1171 void iwl_mvm_init_link(struct iwl_mvm_vif_link_info
*link
)
1173 link
->bcast_sta
.sta_id
= IWL_INVALID_STA
;
1174 link
->mcast_sta
.sta_id
= IWL_INVALID_STA
;
1175 link
->ap_sta_id
= IWL_INVALID_STA
;
1177 for (int r
= 0; r
< NUM_IWL_MVM_SMPS_REQ
; r
++)
1178 link
->smps_requests
[r
] =
1179 IEEE80211_SMPS_AUTOMATIC
;