19af1bba4SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 29af1bba4SBjoern A. Zeeb /* 3*a4128aadSBjoern A. Zeeb * Copyright (C) 2022 - 2024 Intel Corporation 49af1bba4SBjoern A. Zeeb */ 59af1bba4SBjoern A. Zeeb #include "mvm.h" 69af1bba4SBjoern A. Zeeb #include "time-event.h" 79af1bba4SBjoern A. Zeeb 8*a4128aadSBjoern A. Zeeb #define HANDLE_ESR_REASONS(HOW) \ 9*a4128aadSBjoern A. Zeeb HOW(BLOCKED_PREVENTION) \ 10*a4128aadSBjoern A. Zeeb HOW(BLOCKED_WOWLAN) \ 11*a4128aadSBjoern A. Zeeb HOW(BLOCKED_TPT) \ 12*a4128aadSBjoern A. Zeeb HOW(BLOCKED_FW) \ 13*a4128aadSBjoern A. Zeeb HOW(BLOCKED_NON_BSS) \ 14*a4128aadSBjoern A. Zeeb HOW(BLOCKED_ROC) \ 15*a4128aadSBjoern A. Zeeb HOW(EXIT_MISSED_BEACON) \ 16*a4128aadSBjoern A. Zeeb HOW(EXIT_LOW_RSSI) \ 17*a4128aadSBjoern A. Zeeb HOW(EXIT_COEX) \ 18*a4128aadSBjoern A. Zeeb HOW(EXIT_BANDWIDTH) \ 19*a4128aadSBjoern A. Zeeb HOW(EXIT_CSA) \ 20*a4128aadSBjoern A. Zeeb HOW(EXIT_LINK_USAGE) 21*a4128aadSBjoern A. Zeeb 22*a4128aadSBjoern A. Zeeb static const char *const iwl_mvm_esr_states_names[] = { 23*a4128aadSBjoern A. Zeeb #define NAME_ENTRY(x) [ilog2(IWL_MVM_ESR_##x)] = #x, 24*a4128aadSBjoern A. Zeeb HANDLE_ESR_REASONS(NAME_ENTRY) 25*a4128aadSBjoern A. Zeeb }; 26*a4128aadSBjoern A. Zeeb 27*a4128aadSBjoern A. Zeeb const char *iwl_get_esr_state_string(enum iwl_mvm_esr_state state) 28*a4128aadSBjoern A. Zeeb { 29*a4128aadSBjoern A. Zeeb int offs = ilog2(state); 30*a4128aadSBjoern A. Zeeb 31*a4128aadSBjoern A. Zeeb if (offs >= ARRAY_SIZE(iwl_mvm_esr_states_names) || 32*a4128aadSBjoern A. Zeeb !iwl_mvm_esr_states_names[offs]) 33*a4128aadSBjoern A. Zeeb return "UNKNOWN"; 34*a4128aadSBjoern A. Zeeb 35*a4128aadSBjoern A. Zeeb return iwl_mvm_esr_states_names[offs]; 36*a4128aadSBjoern A. Zeeb } 37*a4128aadSBjoern A. Zeeb 38*a4128aadSBjoern A. Zeeb static void iwl_mvm_print_esr_state(struct iwl_mvm *mvm, u32 mask) 39*a4128aadSBjoern A. Zeeb { 40*a4128aadSBjoern A. Zeeb #define NAME_FMT(x) "%s" 41*a4128aadSBjoern A. Zeeb #define NAME_PR(x) (mask & IWL_MVM_ESR_##x) ? "[" #x "]" : "", 42*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 43*a4128aadSBjoern A. Zeeb "EMLSR state = " HANDLE_ESR_REASONS(NAME_FMT) 44*a4128aadSBjoern A. Zeeb " (0x%x)\n", 45*a4128aadSBjoern A. Zeeb HANDLE_ESR_REASONS(NAME_PR) 46*a4128aadSBjoern A. Zeeb mask); 47*a4128aadSBjoern A. Zeeb #undef NAME_FMT 48*a4128aadSBjoern A. Zeeb #undef NAME_PR 49*a4128aadSBjoern A. Zeeb } 50*a4128aadSBjoern A. Zeeb 519af1bba4SBjoern A. Zeeb static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm, 529af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvm_vif) 539af1bba4SBjoern A. Zeeb { 54*a4128aadSBjoern A. Zeeb u32 i; 559af1bba4SBjoern A. Zeeb 569af1bba4SBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 579af1bba4SBjoern A. Zeeb 58*a4128aadSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(mvm->link_id_to_link_conf); i++) 59*a4128aadSBjoern A. Zeeb if (!rcu_access_pointer(mvm->link_id_to_link_conf[i])) 60*a4128aadSBjoern A. Zeeb return i; 619af1bba4SBjoern A. Zeeb 629af1bba4SBjoern A. Zeeb return IWL_MVM_FW_LINK_ID_INVALID; 639af1bba4SBjoern A. Zeeb } 649af1bba4SBjoern A. Zeeb 659af1bba4SBjoern A. Zeeb static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm, 669af1bba4SBjoern A. Zeeb struct iwl_link_config_cmd *cmd, 679af1bba4SBjoern A. Zeeb enum iwl_ctxt_action action) 689af1bba4SBjoern A. Zeeb { 699af1bba4SBjoern A. Zeeb int ret; 709af1bba4SBjoern A. Zeeb 719af1bba4SBjoern A. Zeeb cmd->action = cpu_to_le32(action); 729af1bba4SBjoern A. Zeeb ret = iwl_mvm_send_cmd_pdu(mvm, 739af1bba4SBjoern A. Zeeb WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0, 749af1bba4SBjoern A. Zeeb sizeof(*cmd), cmd); 759af1bba4SBjoern A. Zeeb if (ret) 769af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n", 779af1bba4SBjoern A. Zeeb action, ret); 789af1bba4SBjoern A. Zeeb return ret; 799af1bba4SBjoern A. Zeeb } 809af1bba4SBjoern A. Zeeb 81*a4128aadSBjoern A. Zeeb int iwl_mvm_set_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 82*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 83*a4128aadSBjoern A. Zeeb { 84*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 85*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = 86*a4128aadSBjoern A. Zeeb mvmvif->link[link_conf->link_id]; 87*a4128aadSBjoern A. Zeeb 88*a4128aadSBjoern A. Zeeb if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) { 89*a4128aadSBjoern A. Zeeb link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm, 90*a4128aadSBjoern A. Zeeb mvmvif); 91*a4128aadSBjoern A. Zeeb if (link_info->fw_link_id >= 92*a4128aadSBjoern A. Zeeb ARRAY_SIZE(mvm->link_id_to_link_conf)) 93*a4128aadSBjoern A. Zeeb return -EINVAL; 94*a4128aadSBjoern A. Zeeb 95*a4128aadSBjoern A. Zeeb rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id], 96*a4128aadSBjoern A. Zeeb link_conf); 97*a4128aadSBjoern A. Zeeb } 98*a4128aadSBjoern A. Zeeb 99*a4128aadSBjoern A. Zeeb return 0; 100*a4128aadSBjoern A. Zeeb } 101*a4128aadSBjoern A. Zeeb 1029af1bba4SBjoern A. Zeeb int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 1039af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 1049af1bba4SBjoern A. Zeeb { 1059af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1069af1bba4SBjoern A. Zeeb unsigned int link_id = link_conf->link_id; 1079af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; 1089af1bba4SBjoern A. Zeeb struct iwl_link_config_cmd cmd = {}; 109*a4128aadSBjoern A. Zeeb unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD); 110*a4128aadSBjoern A. Zeeb u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1); 111*a4128aadSBjoern A. Zeeb int ret; 1129af1bba4SBjoern A. Zeeb 1139af1bba4SBjoern A. Zeeb if (WARN_ON_ONCE(!link_info)) 1149af1bba4SBjoern A. Zeeb return -EINVAL; 1159af1bba4SBjoern A. Zeeb 116*a4128aadSBjoern A. Zeeb ret = iwl_mvm_set_link_mapping(mvm, vif, link_conf); 117*a4128aadSBjoern A. Zeeb if (ret) 118*a4128aadSBjoern A. Zeeb return ret; 1199af1bba4SBjoern A. Zeeb 1209af1bba4SBjoern A. Zeeb /* Update SF - Disable if needed. if this fails, SF might still be on 1219af1bba4SBjoern A. Zeeb * while many macs are bound, which is forbidden - so fail the binding. 1229af1bba4SBjoern A. Zeeb */ 1239af1bba4SBjoern A. Zeeb if (iwl_mvm_sf_update(mvm, vif, false)) 1249af1bba4SBjoern A. Zeeb return -EINVAL; 1259af1bba4SBjoern A. Zeeb 1269af1bba4SBjoern A. Zeeb cmd.link_id = cpu_to_le32(link_info->fw_link_id); 1279af1bba4SBjoern A. Zeeb cmd.mac_id = cpu_to_le32(mvmvif->id); 1289af1bba4SBjoern A. Zeeb cmd.spec_link_id = link_conf->link_id; 129*a4128aadSBjoern A. Zeeb WARN_ON_ONCE(link_info->phy_ctxt); 1309af1bba4SBjoern A. Zeeb cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); 1319af1bba4SBjoern A. Zeeb 1329af1bba4SBjoern A. Zeeb memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN); 1339af1bba4SBjoern A. Zeeb 1349af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) 1359af1bba4SBjoern A. Zeeb memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN); 1369af1bba4SBjoern A. Zeeb 137*a4128aadSBjoern A. Zeeb if (cmd_ver < 2) 1389af1bba4SBjoern A. Zeeb cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); 1399af1bba4SBjoern A. Zeeb 1409af1bba4SBjoern A. Zeeb return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD); 1419af1bba4SBjoern A. Zeeb } 1429af1bba4SBjoern A. Zeeb 143*a4128aadSBjoern A. Zeeb struct iwl_mvm_esr_iter_data { 144*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif; 145*a4128aadSBjoern A. Zeeb unsigned int link_id; 146*a4128aadSBjoern A. Zeeb bool lift_block; 147*a4128aadSBjoern A. Zeeb }; 148*a4128aadSBjoern A. Zeeb 149*a4128aadSBjoern A. Zeeb static void iwl_mvm_esr_vif_iterator(void *_data, u8 *mac, 150*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif) 151*a4128aadSBjoern A. Zeeb { 152*a4128aadSBjoern A. Zeeb struct iwl_mvm_esr_iter_data *data = _data; 153*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 154*a4128aadSBjoern A. Zeeb int link_id; 155*a4128aadSBjoern A. Zeeb 156*a4128aadSBjoern A. Zeeb if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) 157*a4128aadSBjoern A. Zeeb return; 158*a4128aadSBjoern A. Zeeb 159*a4128aadSBjoern A. Zeeb for_each_mvm_vif_valid_link(mvmvif, link_id) { 160*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = 161*a4128aadSBjoern A. Zeeb mvmvif->link[link_id]; 162*a4128aadSBjoern A. Zeeb if (vif == data->vif && link_id == data->link_id) 163*a4128aadSBjoern A. Zeeb continue; 164*a4128aadSBjoern A. Zeeb if (link_info->active) 165*a4128aadSBjoern A. Zeeb data->lift_block = false; 166*a4128aadSBjoern A. Zeeb } 167*a4128aadSBjoern A. Zeeb } 168*a4128aadSBjoern A. Zeeb 169*a4128aadSBjoern A. Zeeb int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 170*a4128aadSBjoern A. Zeeb unsigned int link_id, bool active) 171*a4128aadSBjoern A. Zeeb { 172*a4128aadSBjoern A. Zeeb /* An active link of a non-station vif blocks EMLSR. Upon activation 173*a4128aadSBjoern A. Zeeb * block EMLSR on the bss vif. Upon deactivation, check if this link 174*a4128aadSBjoern A. Zeeb * was the last non-station link active, and if so unblock the bss vif 175*a4128aadSBjoern A. Zeeb */ 176*a4128aadSBjoern A. Zeeb struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm); 177*a4128aadSBjoern A. Zeeb struct iwl_mvm_esr_iter_data data = { 178*a4128aadSBjoern A. Zeeb .vif = vif, 179*a4128aadSBjoern A. Zeeb .link_id = link_id, 180*a4128aadSBjoern A. Zeeb .lift_block = true, 181*a4128aadSBjoern A. Zeeb }; 182*a4128aadSBjoern A. Zeeb 183*a4128aadSBjoern A. Zeeb if (IS_ERR_OR_NULL(bss_vif)) 184*a4128aadSBjoern A. Zeeb return 0; 185*a4128aadSBjoern A. Zeeb 186*a4128aadSBjoern A. Zeeb if (active) 187*a4128aadSBjoern A. Zeeb return iwl_mvm_block_esr_sync(mvm, bss_vif, 188*a4128aadSBjoern A. Zeeb IWL_MVM_ESR_BLOCKED_NON_BSS); 189*a4128aadSBjoern A. Zeeb 190*a4128aadSBjoern A. Zeeb ieee80211_iterate_active_interfaces(mvm->hw, 191*a4128aadSBjoern A. Zeeb IEEE80211_IFACE_ITER_NORMAL, 192*a4128aadSBjoern A. Zeeb iwl_mvm_esr_vif_iterator, &data); 193*a4128aadSBjoern A. Zeeb if (data.lift_block) { 194*a4128aadSBjoern A. Zeeb mutex_lock(&mvm->mutex); 195*a4128aadSBjoern A. Zeeb iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_NON_BSS); 196*a4128aadSBjoern A. Zeeb mutex_unlock(&mvm->mutex); 197*a4128aadSBjoern A. Zeeb } 198*a4128aadSBjoern A. Zeeb 199*a4128aadSBjoern A. Zeeb return 0; 200*a4128aadSBjoern A. Zeeb } 201*a4128aadSBjoern A. Zeeb 2029af1bba4SBjoern A. Zeeb int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 2039af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 2049af1bba4SBjoern A. Zeeb u32 changes, bool active) 2059af1bba4SBjoern A. Zeeb { 2069af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 2079af1bba4SBjoern A. Zeeb unsigned int link_id = link_conf->link_id; 2089af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; 2099af1bba4SBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phyctxt; 2109af1bba4SBjoern A. Zeeb struct iwl_link_config_cmd cmd = {}; 2119af1bba4SBjoern A. Zeeb u32 ht_flag, flags = 0, flags_mask = 0; 2129af1bba4SBjoern A. Zeeb int ret; 213*a4128aadSBjoern A. Zeeb unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD); 214*a4128aadSBjoern A. Zeeb u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1); 2159af1bba4SBjoern A. Zeeb 2169af1bba4SBjoern A. Zeeb if (WARN_ON_ONCE(!link_info || 2179af1bba4SBjoern A. Zeeb link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) 2189af1bba4SBjoern A. Zeeb return -EINVAL; 2199af1bba4SBjoern A. Zeeb 2209af1bba4SBjoern A. Zeeb if (changes & LINK_CONTEXT_MODIFY_ACTIVE) { 2219af1bba4SBjoern A. Zeeb /* When activating a link, phy context should be valid; 2229af1bba4SBjoern A. Zeeb * when deactivating a link, it also should be valid since 2239af1bba4SBjoern A. Zeeb * the link was active before. So, do nothing in this case. 2249af1bba4SBjoern A. Zeeb * Since a link is added first with FW_CTXT_INVALID, then we 2259af1bba4SBjoern A. Zeeb * can get here in case it's removed before it was activated. 2269af1bba4SBjoern A. Zeeb */ 2279af1bba4SBjoern A. Zeeb if (!link_info->phy_ctxt) 2289af1bba4SBjoern A. Zeeb return 0; 2299af1bba4SBjoern A. Zeeb 2309af1bba4SBjoern A. Zeeb /* Catch early if driver tries to activate or deactivate a link 2319af1bba4SBjoern A. Zeeb * twice. 2329af1bba4SBjoern A. Zeeb */ 2339af1bba4SBjoern A. Zeeb WARN_ON_ONCE(active == link_info->active); 2349af1bba4SBjoern A. Zeeb 2359af1bba4SBjoern A. Zeeb /* When deactivating a link session protection should 2369af1bba4SBjoern A. Zeeb * be stopped 2379af1bba4SBjoern A. Zeeb */ 2389af1bba4SBjoern A. Zeeb if (!active && vif->type == NL80211_IFTYPE_STATION) 2399af1bba4SBjoern A. Zeeb iwl_mvm_stop_session_protection(mvm, vif); 2409af1bba4SBjoern A. Zeeb } 2419af1bba4SBjoern A. Zeeb 2429af1bba4SBjoern A. Zeeb cmd.link_id = cpu_to_le32(link_info->fw_link_id); 2439af1bba4SBjoern A. Zeeb 2449af1bba4SBjoern A. Zeeb /* The phy_id, link address and listen_lmac can be modified only until 2459af1bba4SBjoern A. Zeeb * the link becomes active, otherwise they will be ignored. 2469af1bba4SBjoern A. Zeeb */ 2479af1bba4SBjoern A. Zeeb phyctxt = link_info->phy_ctxt; 2489af1bba4SBjoern A. Zeeb if (phyctxt) 2499af1bba4SBjoern A. Zeeb cmd.phy_id = cpu_to_le32(phyctxt->id); 2509af1bba4SBjoern A. Zeeb else 2519af1bba4SBjoern A. Zeeb cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); 2529af1bba4SBjoern A. Zeeb cmd.mac_id = cpu_to_le32(mvmvif->id); 2539af1bba4SBjoern A. Zeeb 2549af1bba4SBjoern A. Zeeb memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN); 2559af1bba4SBjoern A. Zeeb 2569af1bba4SBjoern A. Zeeb cmd.active = cpu_to_le32(active); 2579af1bba4SBjoern A. Zeeb 2589af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) 2599af1bba4SBjoern A. Zeeb memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN); 2609af1bba4SBjoern A. Zeeb 2619af1bba4SBjoern A. Zeeb iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf, 2629af1bba4SBjoern A. Zeeb &cmd.cck_rates, &cmd.ofdm_rates); 2639af1bba4SBjoern A. Zeeb 2649af1bba4SBjoern A. Zeeb cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble); 2659af1bba4SBjoern A. Zeeb cmd.short_slot = cpu_to_le32(link_conf->use_short_slot); 2669af1bba4SBjoern A. Zeeb 2679af1bba4SBjoern A. Zeeb /* The fw does not distinguish between ht and fat */ 2689af1bba4SBjoern A. Zeeb ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT; 2699af1bba4SBjoern A. Zeeb iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf, 2709af1bba4SBjoern A. Zeeb &cmd.protection_flags, 2719af1bba4SBjoern A. Zeeb ht_flag, LINK_PROT_FLG_TGG_PROTECT); 2729af1bba4SBjoern A. Zeeb 2739af1bba4SBjoern A. Zeeb iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac, 2749af1bba4SBjoern A. Zeeb &cmd.qos_flags); 2759af1bba4SBjoern A. Zeeb 2769af1bba4SBjoern A. Zeeb 2779af1bba4SBjoern A. Zeeb cmd.bi = cpu_to_le32(link_conf->beacon_int); 2789af1bba4SBjoern A. Zeeb cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int * 2799af1bba4SBjoern A. Zeeb link_conf->dtim_period); 2809af1bba4SBjoern A. Zeeb 2819af1bba4SBjoern A. Zeeb if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax || 2829af1bba4SBjoern A. Zeeb (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) { 2839af1bba4SBjoern A. Zeeb changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS; 2849af1bba4SBjoern A. Zeeb goto send_cmd; 2859af1bba4SBjoern A. Zeeb } 2869af1bba4SBjoern A. Zeeb 2879af1bba4SBjoern A. Zeeb cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext; 2889af1bba4SBjoern A. Zeeb 2899af1bba4SBjoern A. Zeeb if (link_conf->uora_exists) { 2909af1bba4SBjoern A. Zeeb cmd.rand_alloc_ecwmin = 2919af1bba4SBjoern A. Zeeb link_conf->uora_ocw_range & 0x7; 2929af1bba4SBjoern A. Zeeb cmd.rand_alloc_ecwmax = 2939af1bba4SBjoern A. Zeeb (link_conf->uora_ocw_range >> 3) & 0x7; 2949af1bba4SBjoern A. Zeeb } 2959af1bba4SBjoern A. Zeeb 2969af1bba4SBjoern A. Zeeb /* TODO how to set ndp_fdbk_buff_th_exp? */ 2979af1bba4SBjoern A. Zeeb 2989af1bba4SBjoern A. Zeeb if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id], 2999af1bba4SBjoern A. Zeeb &cmd.trig_based_txf[0])) { 3009af1bba4SBjoern A. Zeeb flags |= LINK_FLG_MU_EDCA_CW; 3019af1bba4SBjoern A. Zeeb flags_mask |= LINK_FLG_MU_EDCA_CW; 3029af1bba4SBjoern A. Zeeb } 3039af1bba4SBjoern A. Zeeb 304*a4128aadSBjoern A. Zeeb if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) { 305*a4128aadSBjoern A. Zeeb struct ieee80211_chanctx_conf *ctx; 306*a4128aadSBjoern A. Zeeb struct cfg80211_chan_def *def = NULL; 307*a4128aadSBjoern A. Zeeb 308*a4128aadSBjoern A. Zeeb rcu_read_lock(); 309*a4128aadSBjoern A. Zeeb ctx = rcu_dereference(link_conf->chanctx_conf); 310*a4128aadSBjoern A. Zeeb if (ctx) 311*a4128aadSBjoern A. Zeeb def = iwl_mvm_chanctx_def(mvm, ctx); 312*a4128aadSBjoern A. Zeeb 313*a4128aadSBjoern A. Zeeb if (iwlwifi_mod_params.disable_11be || 314*a4128aadSBjoern A. Zeeb !link_conf->eht_support || !def || 315*a4128aadSBjoern A. Zeeb iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) >= 6) 3169af1bba4SBjoern A. Zeeb changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; 317*a4128aadSBjoern A. Zeeb else 318*a4128aadSBjoern A. Zeeb cmd.puncture_mask = cpu_to_le16(def->punctured); 319*a4128aadSBjoern A. Zeeb rcu_read_unlock(); 320*a4128aadSBjoern A. Zeeb } 3219af1bba4SBjoern A. Zeeb 3229af1bba4SBjoern A. Zeeb cmd.bss_color = link_conf->he_bss_color.color; 3239af1bba4SBjoern A. Zeeb 3249af1bba4SBjoern A. Zeeb if (!link_conf->he_bss_color.enabled) { 3259af1bba4SBjoern A. Zeeb flags |= LINK_FLG_BSS_COLOR_DIS; 3269af1bba4SBjoern A. Zeeb flags_mask |= LINK_FLG_BSS_COLOR_DIS; 3279af1bba4SBjoern A. Zeeb } 3289af1bba4SBjoern A. Zeeb 3299af1bba4SBjoern A. Zeeb cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th); 3309af1bba4SBjoern A. Zeeb 3319af1bba4SBjoern A. Zeeb /* Block 26-tone RU OFDMA transmissions */ 3329af1bba4SBjoern A. Zeeb if (link_info->he_ru_2mhz_block) { 3339af1bba4SBjoern A. Zeeb flags |= LINK_FLG_RU_2MHZ_BLOCK; 3349af1bba4SBjoern A. Zeeb flags_mask |= LINK_FLG_RU_2MHZ_BLOCK; 3359af1bba4SBjoern A. Zeeb } 3369af1bba4SBjoern A. Zeeb 3379af1bba4SBjoern A. Zeeb if (link_conf->nontransmitted) { 3389af1bba4SBjoern A. Zeeb ether_addr_copy(cmd.ref_bssid_addr, 3399af1bba4SBjoern A. Zeeb link_conf->transmitter_bssid); 3409af1bba4SBjoern A. Zeeb cmd.bssid_index = link_conf->bssid_index; 3419af1bba4SBjoern A. Zeeb } 3429af1bba4SBjoern A. Zeeb 3439af1bba4SBjoern A. Zeeb send_cmd: 3449af1bba4SBjoern A. Zeeb cmd.modify_mask = cpu_to_le32(changes); 3459af1bba4SBjoern A. Zeeb cmd.flags = cpu_to_le32(flags); 3469af1bba4SBjoern A. Zeeb cmd.flags_mask = cpu_to_le32(flags_mask); 3479af1bba4SBjoern A. Zeeb cmd.spec_link_id = link_conf->link_id; 348*a4128aadSBjoern A. Zeeb if (cmd_ver < 2) 3499af1bba4SBjoern A. Zeeb cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); 3509af1bba4SBjoern A. Zeeb 3519af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY); 3529af1bba4SBjoern A. Zeeb if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE)) 3539af1bba4SBjoern A. Zeeb link_info->active = active; 3549af1bba4SBjoern A. Zeeb 3559af1bba4SBjoern A. Zeeb return ret; 3569af1bba4SBjoern A. Zeeb } 3579af1bba4SBjoern A. Zeeb 358*a4128aadSBjoern A. Zeeb int iwl_mvm_unset_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 359*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 360*a4128aadSBjoern A. Zeeb { 361*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 362*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = 363*a4128aadSBjoern A. Zeeb mvmvif->link[link_conf->link_id]; 364*a4128aadSBjoern A. Zeeb 365*a4128aadSBjoern A. Zeeb /* mac80211 thought we have the link, but it was never configured */ 366*a4128aadSBjoern A. Zeeb if (WARN_ON(!link_info || 367*a4128aadSBjoern A. Zeeb link_info->fw_link_id >= 368*a4128aadSBjoern A. Zeeb ARRAY_SIZE(mvm->link_id_to_link_conf))) 369*a4128aadSBjoern A. Zeeb return -EINVAL; 370*a4128aadSBjoern A. Zeeb 371*a4128aadSBjoern A. Zeeb RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id], 372*a4128aadSBjoern A. Zeeb NULL); 373*a4128aadSBjoern A. Zeeb return 0; 374*a4128aadSBjoern A. Zeeb } 375*a4128aadSBjoern A. Zeeb 3769af1bba4SBjoern A. Zeeb int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 3779af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 3789af1bba4SBjoern A. Zeeb { 3799af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 3809af1bba4SBjoern A. Zeeb unsigned int link_id = link_conf->link_id; 3819af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; 3829af1bba4SBjoern A. Zeeb struct iwl_link_config_cmd cmd = {}; 3839af1bba4SBjoern A. Zeeb int ret; 3849af1bba4SBjoern A. Zeeb 385*a4128aadSBjoern A. Zeeb ret = iwl_mvm_unset_link_mapping(mvm, vif, link_conf); 386*a4128aadSBjoern A. Zeeb if (ret) 387*a4128aadSBjoern A. Zeeb return 0; 3889af1bba4SBjoern A. Zeeb 3899af1bba4SBjoern A. Zeeb cmd.link_id = cpu_to_le32(link_info->fw_link_id); 3909af1bba4SBjoern A. Zeeb link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; 3919af1bba4SBjoern A. Zeeb cmd.spec_link_id = link_conf->link_id; 392*a4128aadSBjoern A. Zeeb cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); 3939af1bba4SBjoern A. Zeeb 3949af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE); 3959af1bba4SBjoern A. Zeeb 3969af1bba4SBjoern A. Zeeb if (!ret) 3979af1bba4SBjoern A. Zeeb if (iwl_mvm_sf_update(mvm, vif, true)) 3989af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "Failed to update SF state\n"); 3999af1bba4SBjoern A. Zeeb 4009af1bba4SBjoern A. Zeeb return ret; 4019af1bba4SBjoern A. Zeeb } 4029af1bba4SBjoern A. Zeeb 4039af1bba4SBjoern A. Zeeb /* link should be deactivated before removal, so in most cases we need to 4049af1bba4SBjoern A. Zeeb * perform these two operations together 4059af1bba4SBjoern A. Zeeb */ 4069af1bba4SBjoern A. Zeeb int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 4079af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 4089af1bba4SBjoern A. Zeeb { 4099af1bba4SBjoern A. Zeeb int ret; 4109af1bba4SBjoern A. Zeeb 4119af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, link_conf, 4129af1bba4SBjoern A. Zeeb LINK_CONTEXT_MODIFY_ACTIVE, false); 4139af1bba4SBjoern A. Zeeb if (ret) 4149af1bba4SBjoern A. Zeeb return ret; 4159af1bba4SBjoern A. Zeeb 4169af1bba4SBjoern A. Zeeb ret = iwl_mvm_remove_link(mvm, vif, link_conf); 4179af1bba4SBjoern A. Zeeb if (ret) 4189af1bba4SBjoern A. Zeeb return ret; 4199af1bba4SBjoern A. Zeeb 4209af1bba4SBjoern A. Zeeb return ret; 4219af1bba4SBjoern A. Zeeb } 422*a4128aadSBjoern A. Zeeb 423*a4128aadSBjoern A. Zeeb struct iwl_mvm_rssi_to_grade { 424*a4128aadSBjoern A. Zeeb s8 rssi[2]; 425*a4128aadSBjoern A. Zeeb u16 grade; 426*a4128aadSBjoern A. Zeeb }; 427*a4128aadSBjoern A. Zeeb 428*a4128aadSBjoern A. Zeeb #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \ 429*a4128aadSBjoern A. Zeeb { \ 430*a4128aadSBjoern A. Zeeb .rssi = {_lb, _hb_uhb}, \ 431*a4128aadSBjoern A. Zeeb .grade = _grade \ 432*a4128aadSBjoern A. Zeeb } 433*a4128aadSBjoern A. Zeeb 434*a4128aadSBjoern A. Zeeb /* 435*a4128aadSBjoern A. Zeeb * This array must be sorted by increasing RSSI for proper functionality. 436*a4128aadSBjoern A. Zeeb * The grades are actually estimated throughput, represented as fixed-point 437*a4128aadSBjoern A. Zeeb * with a scale factor of 1/10. 438*a4128aadSBjoern A. Zeeb */ 439*a4128aadSBjoern A. Zeeb static const struct iwl_mvm_rssi_to_grade rssi_to_grade_map[] = { 440*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-85, -89, 177), 441*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-83, -86, 344), 442*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-82, -85, 516), 443*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-80, -83, 688), 444*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-77, -79, 1032), 445*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-73, -76, 1376), 446*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-70, -74, 1548), 447*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-69, -72, 1750), 448*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-65, -68, 2064), 449*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-61, -66, 2294), 450*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-58, -61, 2580), 451*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-55, -58, 2868), 452*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-46, -55, 3098), 453*a4128aadSBjoern A. Zeeb RSSI_TO_GRADE_LINE(-43, -54, 3442) 454*a4128aadSBjoern A. Zeeb }; 455*a4128aadSBjoern A. Zeeb 456*a4128aadSBjoern A. Zeeb #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade) 457*a4128aadSBjoern A. Zeeb 458*a4128aadSBjoern A. Zeeb #define DEFAULT_CHAN_LOAD_LB 30 459*a4128aadSBjoern A. Zeeb #define DEFAULT_CHAN_LOAD_HB 15 460*a4128aadSBjoern A. Zeeb #define DEFAULT_CHAN_LOAD_UHB 0 461*a4128aadSBjoern A. Zeeb 462*a4128aadSBjoern A. Zeeb /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */ 463*a4128aadSBjoern A. Zeeb #define SCALE_FACTOR 256 464*a4128aadSBjoern A. Zeeb 465*a4128aadSBjoern A. Zeeb /* Convert a percentage from [0,100] to [0,255] */ 466*a4128aadSBjoern A. Zeeb #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * SCALE_FACTOR / 100) 467*a4128aadSBjoern A. Zeeb 468*a4128aadSBjoern A. Zeeb static unsigned int 469*a4128aadSBjoern A. Zeeb iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf *link_conf) 470*a4128aadSBjoern A. Zeeb { 471*a4128aadSBjoern A. Zeeb enum nl80211_chan_width chan_width = 472*a4128aadSBjoern A. Zeeb link_conf->chanreq.oper.width; 473*a4128aadSBjoern A. Zeeb int mhz = nl80211_chan_width_to_mhz(chan_width); 474*a4128aadSBjoern A. Zeeb unsigned int n_subchannels, n_punctured, puncturing_penalty; 475*a4128aadSBjoern A. Zeeb 476*a4128aadSBjoern A. Zeeb if (WARN_ONCE(mhz < 20 || mhz > 320, 477*a4128aadSBjoern A. Zeeb "Invalid channel width : (%d)\n", mhz)) 478*a4128aadSBjoern A. Zeeb return SCALE_FACTOR; 479*a4128aadSBjoern A. Zeeb 480*a4128aadSBjoern A. Zeeb /* No puncturing, no penalty */ 481*a4128aadSBjoern A. Zeeb if (mhz < 80) 482*a4128aadSBjoern A. Zeeb return SCALE_FACTOR; 483*a4128aadSBjoern A. Zeeb 484*a4128aadSBjoern A. Zeeb /* total number of subchannels */ 485*a4128aadSBjoern A. Zeeb n_subchannels = mhz / 20; 486*a4128aadSBjoern A. Zeeb /* how many of these are punctured */ 487*a4128aadSBjoern A. Zeeb n_punctured = hweight16(link_conf->chanreq.oper.punctured); 488*a4128aadSBjoern A. Zeeb 489*a4128aadSBjoern A. Zeeb puncturing_penalty = n_punctured * SCALE_FACTOR / n_subchannels; 490*a4128aadSBjoern A. Zeeb return SCALE_FACTOR - puncturing_penalty; 491*a4128aadSBjoern A. Zeeb } 492*a4128aadSBjoern A. Zeeb 493*a4128aadSBjoern A. Zeeb static unsigned int 494*a4128aadSBjoern A. Zeeb iwl_mvm_get_chan_load(struct ieee80211_bss_conf *link_conf) 495*a4128aadSBjoern A. Zeeb { 496*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif = link_conf->vif; 497*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *mvm_link = 498*a4128aadSBjoern A. Zeeb iwl_mvm_vif_from_mac80211(link_conf->vif)->link[link_conf->link_id]; 499*a4128aadSBjoern A. Zeeb const struct element *bss_load_elem; 500*a4128aadSBjoern A. Zeeb const struct ieee80211_bss_load_elem *bss_load; 501*a4128aadSBjoern A. Zeeb enum nl80211_band band = link_conf->chanreq.oper.chan->band; 502*a4128aadSBjoern A. Zeeb const struct cfg80211_bss_ies *ies; 503*a4128aadSBjoern A. Zeeb unsigned int chan_load; 504*a4128aadSBjoern A. Zeeb u32 chan_load_by_us; 505*a4128aadSBjoern A. Zeeb 506*a4128aadSBjoern A. Zeeb rcu_read_lock(); 507*a4128aadSBjoern A. Zeeb if (ieee80211_vif_link_active(vif, link_conf->link_id)) 508*a4128aadSBjoern A. Zeeb ies = rcu_dereference(link_conf->bss->beacon_ies); 509*a4128aadSBjoern A. Zeeb else 510*a4128aadSBjoern A. Zeeb ies = rcu_dereference(link_conf->bss->ies); 511*a4128aadSBjoern A. Zeeb 512*a4128aadSBjoern A. Zeeb if (ies) 513*a4128aadSBjoern A. Zeeb bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD, 514*a4128aadSBjoern A. Zeeb ies->data, ies->len); 515*a4128aadSBjoern A. Zeeb else 516*a4128aadSBjoern A. Zeeb bss_load_elem = NULL; 517*a4128aadSBjoern A. Zeeb 518*a4128aadSBjoern A. Zeeb /* If there isn't BSS Load element, take the defaults */ 519*a4128aadSBjoern A. Zeeb if (!bss_load_elem || 520*a4128aadSBjoern A. Zeeb bss_load_elem->datalen != sizeof(*bss_load)) { 521*a4128aadSBjoern A. Zeeb rcu_read_unlock(); 522*a4128aadSBjoern A. Zeeb switch (band) { 523*a4128aadSBjoern A. Zeeb case NL80211_BAND_2GHZ: 524*a4128aadSBjoern A. Zeeb chan_load = DEFAULT_CHAN_LOAD_LB; 525*a4128aadSBjoern A. Zeeb break; 526*a4128aadSBjoern A. Zeeb case NL80211_BAND_5GHZ: 527*a4128aadSBjoern A. Zeeb chan_load = DEFAULT_CHAN_LOAD_HB; 528*a4128aadSBjoern A. Zeeb break; 529*a4128aadSBjoern A. Zeeb case NL80211_BAND_6GHZ: 530*a4128aadSBjoern A. Zeeb chan_load = DEFAULT_CHAN_LOAD_UHB; 531*a4128aadSBjoern A. Zeeb break; 532*a4128aadSBjoern A. Zeeb default: 533*a4128aadSBjoern A. Zeeb chan_load = 0; 534*a4128aadSBjoern A. Zeeb break; 535*a4128aadSBjoern A. Zeeb } 536*a4128aadSBjoern A. Zeeb /* The defaults are given in percentage */ 537*a4128aadSBjoern A. Zeeb return NORMALIZE_PERCENT_TO_255(chan_load); 538*a4128aadSBjoern A. Zeeb } 539*a4128aadSBjoern A. Zeeb 540*a4128aadSBjoern A. Zeeb bss_load = (const void *)bss_load_elem->data; 541*a4128aadSBjoern A. Zeeb /* Channel util is in range 0-255 */ 542*a4128aadSBjoern A. Zeeb chan_load = bss_load->channel_util; 543*a4128aadSBjoern A. Zeeb rcu_read_unlock(); 544*a4128aadSBjoern A. Zeeb 545*a4128aadSBjoern A. Zeeb if (!mvm_link || !mvm_link->active) 546*a4128aadSBjoern A. Zeeb return chan_load; 547*a4128aadSBjoern A. Zeeb 548*a4128aadSBjoern A. Zeeb if (WARN_ONCE(!mvm_link->phy_ctxt, 549*a4128aadSBjoern A. Zeeb "Active link (%u) without phy ctxt assigned!\n", 550*a4128aadSBjoern A. Zeeb link_conf->link_id)) 551*a4128aadSBjoern A. Zeeb return chan_load; 552*a4128aadSBjoern A. Zeeb 553*a4128aadSBjoern A. Zeeb /* channel load by us is given in percentage */ 554*a4128aadSBjoern A. Zeeb chan_load_by_us = 555*a4128aadSBjoern A. Zeeb NORMALIZE_PERCENT_TO_255(mvm_link->phy_ctxt->channel_load_by_us); 556*a4128aadSBjoern A. Zeeb 557*a4128aadSBjoern A. Zeeb /* Use only values that firmware sends that can possibly be valid */ 558*a4128aadSBjoern A. Zeeb if (chan_load_by_us <= chan_load) 559*a4128aadSBjoern A. Zeeb chan_load -= chan_load_by_us; 560*a4128aadSBjoern A. Zeeb 561*a4128aadSBjoern A. Zeeb return chan_load; 562*a4128aadSBjoern A. Zeeb } 563*a4128aadSBjoern A. Zeeb 564*a4128aadSBjoern A. Zeeb static unsigned int 565*a4128aadSBjoern A. Zeeb iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf) 566*a4128aadSBjoern A. Zeeb { 567*a4128aadSBjoern A. Zeeb return SCALE_FACTOR - iwl_mvm_get_chan_load(link_conf); 568*a4128aadSBjoern A. Zeeb } 569*a4128aadSBjoern A. Zeeb 570*a4128aadSBjoern A. Zeeb /* This function calculates the grade of a link. Returns 0 in error case */ 571*a4128aadSBjoern A. Zeeb VISIBLE_IF_IWLWIFI_KUNIT 572*a4128aadSBjoern A. Zeeb unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf) 573*a4128aadSBjoern A. Zeeb { 574*a4128aadSBjoern A. Zeeb enum nl80211_band band; 575*a4128aadSBjoern A. Zeeb int i, rssi_idx; 576*a4128aadSBjoern A. Zeeb s32 link_rssi; 577*a4128aadSBjoern A. Zeeb unsigned int grade = MAX_GRADE; 578*a4128aadSBjoern A. Zeeb 579*a4128aadSBjoern A. Zeeb if (WARN_ON_ONCE(!link_conf)) 580*a4128aadSBjoern A. Zeeb return 0; 581*a4128aadSBjoern A. Zeeb 582*a4128aadSBjoern A. Zeeb band = link_conf->chanreq.oper.chan->band; 583*a4128aadSBjoern A. Zeeb if (WARN_ONCE(band != NL80211_BAND_2GHZ && 584*a4128aadSBjoern A. Zeeb band != NL80211_BAND_5GHZ && 585*a4128aadSBjoern A. Zeeb band != NL80211_BAND_6GHZ, 586*a4128aadSBjoern A. Zeeb "Invalid band (%u)\n", band)) 587*a4128aadSBjoern A. Zeeb return 0; 588*a4128aadSBjoern A. Zeeb 589*a4128aadSBjoern A. Zeeb link_rssi = MBM_TO_DBM(link_conf->bss->signal); 590*a4128aadSBjoern A. Zeeb /* 591*a4128aadSBjoern A. Zeeb * For 6 GHz the RSSI of the beacons is lower than 592*a4128aadSBjoern A. Zeeb * the RSSI of the data. 593*a4128aadSBjoern A. Zeeb */ 594*a4128aadSBjoern A. Zeeb if (band == NL80211_BAND_6GHZ) 595*a4128aadSBjoern A. Zeeb link_rssi += 4; 596*a4128aadSBjoern A. Zeeb 597*a4128aadSBjoern A. Zeeb rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1; 598*a4128aadSBjoern A. Zeeb 599*a4128aadSBjoern A. Zeeb /* No valid RSSI - take the lowest grade */ 600*a4128aadSBjoern A. Zeeb if (!link_rssi) 601*a4128aadSBjoern A. Zeeb link_rssi = rssi_to_grade_map[0].rssi[rssi_idx]; 602*a4128aadSBjoern A. Zeeb 603*a4128aadSBjoern A. Zeeb /* Get grade based on RSSI */ 604*a4128aadSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) { 605*a4128aadSBjoern A. Zeeb const struct iwl_mvm_rssi_to_grade *line = 606*a4128aadSBjoern A. Zeeb &rssi_to_grade_map[i]; 607*a4128aadSBjoern A. Zeeb 608*a4128aadSBjoern A. Zeeb if (link_rssi > line->rssi[rssi_idx]) 609*a4128aadSBjoern A. Zeeb continue; 610*a4128aadSBjoern A. Zeeb grade = line->grade; 611*a4128aadSBjoern A. Zeeb break; 612*a4128aadSBjoern A. Zeeb } 613*a4128aadSBjoern A. Zeeb 614*a4128aadSBjoern A. Zeeb /* apply the channel load and puncturing factors */ 615*a4128aadSBjoern A. Zeeb grade = grade * iwl_mvm_get_chan_load_factor(link_conf) / SCALE_FACTOR; 616*a4128aadSBjoern A. Zeeb grade = grade * iwl_mvm_get_puncturing_factor(link_conf) / SCALE_FACTOR; 617*a4128aadSBjoern A. Zeeb return grade; 618*a4128aadSBjoern A. Zeeb } 619*a4128aadSBjoern A. Zeeb EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade); 620*a4128aadSBjoern A. Zeeb 621*a4128aadSBjoern A. Zeeb static 622*a4128aadSBjoern A. Zeeb u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif, 623*a4128aadSBjoern A. Zeeb struct iwl_mvm_link_sel_data *data, 624*a4128aadSBjoern A. Zeeb unsigned long usable_links, 625*a4128aadSBjoern A. Zeeb u8 *best_link_idx) 626*a4128aadSBjoern A. Zeeb { 627*a4128aadSBjoern A. Zeeb u8 n_data = 0; 628*a4128aadSBjoern A. Zeeb u16 max_grade = 0; 629*a4128aadSBjoern A. Zeeb unsigned long link_id; 630*a4128aadSBjoern A. Zeeb 631*a4128aadSBjoern A. Zeeb /* TODO: don't select links that weren't discovered in the last scan */ 632*a4128aadSBjoern A. Zeeb for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { 633*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *link_conf = 634*a4128aadSBjoern A. Zeeb link_conf_dereference_protected(vif, link_id); 635*a4128aadSBjoern A. Zeeb 636*a4128aadSBjoern A. Zeeb if (WARN_ON_ONCE(!link_conf)) 637*a4128aadSBjoern A. Zeeb continue; 638*a4128aadSBjoern A. Zeeb 639*a4128aadSBjoern A. Zeeb data[n_data].link_id = link_id; 640*a4128aadSBjoern A. Zeeb data[n_data].chandef = &link_conf->chanreq.oper; 641*a4128aadSBjoern A. Zeeb data[n_data].signal = link_conf->bss->signal / 100; 642*a4128aadSBjoern A. Zeeb data[n_data].grade = iwl_mvm_get_link_grade(link_conf); 643*a4128aadSBjoern A. Zeeb 644*a4128aadSBjoern A. Zeeb if (data[n_data].grade > max_grade) { 645*a4128aadSBjoern A. Zeeb max_grade = data[n_data].grade; 646*a4128aadSBjoern A. Zeeb *best_link_idx = n_data; 647*a4128aadSBjoern A. Zeeb } 648*a4128aadSBjoern A. Zeeb n_data++; 649*a4128aadSBjoern A. Zeeb } 650*a4128aadSBjoern A. Zeeb 651*a4128aadSBjoern A. Zeeb return n_data; 652*a4128aadSBjoern A. Zeeb } 653*a4128aadSBjoern A. Zeeb 654*a4128aadSBjoern A. Zeeb struct iwl_mvm_bw_to_rssi_threshs { 655*a4128aadSBjoern A. Zeeb s8 low; 656*a4128aadSBjoern A. Zeeb s8 high; 657*a4128aadSBjoern A. Zeeb }; 658*a4128aadSBjoern A. Zeeb 659*a4128aadSBjoern A. Zeeb #define BW_TO_RSSI_THRESHOLDS(_bw) \ 660*a4128aadSBjoern A. Zeeb [IWL_PHY_CHANNEL_MODE ## _bw] = { \ 661*a4128aadSBjoern A. Zeeb .low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ, \ 662*a4128aadSBjoern A. Zeeb .high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ \ 663*a4128aadSBjoern A. Zeeb } 664*a4128aadSBjoern A. Zeeb 665*a4128aadSBjoern A. Zeeb s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm, 666*a4128aadSBjoern A. Zeeb const struct cfg80211_chan_def *chandef, 667*a4128aadSBjoern A. Zeeb bool low) 668*a4128aadSBjoern A. Zeeb { 669*a4128aadSBjoern A. Zeeb const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map[] = { 670*a4128aadSBjoern A. Zeeb BW_TO_RSSI_THRESHOLDS(20), 671*a4128aadSBjoern A. Zeeb BW_TO_RSSI_THRESHOLDS(40), 672*a4128aadSBjoern A. Zeeb BW_TO_RSSI_THRESHOLDS(80), 673*a4128aadSBjoern A. Zeeb BW_TO_RSSI_THRESHOLDS(160) 674*a4128aadSBjoern A. Zeeb /* 320 MHz has the same thresholds as 20 MHz */ 675*a4128aadSBjoern A. Zeeb }; 676*a4128aadSBjoern A. Zeeb const struct iwl_mvm_bw_to_rssi_threshs *threshs; 677*a4128aadSBjoern A. Zeeb u8 chan_width = iwl_mvm_get_channel_width(chandef); 678*a4128aadSBjoern A. Zeeb 679*a4128aadSBjoern A. Zeeb if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ && 680*a4128aadSBjoern A. Zeeb chandef->chan->band != NL80211_BAND_5GHZ && 681*a4128aadSBjoern A. Zeeb chandef->chan->band != NL80211_BAND_6GHZ)) 682*a4128aadSBjoern A. Zeeb return S8_MAX; 683*a4128aadSBjoern A. Zeeb 684*a4128aadSBjoern A. Zeeb /* 6 GHz will always use 20 MHz thresholds, regardless of the BW */ 685*a4128aadSBjoern A. Zeeb if (chan_width == IWL_PHY_CHANNEL_MODE320) 686*a4128aadSBjoern A. Zeeb chan_width = IWL_PHY_CHANNEL_MODE20; 687*a4128aadSBjoern A. Zeeb 688*a4128aadSBjoern A. Zeeb threshs = &bw_to_rssi_threshs_map[chan_width]; 689*a4128aadSBjoern A. Zeeb 690*a4128aadSBjoern A. Zeeb return low ? threshs->low : threshs->high; 691*a4128aadSBjoern A. Zeeb } 692*a4128aadSBjoern A. Zeeb 693*a4128aadSBjoern A. Zeeb static u32 694*a4128aadSBjoern A. Zeeb iwl_mvm_esr_disallowed_with_link(struct iwl_mvm *mvm, 695*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 696*a4128aadSBjoern A. Zeeb const struct iwl_mvm_link_sel_data *link, 697*a4128aadSBjoern A. Zeeb bool primary) 698*a4128aadSBjoern A. Zeeb { 699*a4128aadSBjoern A. Zeeb struct wiphy *wiphy = mvm->hw->wiphy; 700*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *conf; 701*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state ret = 0; 702*a4128aadSBjoern A. Zeeb s8 thresh; 703*a4128aadSBjoern A. Zeeb 704*a4128aadSBjoern A. Zeeb conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); 705*a4128aadSBjoern A. Zeeb if (WARN_ON_ONCE(!conf)) 706*a4128aadSBjoern A. Zeeb return false; 707*a4128aadSBjoern A. Zeeb 708*a4128aadSBjoern A. Zeeb /* BT Coex effects eSR mode only if one of the links is on LB */ 709*a4128aadSBjoern A. Zeeb if (link->chandef->chan->band == NL80211_BAND_2GHZ && 710*a4128aadSBjoern A. Zeeb (!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link->signal, 711*a4128aadSBjoern A. Zeeb primary))) 712*a4128aadSBjoern A. Zeeb ret |= IWL_MVM_ESR_EXIT_COEX; 713*a4128aadSBjoern A. Zeeb 714*a4128aadSBjoern A. Zeeb thresh = iwl_mvm_get_esr_rssi_thresh(mvm, link->chandef, 715*a4128aadSBjoern A. Zeeb false); 716*a4128aadSBjoern A. Zeeb 717*a4128aadSBjoern A. Zeeb if (link->signal < thresh) 718*a4128aadSBjoern A. Zeeb ret |= IWL_MVM_ESR_EXIT_LOW_RSSI; 719*a4128aadSBjoern A. Zeeb 720*a4128aadSBjoern A. Zeeb if (conf->csa_active) 721*a4128aadSBjoern A. Zeeb ret |= IWL_MVM_ESR_EXIT_CSA; 722*a4128aadSBjoern A. Zeeb 723*a4128aadSBjoern A. Zeeb if (ret) { 724*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 725*a4128aadSBjoern A. Zeeb "Link %d is not allowed for esr\n", 726*a4128aadSBjoern A. Zeeb link->link_id); 727*a4128aadSBjoern A. Zeeb iwl_mvm_print_esr_state(mvm, ret); 728*a4128aadSBjoern A. Zeeb } 729*a4128aadSBjoern A. Zeeb return ret; 730*a4128aadSBjoern A. Zeeb } 731*a4128aadSBjoern A. Zeeb 732*a4128aadSBjoern A. Zeeb VISIBLE_IF_IWLWIFI_KUNIT 733*a4128aadSBjoern A. Zeeb bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif, 734*a4128aadSBjoern A. Zeeb const struct iwl_mvm_link_sel_data *a, 735*a4128aadSBjoern A. Zeeb const struct iwl_mvm_link_sel_data *b) 736*a4128aadSBjoern A. Zeeb { 737*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 738*a4128aadSBjoern A. Zeeb struct iwl_mvm *mvm = mvmvif->mvm; 739*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state ret = 0; 740*a4128aadSBjoern A. Zeeb 741*a4128aadSBjoern A. Zeeb /* Per-link considerations */ 742*a4128aadSBjoern A. Zeeb if (iwl_mvm_esr_disallowed_with_link(mvm, vif, a, true) || 743*a4128aadSBjoern A. Zeeb iwl_mvm_esr_disallowed_with_link(mvm, vif, b, false)) 744*a4128aadSBjoern A. Zeeb return false; 745*a4128aadSBjoern A. Zeeb 746*a4128aadSBjoern A. Zeeb if (a->chandef->width != b->chandef->width || 747*a4128aadSBjoern A. Zeeb !(a->chandef->chan->band == NL80211_BAND_6GHZ && 748*a4128aadSBjoern A. Zeeb b->chandef->chan->band == NL80211_BAND_5GHZ)) 749*a4128aadSBjoern A. Zeeb ret |= IWL_MVM_ESR_EXIT_BANDWIDTH; 750*a4128aadSBjoern A. Zeeb 751*a4128aadSBjoern A. Zeeb if (ret) { 752*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 753*a4128aadSBjoern A. Zeeb "Links %d and %d are not a valid pair for EMLSR\n", 754*a4128aadSBjoern A. Zeeb a->link_id, b->link_id); 755*a4128aadSBjoern A. Zeeb iwl_mvm_print_esr_state(mvm, ret); 756*a4128aadSBjoern A. Zeeb return false; 757*a4128aadSBjoern A. Zeeb } 758*a4128aadSBjoern A. Zeeb 759*a4128aadSBjoern A. Zeeb return true; 760*a4128aadSBjoern A. Zeeb 761*a4128aadSBjoern A. Zeeb } 762*a4128aadSBjoern A. Zeeb EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair); 763*a4128aadSBjoern A. Zeeb 764*a4128aadSBjoern A. Zeeb /* 765*a4128aadSBjoern A. Zeeb * Returns the combined eSR grade of two given links. 766*a4128aadSBjoern A. Zeeb * Returns 0 if eSR is not allowed with these 2 links. 767*a4128aadSBjoern A. Zeeb */ 768*a4128aadSBjoern A. Zeeb static 769*a4128aadSBjoern A. Zeeb unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif *vif, 770*a4128aadSBjoern A. Zeeb const struct iwl_mvm_link_sel_data *a, 771*a4128aadSBjoern A. Zeeb const struct iwl_mvm_link_sel_data *b, 772*a4128aadSBjoern A. Zeeb u8 *primary_id) 773*a4128aadSBjoern A. Zeeb { 774*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *primary_conf; 775*a4128aadSBjoern A. Zeeb struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy; 776*a4128aadSBjoern A. Zeeb unsigned int primary_load; 777*a4128aadSBjoern A. Zeeb 778*a4128aadSBjoern A. Zeeb lockdep_assert_wiphy(wiphy); 779*a4128aadSBjoern A. Zeeb 780*a4128aadSBjoern A. Zeeb /* a is always primary, b is always secondary */ 781*a4128aadSBjoern A. Zeeb if (b->grade > a->grade) 782*a4128aadSBjoern A. Zeeb swap(a, b); 783*a4128aadSBjoern A. Zeeb 784*a4128aadSBjoern A. Zeeb *primary_id = a->link_id; 785*a4128aadSBjoern A. Zeeb 786*a4128aadSBjoern A. Zeeb if (!iwl_mvm_mld_valid_link_pair(vif, a, b)) 787*a4128aadSBjoern A. Zeeb return 0; 788*a4128aadSBjoern A. Zeeb 789*a4128aadSBjoern A. Zeeb primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]); 790*a4128aadSBjoern A. Zeeb 791*a4128aadSBjoern A. Zeeb if (WARN_ON_ONCE(!primary_conf)) 792*a4128aadSBjoern A. Zeeb return 0; 793*a4128aadSBjoern A. Zeeb 794*a4128aadSBjoern A. Zeeb primary_load = iwl_mvm_get_chan_load(primary_conf); 795*a4128aadSBjoern A. Zeeb 796*a4128aadSBjoern A. Zeeb return a->grade + 797*a4128aadSBjoern A. Zeeb ((b->grade * primary_load) / SCALE_FACTOR); 798*a4128aadSBjoern A. Zeeb } 799*a4128aadSBjoern A. Zeeb 800*a4128aadSBjoern A. Zeeb void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 801*a4128aadSBjoern A. Zeeb { 802*a4128aadSBjoern A. Zeeb struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; 803*a4128aadSBjoern A. Zeeb struct iwl_mvm_link_sel_data *best_link; 804*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 805*a4128aadSBjoern A. Zeeb u32 max_active_links = iwl_mvm_max_active_links(mvm, vif); 806*a4128aadSBjoern A. Zeeb u16 usable_links = ieee80211_vif_usable_links(vif); 807*a4128aadSBjoern A. Zeeb u8 best, primary_link, best_in_pair, n_data; 808*a4128aadSBjoern A. Zeeb u16 max_esr_grade = 0, new_active_links; 809*a4128aadSBjoern A. Zeeb 810*a4128aadSBjoern A. Zeeb lockdep_assert_wiphy(mvm->hw->wiphy); 811*a4128aadSBjoern A. Zeeb 812*a4128aadSBjoern A. Zeeb if (!mvmvif->authorized || !ieee80211_vif_is_mld(vif)) 813*a4128aadSBjoern A. Zeeb return; 814*a4128aadSBjoern A. Zeeb 815*a4128aadSBjoern A. Zeeb if (!IWL_MVM_AUTO_EML_ENABLE) 816*a4128aadSBjoern A. Zeeb return; 817*a4128aadSBjoern A. Zeeb 818*a4128aadSBjoern A. Zeeb /* The logic below is a simple version that doesn't suit more than 2 819*a4128aadSBjoern A. Zeeb * links 820*a4128aadSBjoern A. Zeeb */ 821*a4128aadSBjoern A. Zeeb WARN_ON_ONCE(max_active_links > 2); 822*a4128aadSBjoern A. Zeeb 823*a4128aadSBjoern A. Zeeb n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links, 824*a4128aadSBjoern A. Zeeb &best); 825*a4128aadSBjoern A. Zeeb 826*a4128aadSBjoern A. Zeeb if (WARN(!n_data, "Couldn't find a valid grade for any link!\n")) 827*a4128aadSBjoern A. Zeeb return; 828*a4128aadSBjoern A. Zeeb 829*a4128aadSBjoern A. Zeeb best_link = &data[best]; 830*a4128aadSBjoern A. Zeeb primary_link = best_link->link_id; 831*a4128aadSBjoern A. Zeeb new_active_links = BIT(best_link->link_id); 832*a4128aadSBjoern A. Zeeb 833*a4128aadSBjoern A. Zeeb /* eSR is not supported/blocked, or only one usable link */ 834*a4128aadSBjoern A. Zeeb if (max_active_links == 1 || !iwl_mvm_vif_has_esr_cap(mvm, vif) || 835*a4128aadSBjoern A. Zeeb mvmvif->esr_disable_reason || n_data == 1) 836*a4128aadSBjoern A. Zeeb goto set_active; 837*a4128aadSBjoern A. Zeeb 838*a4128aadSBjoern A. Zeeb for (u8 a = 0; a < n_data; a++) 839*a4128aadSBjoern A. Zeeb for (u8 b = a + 1; b < n_data; b++) { 840*a4128aadSBjoern A. Zeeb u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a], 841*a4128aadSBjoern A. Zeeb &data[b], 842*a4128aadSBjoern A. Zeeb &best_in_pair); 843*a4128aadSBjoern A. Zeeb 844*a4128aadSBjoern A. Zeeb if (esr_grade <= max_esr_grade) 845*a4128aadSBjoern A. Zeeb continue; 846*a4128aadSBjoern A. Zeeb 847*a4128aadSBjoern A. Zeeb max_esr_grade = esr_grade; 848*a4128aadSBjoern A. Zeeb primary_link = best_in_pair; 849*a4128aadSBjoern A. Zeeb new_active_links = BIT(data[a].link_id) | 850*a4128aadSBjoern A. Zeeb BIT(data[b].link_id); 851*a4128aadSBjoern A. Zeeb } 852*a4128aadSBjoern A. Zeeb 853*a4128aadSBjoern A. Zeeb /* No valid pair was found, go with the best link */ 854*a4128aadSBjoern A. Zeeb if (hweight16(new_active_links) <= 1) 855*a4128aadSBjoern A. Zeeb goto set_active; 856*a4128aadSBjoern A. Zeeb 857*a4128aadSBjoern A. Zeeb /* For equal grade - prefer EMLSR */ 858*a4128aadSBjoern A. Zeeb if (best_link->grade > max_esr_grade) { 859*a4128aadSBjoern A. Zeeb primary_link = best_link->link_id; 860*a4128aadSBjoern A. Zeeb new_active_links = BIT(best_link->link_id); 861*a4128aadSBjoern A. Zeeb } 862*a4128aadSBjoern A. Zeeb set_active: 863*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "Link selection result: 0x%x. Primary = %d\n", 864*a4128aadSBjoern A. Zeeb new_active_links, primary_link); 865*a4128aadSBjoern A. Zeeb ieee80211_set_active_links_async(vif, new_active_links); 866*a4128aadSBjoern A. Zeeb mvmvif->link_selection_res = new_active_links; 867*a4128aadSBjoern A. Zeeb mvmvif->link_selection_primary = primary_link; 868*a4128aadSBjoern A. Zeeb } 869*a4128aadSBjoern A. Zeeb 870*a4128aadSBjoern A. Zeeb u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif) 871*a4128aadSBjoern A. Zeeb { 872*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 873*a4128aadSBjoern A. Zeeb 874*a4128aadSBjoern A. Zeeb /* relevant data is written with both locks held, so read with either */ 875*a4128aadSBjoern A. Zeeb lockdep_assert(lockdep_is_held(&mvmvif->mvm->mutex) || 876*a4128aadSBjoern A. Zeeb lockdep_is_held(&mvmvif->mvm->hw->wiphy->mtx)); 877*a4128aadSBjoern A. Zeeb 878*a4128aadSBjoern A. Zeeb if (!ieee80211_vif_is_mld(vif)) 879*a4128aadSBjoern A. Zeeb return 0; 880*a4128aadSBjoern A. Zeeb 881*a4128aadSBjoern A. Zeeb /* In AP mode, there is no primary link */ 882*a4128aadSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP) 883*a4128aadSBjoern A. Zeeb return __ffs(vif->active_links); 884*a4128aadSBjoern A. Zeeb 885*a4128aadSBjoern A. Zeeb if (mvmvif->esr_active && 886*a4128aadSBjoern A. Zeeb !WARN_ON(!(BIT(mvmvif->primary_link) & vif->active_links))) 887*a4128aadSBjoern A. Zeeb return mvmvif->primary_link; 888*a4128aadSBjoern A. Zeeb 889*a4128aadSBjoern A. Zeeb return __ffs(vif->active_links); 890*a4128aadSBjoern A. Zeeb } 891*a4128aadSBjoern A. Zeeb 892*a4128aadSBjoern A. Zeeb /* 893*a4128aadSBjoern A. Zeeb * For non-MLO/single link, this will return the deflink/single active link, 894*a4128aadSBjoern A. Zeeb * respectively 895*a4128aadSBjoern A. Zeeb */ 896*a4128aadSBjoern A. Zeeb u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id) 897*a4128aadSBjoern A. Zeeb { 898*a4128aadSBjoern A. Zeeb switch (hweight16(vif->active_links)) { 899*a4128aadSBjoern A. Zeeb case 0: 900*a4128aadSBjoern A. Zeeb return 0; 901*a4128aadSBjoern A. Zeeb default: 902*a4128aadSBjoern A. Zeeb WARN_ON(1); 903*a4128aadSBjoern A. Zeeb fallthrough; 904*a4128aadSBjoern A. Zeeb case 1: 905*a4128aadSBjoern A. Zeeb return __ffs(vif->active_links); 906*a4128aadSBjoern A. Zeeb case 2: 907*a4128aadSBjoern A. Zeeb return __ffs(vif->active_links & ~BIT(link_id)); 908*a4128aadSBjoern A. Zeeb } 909*a4128aadSBjoern A. Zeeb } 910*a4128aadSBjoern A. Zeeb 911*a4128aadSBjoern A. Zeeb /* Reasons that can cause esr prevention */ 912*a4128aadSBjoern A. Zeeb #define IWL_MVM_ESR_PREVENT_REASONS IWL_MVM_ESR_EXIT_MISSED_BEACON 913*a4128aadSBjoern A. Zeeb #define IWL_MVM_PREVENT_ESR_TIMEOUT (HZ * 400) 914*a4128aadSBjoern A. Zeeb #define IWL_MVM_ESR_PREVENT_SHORT (HZ * 300) 915*a4128aadSBjoern A. Zeeb #define IWL_MVM_ESR_PREVENT_LONG (HZ * 600) 916*a4128aadSBjoern A. Zeeb 917*a4128aadSBjoern A. Zeeb static bool iwl_mvm_check_esr_prevention(struct iwl_mvm *mvm, 918*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif, 919*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state reason) 920*a4128aadSBjoern A. Zeeb { 921*a4128aadSBjoern A. Zeeb bool timeout_expired = time_after(jiffies, 922*a4128aadSBjoern A. Zeeb mvmvif->last_esr_exit.ts + 923*a4128aadSBjoern A. Zeeb IWL_MVM_PREVENT_ESR_TIMEOUT); 924*a4128aadSBjoern A. Zeeb unsigned long delay; 925*a4128aadSBjoern A. Zeeb 926*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 927*a4128aadSBjoern A. Zeeb 928*a4128aadSBjoern A. Zeeb /* Only handle reasons that can cause prevention */ 929*a4128aadSBjoern A. Zeeb if (!(reason & IWL_MVM_ESR_PREVENT_REASONS)) 930*a4128aadSBjoern A. Zeeb return false; 931*a4128aadSBjoern A. Zeeb 932*a4128aadSBjoern A. Zeeb /* 933*a4128aadSBjoern A. Zeeb * Reset the counter if more than 400 seconds have passed between one 934*a4128aadSBjoern A. Zeeb * exit and the other, or if we exited due to a different reason. 935*a4128aadSBjoern A. Zeeb * Will also reset the counter after the long prevention is done. 936*a4128aadSBjoern A. Zeeb */ 937*a4128aadSBjoern A. Zeeb if (timeout_expired || mvmvif->last_esr_exit.reason != reason) { 938*a4128aadSBjoern A. Zeeb mvmvif->exit_same_reason_count = 1; 939*a4128aadSBjoern A. Zeeb return false; 940*a4128aadSBjoern A. Zeeb } 941*a4128aadSBjoern A. Zeeb 942*a4128aadSBjoern A. Zeeb mvmvif->exit_same_reason_count++; 943*a4128aadSBjoern A. Zeeb if (WARN_ON(mvmvif->exit_same_reason_count < 2 || 944*a4128aadSBjoern A. Zeeb mvmvif->exit_same_reason_count > 3)) 945*a4128aadSBjoern A. Zeeb return false; 946*a4128aadSBjoern A. Zeeb 947*a4128aadSBjoern A. Zeeb mvmvif->esr_disable_reason |= IWL_MVM_ESR_BLOCKED_PREVENTION; 948*a4128aadSBjoern A. Zeeb 949*a4128aadSBjoern A. Zeeb /* 950*a4128aadSBjoern A. Zeeb * For the second exit, use a short prevention, and for the third one, 951*a4128aadSBjoern A. Zeeb * use a long prevention. 952*a4128aadSBjoern A. Zeeb */ 953*a4128aadSBjoern A. Zeeb delay = mvmvif->exit_same_reason_count == 2 ? 954*a4128aadSBjoern A. Zeeb IWL_MVM_ESR_PREVENT_SHORT : 955*a4128aadSBjoern A. Zeeb IWL_MVM_ESR_PREVENT_LONG; 956*a4128aadSBjoern A. Zeeb 957*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 958*a4128aadSBjoern A. Zeeb "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n", 959*a4128aadSBjoern A. Zeeb delay / HZ, mvmvif->exit_same_reason_count, 960*a4128aadSBjoern A. Zeeb iwl_get_esr_state_string(reason), reason); 961*a4128aadSBjoern A. Zeeb 962*a4128aadSBjoern A. Zeeb wiphy_delayed_work_queue(mvm->hw->wiphy, 963*a4128aadSBjoern A. Zeeb &mvmvif->prevent_esr_done_wk, delay); 964*a4128aadSBjoern A. Zeeb return true; 965*a4128aadSBjoern A. Zeeb } 966*a4128aadSBjoern A. Zeeb 967*a4128aadSBjoern A. Zeeb #define IWL_MVM_TRIGGER_LINK_SEL_TIME (IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC * HZ) 968*a4128aadSBjoern A. Zeeb 969*a4128aadSBjoern A. Zeeb /* API to exit eSR mode */ 970*a4128aadSBjoern A. Zeeb void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 971*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state reason, 972*a4128aadSBjoern A. Zeeb u8 link_to_keep) 973*a4128aadSBjoern A. Zeeb { 974*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 975*a4128aadSBjoern A. Zeeb u16 new_active_links; 976*a4128aadSBjoern A. Zeeb bool prevented; 977*a4128aadSBjoern A. Zeeb 978*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 979*a4128aadSBjoern A. Zeeb 980*a4128aadSBjoern A. Zeeb if (!IWL_MVM_AUTO_EML_ENABLE) 981*a4128aadSBjoern A. Zeeb return; 982*a4128aadSBjoern A. Zeeb 983*a4128aadSBjoern A. Zeeb /* Nothing to do */ 984*a4128aadSBjoern A. Zeeb if (!mvmvif->esr_active) 985*a4128aadSBjoern A. Zeeb return; 986*a4128aadSBjoern A. Zeeb 987*a4128aadSBjoern A. Zeeb if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mvmvif->authorized)) 988*a4128aadSBjoern A. Zeeb return; 989*a4128aadSBjoern A. Zeeb 990*a4128aadSBjoern A. Zeeb if (WARN_ON(!(vif->active_links & BIT(link_to_keep)))) 991*a4128aadSBjoern A. Zeeb link_to_keep = __ffs(vif->active_links); 992*a4128aadSBjoern A. Zeeb 993*a4128aadSBjoern A. Zeeb new_active_links = BIT(link_to_keep); 994*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 995*a4128aadSBjoern A. Zeeb "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n", 996*a4128aadSBjoern A. Zeeb iwl_get_esr_state_string(reason), reason, 997*a4128aadSBjoern A. Zeeb vif->active_links, new_active_links); 998*a4128aadSBjoern A. Zeeb 999*a4128aadSBjoern A. Zeeb ieee80211_set_active_links_async(vif, new_active_links); 1000*a4128aadSBjoern A. Zeeb 1001*a4128aadSBjoern A. Zeeb /* Prevent EMLSR if needed */ 1002*a4128aadSBjoern A. Zeeb prevented = iwl_mvm_check_esr_prevention(mvm, mvmvif, reason); 1003*a4128aadSBjoern A. Zeeb 1004*a4128aadSBjoern A. Zeeb /* Remember why and when we exited EMLSR */ 1005*a4128aadSBjoern A. Zeeb mvmvif->last_esr_exit.ts = jiffies; 1006*a4128aadSBjoern A. Zeeb mvmvif->last_esr_exit.reason = reason; 1007*a4128aadSBjoern A. Zeeb 1008*a4128aadSBjoern A. Zeeb /* 1009*a4128aadSBjoern A. Zeeb * If EMLSR is prevented now - don't try to get back to EMLSR. 1010*a4128aadSBjoern A. Zeeb * If we exited due to a blocking event, we will try to get back to 1011*a4128aadSBjoern A. Zeeb * EMLSR when the corresponding unblocking event will happen. 1012*a4128aadSBjoern A. Zeeb */ 1013*a4128aadSBjoern A. Zeeb if (prevented || reason & IWL_MVM_BLOCK_ESR_REASONS) 1014*a4128aadSBjoern A. Zeeb return; 1015*a4128aadSBjoern A. Zeeb 1016*a4128aadSBjoern A. Zeeb /* If EMLSR is not blocked - try enabling it again in 30 seconds */ 1017*a4128aadSBjoern A. Zeeb wiphy_delayed_work_queue(mvm->hw->wiphy, 1018*a4128aadSBjoern A. Zeeb &mvmvif->mlo_int_scan_wk, 1019*a4128aadSBjoern A. Zeeb round_jiffies_relative(IWL_MVM_TRIGGER_LINK_SEL_TIME)); 1020*a4128aadSBjoern A. Zeeb } 1021*a4128aadSBjoern A. Zeeb 1022*a4128aadSBjoern A. Zeeb void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 1023*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state reason, 1024*a4128aadSBjoern A. Zeeb u8 link_to_keep) 1025*a4128aadSBjoern A. Zeeb { 1026*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1027*a4128aadSBjoern A. Zeeb 1028*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1029*a4128aadSBjoern A. Zeeb 1030*a4128aadSBjoern A. Zeeb if (!IWL_MVM_AUTO_EML_ENABLE) 1031*a4128aadSBjoern A. Zeeb return; 1032*a4128aadSBjoern A. Zeeb 1033*a4128aadSBjoern A. Zeeb /* This should be called only with disable reasons */ 1034*a4128aadSBjoern A. Zeeb if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS))) 1035*a4128aadSBjoern A. Zeeb return; 1036*a4128aadSBjoern A. Zeeb 1037*a4128aadSBjoern A. Zeeb if (mvmvif->esr_disable_reason & reason) 1038*a4128aadSBjoern A. Zeeb return; 1039*a4128aadSBjoern A. Zeeb 1040*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1041*a4128aadSBjoern A. Zeeb "Blocking EMLSR mode. reason = %s (0x%x)\n", 1042*a4128aadSBjoern A. Zeeb iwl_get_esr_state_string(reason), reason); 1043*a4128aadSBjoern A. Zeeb 1044*a4128aadSBjoern A. Zeeb mvmvif->esr_disable_reason |= reason; 1045*a4128aadSBjoern A. Zeeb 1046*a4128aadSBjoern A. Zeeb iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason); 1047*a4128aadSBjoern A. Zeeb 1048*a4128aadSBjoern A. Zeeb iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep); 1049*a4128aadSBjoern A. Zeeb } 1050*a4128aadSBjoern A. Zeeb 1051*a4128aadSBjoern A. Zeeb int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 1052*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state reason) 1053*a4128aadSBjoern A. Zeeb { 1054*a4128aadSBjoern A. Zeeb int primary_link = iwl_mvm_get_primary_link(vif); 1055*a4128aadSBjoern A. Zeeb int ret; 1056*a4128aadSBjoern A. Zeeb 1057*a4128aadSBjoern A. Zeeb if (!IWL_MVM_AUTO_EML_ENABLE || !ieee80211_vif_is_mld(vif)) 1058*a4128aadSBjoern A. Zeeb return 0; 1059*a4128aadSBjoern A. Zeeb 1060*a4128aadSBjoern A. Zeeb /* This should be called only with blocking reasons */ 1061*a4128aadSBjoern A. Zeeb if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS))) 1062*a4128aadSBjoern A. Zeeb return 0; 1063*a4128aadSBjoern A. Zeeb 1064*a4128aadSBjoern A. Zeeb /* leave ESR immediately, not only async with iwl_mvm_block_esr() */ 1065*a4128aadSBjoern A. Zeeb ret = ieee80211_set_active_links(vif, BIT(primary_link)); 1066*a4128aadSBjoern A. Zeeb if (ret) 1067*a4128aadSBjoern A. Zeeb return ret; 1068*a4128aadSBjoern A. Zeeb 1069*a4128aadSBjoern A. Zeeb mutex_lock(&mvm->mutex); 1070*a4128aadSBjoern A. Zeeb /* only additionally block for consistency and to avoid concurrency */ 1071*a4128aadSBjoern A. Zeeb iwl_mvm_block_esr(mvm, vif, reason, primary_link); 1072*a4128aadSBjoern A. Zeeb mutex_unlock(&mvm->mutex); 1073*a4128aadSBjoern A. Zeeb 1074*a4128aadSBjoern A. Zeeb return 0; 1075*a4128aadSBjoern A. Zeeb } 1076*a4128aadSBjoern A. Zeeb 1077*a4128aadSBjoern A. Zeeb static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm, 1078*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif) 1079*a4128aadSBjoern A. Zeeb { 1080*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1081*a4128aadSBjoern A. Zeeb bool need_new_sel = time_after(jiffies, mvmvif->last_esr_exit.ts + 1082*a4128aadSBjoern A. Zeeb IWL_MVM_TRIGGER_LINK_SEL_TIME); 1083*a4128aadSBjoern A. Zeeb 1084*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1085*a4128aadSBjoern A. Zeeb 1086*a4128aadSBjoern A. Zeeb if (!ieee80211_vif_is_mld(vif) || !mvmvif->authorized || 1087*a4128aadSBjoern A. Zeeb mvmvif->esr_active) 1088*a4128aadSBjoern A. Zeeb return; 1089*a4128aadSBjoern A. Zeeb 1090*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "EMLSR is unblocked\n"); 1091*a4128aadSBjoern A. Zeeb 1092*a4128aadSBjoern A. Zeeb /* If we exited due to an EXIT reason, and the exit was in less than 1093*a4128aadSBjoern A. Zeeb * 30 seconds, then a MLO scan was scheduled already. 1094*a4128aadSBjoern A. Zeeb */ 1095*a4128aadSBjoern A. Zeeb if (!need_new_sel && 1096*a4128aadSBjoern A. Zeeb !(mvmvif->last_esr_exit.reason & IWL_MVM_BLOCK_ESR_REASONS)) { 1097*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "Wait for MLO scan\n"); 1098*a4128aadSBjoern A. Zeeb return; 1099*a4128aadSBjoern A. Zeeb } 1100*a4128aadSBjoern A. Zeeb 1101*a4128aadSBjoern A. Zeeb /* 1102*a4128aadSBjoern A. Zeeb * If EMLSR was blocked for more than 30 seconds, or the last link 1103*a4128aadSBjoern A. Zeeb * selection decided to not enter EMLSR, trigger a new scan. 1104*a4128aadSBjoern A. Zeeb */ 1105*a4128aadSBjoern A. Zeeb if (need_new_sel || hweight16(mvmvif->link_selection_res) < 2) { 1106*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "Trigger MLO scan\n"); 1107*a4128aadSBjoern A. Zeeb wiphy_delayed_work_queue(mvm->hw->wiphy, 1108*a4128aadSBjoern A. Zeeb &mvmvif->mlo_int_scan_wk, 0); 1109*a4128aadSBjoern A. Zeeb /* 1110*a4128aadSBjoern A. Zeeb * If EMLSR was blocked for less than 30 seconds, and the last link 1111*a4128aadSBjoern A. Zeeb * selection decided to use EMLSR, activate EMLSR using the previous 1112*a4128aadSBjoern A. Zeeb * link selection result. 1113*a4128aadSBjoern A. Zeeb */ 1114*a4128aadSBjoern A. Zeeb } else { 1115*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1116*a4128aadSBjoern A. Zeeb "Use the latest link selection result: 0x%x\n", 1117*a4128aadSBjoern A. Zeeb mvmvif->link_selection_res); 1118*a4128aadSBjoern A. Zeeb ieee80211_set_active_links_async(vif, 1119*a4128aadSBjoern A. Zeeb mvmvif->link_selection_res); 1120*a4128aadSBjoern A. Zeeb } 1121*a4128aadSBjoern A. Zeeb } 1122*a4128aadSBjoern A. Zeeb 1123*a4128aadSBjoern A. Zeeb void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 1124*a4128aadSBjoern A. Zeeb enum iwl_mvm_esr_state reason) 1125*a4128aadSBjoern A. Zeeb { 1126*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1127*a4128aadSBjoern A. Zeeb 1128*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1129*a4128aadSBjoern A. Zeeb 1130*a4128aadSBjoern A. Zeeb if (!IWL_MVM_AUTO_EML_ENABLE) 1131*a4128aadSBjoern A. Zeeb return; 1132*a4128aadSBjoern A. Zeeb 1133*a4128aadSBjoern A. Zeeb /* This should be called only with disable reasons */ 1134*a4128aadSBjoern A. Zeeb if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS))) 1135*a4128aadSBjoern A. Zeeb return; 1136*a4128aadSBjoern A. Zeeb 1137*a4128aadSBjoern A. Zeeb /* No Change */ 1138*a4128aadSBjoern A. Zeeb if (!(mvmvif->esr_disable_reason & reason)) 1139*a4128aadSBjoern A. Zeeb return; 1140*a4128aadSBjoern A. Zeeb 1141*a4128aadSBjoern A. Zeeb mvmvif->esr_disable_reason &= ~reason; 1142*a4128aadSBjoern A. Zeeb 1143*a4128aadSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1144*a4128aadSBjoern A. Zeeb "Unblocking EMLSR mode. reason = %s (0x%x)\n", 1145*a4128aadSBjoern A. Zeeb iwl_get_esr_state_string(reason), reason); 1146*a4128aadSBjoern A. Zeeb iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason); 1147*a4128aadSBjoern A. Zeeb 1148*a4128aadSBjoern A. Zeeb if (!mvmvif->esr_disable_reason) 1149*a4128aadSBjoern A. Zeeb iwl_mvm_esr_unblocked(mvm, vif); 1150*a4128aadSBjoern A. Zeeb } 1151