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 79af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, 89af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 99af1bba4SBjoern A. Zeeb { 109af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 119af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 129af1bba4SBjoern A. Zeeb int ret; 13*a4128aadSBjoern A. Zeeb int i; 149af1bba4SBjoern A. Zeeb 15*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 16*a4128aadSBjoern A. Zeeb 17*a4128aadSBjoern A. Zeeb iwl_mvm_mac_init_mvmvif(mvm, mvmvif); 189af1bba4SBjoern A. Zeeb 199af1bba4SBjoern A. Zeeb mvmvif->mvm = mvm; 209af1bba4SBjoern A. Zeeb 219af1bba4SBjoern A. Zeeb /* Not much to do here. The stack will not allow interface 229af1bba4SBjoern A. Zeeb * types or combinations that we didn't advertise, so we 239af1bba4SBjoern A. Zeeb * don't really have to check the types. 249af1bba4SBjoern A. Zeeb */ 259af1bba4SBjoern A. Zeeb 269af1bba4SBjoern A. Zeeb /* make sure that beacon statistics don't go backwards with FW reset */ 279af1bba4SBjoern A. Zeeb if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 28*a4128aadSBjoern A. Zeeb for_each_mvm_vif_valid_link(mvmvif, i) 29*a4128aadSBjoern A. Zeeb mvmvif->link[i]->beacon_stats.accu_num_beacons += 30*a4128aadSBjoern A. Zeeb mvmvif->link[i]->beacon_stats.num_beacons; 319af1bba4SBjoern A. Zeeb 329af1bba4SBjoern A. Zeeb /* Allocate resources for the MAC context, and add it to the fw */ 339af1bba4SBjoern A. Zeeb ret = iwl_mvm_mac_ctxt_init(mvm, vif); 349af1bba4SBjoern A. Zeeb if (ret) 35*a4128aadSBjoern A. Zeeb return ret; 369af1bba4SBjoern A. Zeeb 379af1bba4SBjoern A. Zeeb rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif); 389af1bba4SBjoern A. Zeeb 399af1bba4SBjoern A. Zeeb mvmvif->features |= hw->netdev_features; 409af1bba4SBjoern A. Zeeb 419af1bba4SBjoern A. Zeeb /* reset deflink MLO parameters */ 429af1bba4SBjoern A. Zeeb mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; 439af1bba4SBjoern A. Zeeb mvmvif->deflink.active = 0; 449af1bba4SBjoern A. Zeeb /* the first link always points to the default one */ 459af1bba4SBjoern A. Zeeb mvmvif->link[0] = &mvmvif->deflink; 469af1bba4SBjoern A. Zeeb 479af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); 489af1bba4SBjoern A. Zeeb if (ret) 49*a4128aadSBjoern A. Zeeb return ret; 509af1bba4SBjoern A. Zeeb 519af1bba4SBjoern A. Zeeb /* beacon filtering */ 52*a4128aadSBjoern A. Zeeb ret = iwl_mvm_disable_beacon_filter(mvm, vif); 539af1bba4SBjoern A. Zeeb if (ret) 549af1bba4SBjoern A. Zeeb goto out_remove_mac; 559af1bba4SBjoern A. Zeeb 569af1bba4SBjoern A. Zeeb if (!mvm->bf_allowed_vif && 579af1bba4SBjoern A. Zeeb vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { 589af1bba4SBjoern A. Zeeb mvm->bf_allowed_vif = mvmvif; 599af1bba4SBjoern A. Zeeb vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | 609af1bba4SBjoern A. Zeeb IEEE80211_VIF_SUPPORTS_CQM_RSSI; 619af1bba4SBjoern A. Zeeb } 629af1bba4SBjoern A. Zeeb 639af1bba4SBjoern A. Zeeb ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); 649af1bba4SBjoern A. Zeeb if (ret) 65*a4128aadSBjoern A. Zeeb goto out_free_bf; 669af1bba4SBjoern A. Zeeb 679af1bba4SBjoern A. Zeeb /* Save a pointer to p2p device vif, so it can later be used to 689af1bba4SBjoern A. Zeeb * update the p2p device MAC when a GO is started/stopped 699af1bba4SBjoern A. Zeeb */ 70*a4128aadSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_P2P_DEVICE) 719af1bba4SBjoern A. Zeeb mvm->p2p_device_vif = vif; 729af1bba4SBjoern A. Zeeb 739af1bba4SBjoern A. Zeeb ret = iwl_mvm_power_update_mac(mvm); 749af1bba4SBjoern A. Zeeb if (ret) 759af1bba4SBjoern A. Zeeb goto out_free_bf; 769af1bba4SBjoern A. Zeeb 779af1bba4SBjoern A. Zeeb iwl_mvm_tcm_add_vif(mvm, vif); 789af1bba4SBjoern A. Zeeb 799af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_MONITOR) { 809af1bba4SBjoern A. Zeeb mvm->monitor_on = true; 819af1bba4SBjoern A. Zeeb ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); 829af1bba4SBjoern A. Zeeb } 839af1bba4SBjoern A. Zeeb 84*a4128aadSBjoern A. Zeeb if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 85*a4128aadSBjoern A. Zeeb iwl_mvm_vif_dbgfs_add_link(mvm, vif); 869af1bba4SBjoern A. Zeeb 879af1bba4SBjoern A. Zeeb if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && 889af1bba4SBjoern A. Zeeb vif->type == NL80211_IFTYPE_STATION && !vif->p2p && 899af1bba4SBjoern A. Zeeb !mvm->csme_vif && mvm->mei_registered) { 909af1bba4SBjoern A. Zeeb iwl_mei_set_nic_info(vif->addr, mvm->nvm_data->hw_addr); 919af1bba4SBjoern A. Zeeb iwl_mei_set_netdev(ieee80211_vif_to_wdev(vif)->netdev); 929af1bba4SBjoern A. Zeeb mvm->csme_vif = vif; 939af1bba4SBjoern A. Zeeb } 949af1bba4SBjoern A. Zeeb 95*a4128aadSBjoern A. Zeeb if (vif->p2p || iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) < 5) 96*a4128aadSBjoern A. Zeeb vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW; 979af1bba4SBjoern A. Zeeb 98*a4128aadSBjoern A. Zeeb return 0; 99*a4128aadSBjoern A. Zeeb 1009af1bba4SBjoern A. Zeeb out_free_bf: 1019af1bba4SBjoern A. Zeeb if (mvm->bf_allowed_vif == mvmvif) { 1029af1bba4SBjoern A. Zeeb mvm->bf_allowed_vif = NULL; 1039af1bba4SBjoern A. Zeeb vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | 1049af1bba4SBjoern A. Zeeb IEEE80211_VIF_SUPPORTS_CQM_RSSI); 1059af1bba4SBjoern A. Zeeb } 1069af1bba4SBjoern A. Zeeb out_remove_mac: 1079af1bba4SBjoern A. Zeeb mvmvif->link[0] = NULL; 1089af1bba4SBjoern A. Zeeb iwl_mvm_mld_mac_ctxt_remove(mvm, vif); 1099af1bba4SBjoern A. Zeeb return ret; 1109af1bba4SBjoern A. Zeeb } 1119af1bba4SBjoern A. Zeeb 1129af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, 1139af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 1149af1bba4SBjoern A. Zeeb { 1159af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 1169af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1179af1bba4SBjoern A. Zeeb struct iwl_probe_resp_data *probe_data; 1189af1bba4SBjoern A. Zeeb 1199af1bba4SBjoern A. Zeeb iwl_mvm_prepare_mac_removal(mvm, vif); 1209af1bba4SBjoern A. Zeeb 1219af1bba4SBjoern A. Zeeb if (!(vif->type == NL80211_IFTYPE_AP || 1229af1bba4SBjoern A. Zeeb vif->type == NL80211_IFTYPE_ADHOC)) 1239af1bba4SBjoern A. Zeeb iwl_mvm_tcm_rm_vif(mvm, vif); 1249af1bba4SBjoern A. Zeeb 125*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 1269af1bba4SBjoern A. Zeeb 1279af1bba4SBjoern A. Zeeb if (vif == mvm->csme_vif) { 1289af1bba4SBjoern A. Zeeb iwl_mei_set_netdev(NULL); 1299af1bba4SBjoern A. Zeeb mvm->csme_vif = NULL; 1309af1bba4SBjoern A. Zeeb } 1319af1bba4SBjoern A. Zeeb 1329af1bba4SBjoern A. Zeeb if (mvm->bf_allowed_vif == mvmvif) { 1339af1bba4SBjoern A. Zeeb mvm->bf_allowed_vif = NULL; 1349af1bba4SBjoern A. Zeeb vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | 1359af1bba4SBjoern A. Zeeb IEEE80211_VIF_SUPPORTS_CQM_RSSI); 1369af1bba4SBjoern A. Zeeb } 1379af1bba4SBjoern A. Zeeb 1389af1bba4SBjoern A. Zeeb if (vif->bss_conf.ftm_responder) 1399af1bba4SBjoern A. Zeeb memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats)); 1409af1bba4SBjoern A. Zeeb 141*a4128aadSBjoern A. Zeeb iwl_mvm_vif_dbgfs_rm_link(mvm, vif); 1429af1bba4SBjoern A. Zeeb 1439af1bba4SBjoern A. Zeeb /* For AP/GO interface, the tear down of the resources allocated to the 1449af1bba4SBjoern A. Zeeb * interface is be handled as part of the stop_ap flow. 1459af1bba4SBjoern A. Zeeb */ 1469af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP || 1479af1bba4SBjoern A. Zeeb vif->type == NL80211_IFTYPE_ADHOC) { 1489af1bba4SBjoern A. Zeeb #ifdef CONFIG_NL80211_TESTMODE 1499af1bba4SBjoern A. Zeeb if (vif == mvm->noa_vif) { 1509af1bba4SBjoern A. Zeeb mvm->noa_vif = NULL; 1519af1bba4SBjoern A. Zeeb mvm->noa_duration = 0; 1529af1bba4SBjoern A. Zeeb } 1539af1bba4SBjoern A. Zeeb #endif 1549af1bba4SBjoern A. Zeeb } 1559af1bba4SBjoern A. Zeeb 1569af1bba4SBjoern A. Zeeb iwl_mvm_power_update_mac(mvm); 1579af1bba4SBjoern A. Zeeb 158*a4128aadSBjoern A. Zeeb /* Before the interface removal, mac80211 would cancel the ROC, and the 159*a4128aadSBjoern A. Zeeb * ROC worker would be scheduled if needed. The worker would be flushed 160*a4128aadSBjoern A. Zeeb * in iwl_mvm_prepare_mac_removal() and thus at this point the link is 161*a4128aadSBjoern A. Zeeb * not active. So need only to remove the link. 162*a4128aadSBjoern A. Zeeb */ 1639af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 164*a4128aadSBjoern A. Zeeb if (mvmvif->deflink.phy_ctxt) { 1659af1bba4SBjoern A. Zeeb iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); 1669af1bba4SBjoern A. Zeeb mvmvif->deflink.phy_ctxt = NULL; 167*a4128aadSBjoern A. Zeeb } 168*a4128aadSBjoern A. Zeeb mvm->p2p_device_vif = NULL; 169*a4128aadSBjoern A. Zeeb iwl_mvm_remove_link(mvm, vif, &vif->bss_conf); 1709af1bba4SBjoern A. Zeeb } else { 1719af1bba4SBjoern A. Zeeb iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); 1729af1bba4SBjoern A. Zeeb } 1739af1bba4SBjoern A. Zeeb 1749af1bba4SBjoern A. Zeeb iwl_mvm_mld_mac_ctxt_remove(mvm, vif); 1759af1bba4SBjoern A. Zeeb 1769af1bba4SBjoern A. Zeeb RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); 1779af1bba4SBjoern A. Zeeb 1789af1bba4SBjoern A. Zeeb probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data, 1799af1bba4SBjoern A. Zeeb lockdep_is_held(&mvm->mutex)); 1809af1bba4SBjoern A. Zeeb RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL); 1819af1bba4SBjoern A. Zeeb if (probe_data) 1829af1bba4SBjoern A. Zeeb kfree_rcu(probe_data, rcu_head); 1839af1bba4SBjoern A. Zeeb 1849af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_MONITOR) { 1859af1bba4SBjoern A. Zeeb mvm->monitor_on = false; 1869af1bba4SBjoern A. Zeeb __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); 1879af1bba4SBjoern A. Zeeb } 1889af1bba4SBjoern A. Zeeb } 1899af1bba4SBjoern A. Zeeb 190*a4128aadSBjoern A. Zeeb static unsigned int iwl_mvm_mld_count_active_links(struct iwl_mvm_vif *mvmvif) 1919af1bba4SBjoern A. Zeeb { 1929af1bba4SBjoern A. Zeeb unsigned int n_active = 0; 1939af1bba4SBjoern A. Zeeb int i; 1949af1bba4SBjoern A. Zeeb 1959af1bba4SBjoern A. Zeeb for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { 196*a4128aadSBjoern A. Zeeb if (mvmvif->link[i] && mvmvif->link[i]->phy_ctxt) 1979af1bba4SBjoern A. Zeeb n_active++; 1989af1bba4SBjoern A. Zeeb } 1999af1bba4SBjoern A. Zeeb 2009af1bba4SBjoern A. Zeeb return n_active; 2019af1bba4SBjoern A. Zeeb } 2029af1bba4SBjoern A. Zeeb 203*a4128aadSBjoern A. Zeeb static void iwl_mvm_restart_mpdu_count(struct iwl_mvm *mvm, 204*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif) 205*a4128aadSBjoern A. Zeeb { 206*a4128aadSBjoern A. Zeeb struct ieee80211_sta *ap_sta = mvmvif->ap_sta; 207*a4128aadSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta; 208*a4128aadSBjoern A. Zeeb 209*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 210*a4128aadSBjoern A. Zeeb 211*a4128aadSBjoern A. Zeeb if (!ap_sta) 212*a4128aadSBjoern A. Zeeb return; 213*a4128aadSBjoern A. Zeeb 214*a4128aadSBjoern A. Zeeb mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); 215*a4128aadSBjoern A. Zeeb if (!mvmsta->mpdu_counters) 216*a4128aadSBjoern A. Zeeb return; 217*a4128aadSBjoern A. Zeeb 218*a4128aadSBjoern A. Zeeb for (int q = 0; q < mvm->trans->num_rx_queues; q++) { 219*a4128aadSBjoern A. Zeeb spin_lock_bh(&mvmsta->mpdu_counters[q].lock); 220*a4128aadSBjoern A. Zeeb memset(mvmsta->mpdu_counters[q].per_link, 0, 221*a4128aadSBjoern A. Zeeb sizeof(mvmsta->mpdu_counters[q].per_link)); 222*a4128aadSBjoern A. Zeeb mvmsta->mpdu_counters[q].window_start = jiffies; 223*a4128aadSBjoern A. Zeeb spin_unlock_bh(&mvmsta->mpdu_counters[q].lock); 224*a4128aadSBjoern A. Zeeb } 225*a4128aadSBjoern A. Zeeb 226*a4128aadSBjoern A. Zeeb IWL_DEBUG_STATS(mvm, "MPDU counters are cleared\n"); 227*a4128aadSBjoern A. Zeeb } 228*a4128aadSBjoern A. Zeeb 2299af1bba4SBjoern A. Zeeb static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, 2309af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 2319af1bba4SBjoern A. Zeeb { 2329af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 2339af1bba4SBjoern A. Zeeb int link_id, ret = 0; 2349af1bba4SBjoern A. Zeeb 2359af1bba4SBjoern A. Zeeb mvmvif->esr_active = true; 2369af1bba4SBjoern A. Zeeb 237*a4128aadSBjoern A. Zeeb /* Indicate to mac80211 that EML is enabled */ 238*a4128aadSBjoern A. Zeeb vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE; 2399af1bba4SBjoern A. Zeeb 2409af1bba4SBjoern A. Zeeb iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW, 2419af1bba4SBjoern A. Zeeb IEEE80211_SMPS_OFF); 2429af1bba4SBjoern A. Zeeb 2439af1bba4SBjoern A. Zeeb for_each_mvm_vif_valid_link(mvmvif, link_id) { 2449af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *link = mvmvif->link[link_id]; 2459af1bba4SBjoern A. Zeeb 2469af1bba4SBjoern A. Zeeb if (!link->phy_ctxt) 2479af1bba4SBjoern A. Zeeb continue; 2489af1bba4SBjoern A. Zeeb 2499af1bba4SBjoern A. Zeeb ret = iwl_mvm_phy_send_rlc(mvm, link->phy_ctxt, 2, 2); 2509af1bba4SBjoern A. Zeeb if (ret) 2519af1bba4SBjoern A. Zeeb break; 2529af1bba4SBjoern A. Zeeb 2539af1bba4SBjoern A. Zeeb link->phy_ctxt->rlc_disabled = true; 2549af1bba4SBjoern A. Zeeb } 2559af1bba4SBjoern A. Zeeb 256*a4128aadSBjoern A. Zeeb if (vif->active_links == mvmvif->link_selection_res && 257*a4128aadSBjoern A. Zeeb !WARN_ON(!(vif->active_links & BIT(mvmvif->link_selection_primary)))) 258*a4128aadSBjoern A. Zeeb mvmvif->primary_link = mvmvif->link_selection_primary; 259*a4128aadSBjoern A. Zeeb else 260*a4128aadSBjoern A. Zeeb mvmvif->primary_link = __ffs(vif->active_links); 261*a4128aadSBjoern A. Zeeb 262*a4128aadSBjoern A. Zeeb /* Needed for tracking RSSI */ 263*a4128aadSBjoern A. Zeeb iwl_mvm_request_periodic_system_statistics(mvm, true); 264*a4128aadSBjoern A. Zeeb 265*a4128aadSBjoern A. Zeeb /* 266*a4128aadSBjoern A. Zeeb * Restart the MPDU counters and the counting window, so when the 267*a4128aadSBjoern A. Zeeb * statistics arrive (which is where we look at the counters) we 268*a4128aadSBjoern A. Zeeb * will be at the end of the window. 269*a4128aadSBjoern A. Zeeb */ 270*a4128aadSBjoern A. Zeeb iwl_mvm_restart_mpdu_count(mvm, mvmvif); 271*a4128aadSBjoern A. Zeeb 2729af1bba4SBjoern A. Zeeb return ret; 2739af1bba4SBjoern A. Zeeb } 2749af1bba4SBjoern A. Zeeb 2759af1bba4SBjoern A. Zeeb static int 2769af1bba4SBjoern A. Zeeb __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, 2779af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 2789af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 2799af1bba4SBjoern A. Zeeb struct ieee80211_chanctx_conf *ctx, 2809af1bba4SBjoern A. Zeeb bool switching_chanctx) 2819af1bba4SBjoern A. Zeeb { 2829af1bba4SBjoern A. Zeeb u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; 2839af1bba4SBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; 2849af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 285*a4128aadSBjoern A. Zeeb unsigned int n_active = iwl_mvm_mld_count_active_links(mvmvif); 2869af1bba4SBjoern A. Zeeb unsigned int link_id = link_conf->link_id; 2879af1bba4SBjoern A. Zeeb int ret; 2889af1bba4SBjoern A. Zeeb 2899af1bba4SBjoern A. Zeeb if (WARN_ON_ONCE(!mvmvif->link[link_id])) 2909af1bba4SBjoern A. Zeeb return -EINVAL; 2919af1bba4SBjoern A. Zeeb 292*a4128aadSBjoern A. Zeeb /* if the assigned one was not counted yet, count it now */ 293*a4128aadSBjoern A. Zeeb if (!mvmvif->link[link_id]->phy_ctxt) 294*a4128aadSBjoern A. Zeeb n_active++; 295*a4128aadSBjoern A. Zeeb 2969af1bba4SBjoern A. Zeeb /* mac parameters such as HE support can change at this stage 2979af1bba4SBjoern A. Zeeb * For sta, need first to configure correct state from drv_sta_state 2989af1bba4SBjoern A. Zeeb * and only after that update mac config. 2999af1bba4SBjoern A. Zeeb */ 3009af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP) { 3019af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); 3029af1bba4SBjoern A. Zeeb if (ret) { 3039af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); 3049af1bba4SBjoern A. Zeeb return -EINVAL; 3059af1bba4SBjoern A. Zeeb } 3069af1bba4SBjoern A. Zeeb } 3079af1bba4SBjoern A. Zeeb 308*a4128aadSBjoern A. Zeeb mvmvif->link[link_id]->phy_ctxt = phy_ctxt; 309*a4128aadSBjoern A. Zeeb 3109af1bba4SBjoern A. Zeeb if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) { 3119af1bba4SBjoern A. Zeeb mvmvif->link[link_id]->listen_lmac = true; 3129af1bba4SBjoern A. Zeeb ret = iwl_mvm_esr_mode_active(mvm, vif); 3139af1bba4SBjoern A. Zeeb if (ret) { 3149af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to activate ESR mode (%d)\n", ret); 315*a4128aadSBjoern A. Zeeb iwl_mvm_request_periodic_system_statistics(mvm, false); 316*a4128aadSBjoern A. Zeeb goto out; 3179af1bba4SBjoern A. Zeeb } 3189af1bba4SBjoern A. Zeeb } 3199af1bba4SBjoern A. Zeeb 3209af1bba4SBjoern A. Zeeb if (switching_chanctx) { 3219af1bba4SBjoern A. Zeeb /* reactivate if we turned this off during channel switch */ 3229af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP) 3239af1bba4SBjoern A. Zeeb mvmvif->ap_ibss_active = true; 3249af1bba4SBjoern A. Zeeb } 3259af1bba4SBjoern A. Zeeb 3269af1bba4SBjoern A. Zeeb /* send it first with phy context ID */ 3279af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, link_conf, 0, false); 3289af1bba4SBjoern A. Zeeb if (ret) 3299af1bba4SBjoern A. Zeeb goto out; 3309af1bba4SBjoern A. Zeeb 3319af1bba4SBjoern A. Zeeb /* Initialize rate control for the AP station, since we might be 3329af1bba4SBjoern A. Zeeb * doing a link switch here - we cannot initialize it before since 3339af1bba4SBjoern A. Zeeb * this needs the phy context assigned (and in FW?), and we cannot 3349af1bba4SBjoern A. Zeeb * do it later because it needs to be initialized as soon as we're 3359af1bba4SBjoern A. Zeeb * able to TX on the link, i.e. when active. 3369af1bba4SBjoern A. Zeeb */ 337*a4128aadSBjoern A. Zeeb if (mvmvif->ap_sta) { 3389af1bba4SBjoern A. Zeeb struct ieee80211_link_sta *link_sta; 3399af1bba4SBjoern A. Zeeb 3409af1bba4SBjoern A. Zeeb rcu_read_lock(); 3419af1bba4SBjoern A. Zeeb link_sta = rcu_dereference(mvmvif->ap_sta->link[link_id]); 3429af1bba4SBjoern A. Zeeb 3439af1bba4SBjoern A. Zeeb if (!WARN_ON_ONCE(!link_sta)) 3449af1bba4SBjoern A. Zeeb iwl_mvm_rs_rate_init(mvm, vif, mvmvif->ap_sta, 3459af1bba4SBjoern A. Zeeb link_conf, link_sta, 3469af1bba4SBjoern A. Zeeb phy_ctxt->channel->band); 3479af1bba4SBjoern A. Zeeb rcu_read_unlock(); 3489af1bba4SBjoern A. Zeeb } 3499af1bba4SBjoern A. Zeeb 350*a4128aadSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_STATION) 351*a4128aadSBjoern A. Zeeb iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif, 352*a4128aadSBjoern A. Zeeb link_conf, 353*a4128aadSBjoern A. Zeeb false); 354*a4128aadSBjoern A. Zeeb 3559af1bba4SBjoern A. Zeeb /* then activate */ 3569af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, link_conf, 3579af1bba4SBjoern A. Zeeb LINK_CONTEXT_MODIFY_ACTIVE | 3589af1bba4SBjoern A. Zeeb LINK_CONTEXT_MODIFY_RATES_INFO, 3599af1bba4SBjoern A. Zeeb true); 3609af1bba4SBjoern A. Zeeb if (ret) 3619af1bba4SBjoern A. Zeeb goto out; 3629af1bba4SBjoern A. Zeeb 3639af1bba4SBjoern A. Zeeb /* 3649af1bba4SBjoern A. Zeeb * Power state must be updated before quotas, 3659af1bba4SBjoern A. Zeeb * otherwise fw will complain. 3669af1bba4SBjoern A. Zeeb */ 3679af1bba4SBjoern A. Zeeb iwl_mvm_power_update_mac(mvm); 3689af1bba4SBjoern A. Zeeb 3699af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_MONITOR) { 3709af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_add_snif_sta(mvm, vif, link_conf); 3719af1bba4SBjoern A. Zeeb if (ret) 3729af1bba4SBjoern A. Zeeb goto deactivate; 3739af1bba4SBjoern A. Zeeb } 3749af1bba4SBjoern A. Zeeb 3759af1bba4SBjoern A. Zeeb return 0; 3769af1bba4SBjoern A. Zeeb 3779af1bba4SBjoern A. Zeeb deactivate: 3789af1bba4SBjoern A. Zeeb iwl_mvm_link_changed(mvm, vif, link_conf, LINK_CONTEXT_MODIFY_ACTIVE, 3799af1bba4SBjoern A. Zeeb false); 3809af1bba4SBjoern A. Zeeb out: 3819af1bba4SBjoern A. Zeeb mvmvif->link[link_id]->phy_ctxt = NULL; 3829af1bba4SBjoern A. Zeeb iwl_mvm_power_update_mac(mvm); 3839af1bba4SBjoern A. Zeeb return ret; 3849af1bba4SBjoern A. Zeeb } 3859af1bba4SBjoern A. Zeeb 3869af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw, 3879af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 3889af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 3899af1bba4SBjoern A. Zeeb struct ieee80211_chanctx_conf *ctx) 3909af1bba4SBjoern A. Zeeb { 3919af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 392*a4128aadSBjoern A. Zeeb 393*a4128aadSBjoern A. Zeeb /* update EMLSR mode */ 394*a4128aadSBjoern A. Zeeb if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { 3959af1bba4SBjoern A. Zeeb int ret; 3969af1bba4SBjoern A. Zeeb 397*a4128aadSBjoern A. Zeeb ret = iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, 398*a4128aadSBjoern A. Zeeb true); 399*a4128aadSBjoern A. Zeeb /* 400*a4128aadSBjoern A. Zeeb * Don't activate this link if failed to exit EMLSR in 401*a4128aadSBjoern A. Zeeb * the BSS interface 402*a4128aadSBjoern A. Zeeb */ 403*a4128aadSBjoern A. Zeeb if (ret) 4049af1bba4SBjoern A. Zeeb return ret; 4059af1bba4SBjoern A. Zeeb } 4069af1bba4SBjoern A. Zeeb 407*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 408*a4128aadSBjoern A. Zeeb return __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false); 409*a4128aadSBjoern A. Zeeb } 410*a4128aadSBjoern A. Zeeb 4119af1bba4SBjoern A. Zeeb static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm, 4129af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 4139af1bba4SBjoern A. Zeeb { 4149af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 4159af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf; 4169af1bba4SBjoern A. Zeeb int link_id, ret = 0; 4179af1bba4SBjoern A. Zeeb 4189af1bba4SBjoern A. Zeeb mvmvif->esr_active = false; 4199af1bba4SBjoern A. Zeeb 420*a4128aadSBjoern A. Zeeb vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; 4219af1bba4SBjoern A. Zeeb 4229af1bba4SBjoern A. Zeeb iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW, 4239af1bba4SBjoern A. Zeeb IEEE80211_SMPS_AUTOMATIC); 4249af1bba4SBjoern A. Zeeb 4259af1bba4SBjoern A. Zeeb for_each_vif_active_link(vif, link_conf, link_id) { 4269af1bba4SBjoern A. Zeeb struct ieee80211_chanctx_conf *chanctx_conf; 4279af1bba4SBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phy_ctxt; 4289af1bba4SBjoern A. Zeeb u8 static_chains, dynamic_chains; 4299af1bba4SBjoern A. Zeeb 4309af1bba4SBjoern A. Zeeb mvmvif->link[link_id]->listen_lmac = false; 4319af1bba4SBjoern A. Zeeb 4329af1bba4SBjoern A. Zeeb rcu_read_lock(); 4339af1bba4SBjoern A. Zeeb 4349af1bba4SBjoern A. Zeeb chanctx_conf = rcu_dereference(link_conf->chanctx_conf); 4359af1bba4SBjoern A. Zeeb phy_ctxt = mvmvif->link[link_id]->phy_ctxt; 4369af1bba4SBjoern A. Zeeb 4379af1bba4SBjoern A. Zeeb if (!chanctx_conf || !phy_ctxt) { 4389af1bba4SBjoern A. Zeeb rcu_read_unlock(); 4399af1bba4SBjoern A. Zeeb continue; 4409af1bba4SBjoern A. Zeeb } 4419af1bba4SBjoern A. Zeeb 4429af1bba4SBjoern A. Zeeb phy_ctxt->rlc_disabled = false; 4439af1bba4SBjoern A. Zeeb static_chains = chanctx_conf->rx_chains_static; 4449af1bba4SBjoern A. Zeeb dynamic_chains = chanctx_conf->rx_chains_dynamic; 4459af1bba4SBjoern A. Zeeb 4469af1bba4SBjoern A. Zeeb rcu_read_unlock(); 4479af1bba4SBjoern A. Zeeb 4489af1bba4SBjoern A. Zeeb ret = iwl_mvm_phy_send_rlc(mvm, phy_ctxt, static_chains, 4499af1bba4SBjoern A. Zeeb dynamic_chains); 4509af1bba4SBjoern A. Zeeb if (ret) 4519af1bba4SBjoern A. Zeeb break; 4529af1bba4SBjoern A. Zeeb } 4539af1bba4SBjoern A. Zeeb 454*a4128aadSBjoern A. Zeeb iwl_mvm_request_periodic_system_statistics(mvm, false); 455*a4128aadSBjoern A. Zeeb 456*a4128aadSBjoern A. Zeeb /* Start a new counting window */ 457*a4128aadSBjoern A. Zeeb iwl_mvm_restart_mpdu_count(mvm, mvmvif); 458*a4128aadSBjoern A. Zeeb 4599af1bba4SBjoern A. Zeeb return ret; 4609af1bba4SBjoern A. Zeeb } 4619af1bba4SBjoern A. Zeeb 4629af1bba4SBjoern A. Zeeb static void 4639af1bba4SBjoern A. Zeeb __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, 4649af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 4659af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 4669af1bba4SBjoern A. Zeeb struct ieee80211_chanctx_conf *ctx, 4679af1bba4SBjoern A. Zeeb bool switching_chanctx) 4689af1bba4SBjoern A. Zeeb 4699af1bba4SBjoern A. Zeeb { 4709af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 471*a4128aadSBjoern A. Zeeb unsigned int n_active = iwl_mvm_mld_count_active_links(mvmvif); 4729af1bba4SBjoern A. Zeeb unsigned int link_id = link_conf->link_id; 4739af1bba4SBjoern A. Zeeb 4749af1bba4SBjoern A. Zeeb /* shouldn't happen, but verify link_id is valid before accessing */ 4759af1bba4SBjoern A. Zeeb if (WARN_ON_ONCE(!mvmvif->link[link_id])) 4769af1bba4SBjoern A. Zeeb return; 4779af1bba4SBjoern A. Zeeb 4789af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP && switching_chanctx) { 4799af1bba4SBjoern A. Zeeb mvmvif->csa_countdown = false; 4809af1bba4SBjoern A. Zeeb 4819af1bba4SBjoern A. Zeeb /* Set CS bit on all the stations */ 4829af1bba4SBjoern A. Zeeb iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); 4839af1bba4SBjoern A. Zeeb 4849af1bba4SBjoern A. Zeeb /* Save blocked iface, the timeout is set on the next beacon */ 4859af1bba4SBjoern A. Zeeb rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); 4869af1bba4SBjoern A. Zeeb 4879af1bba4SBjoern A. Zeeb mvmvif->ap_ibss_active = false; 4889af1bba4SBjoern A. Zeeb } 4899af1bba4SBjoern A. Zeeb 490*a4128aadSBjoern A. Zeeb iwl_mvm_link_changed(mvm, vif, link_conf, 491*a4128aadSBjoern A. Zeeb LINK_CONTEXT_MODIFY_ACTIVE, false); 492*a4128aadSBjoern A. Zeeb 4939af1bba4SBjoern A. Zeeb if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) { 4949af1bba4SBjoern A. Zeeb int ret = iwl_mvm_esr_mode_inactive(mvm, vif); 4959af1bba4SBjoern A. Zeeb 4969af1bba4SBjoern A. Zeeb if (ret) 4979af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to deactivate ESR mode (%d)\n", 4989af1bba4SBjoern A. Zeeb ret); 4999af1bba4SBjoern A. Zeeb } 5009af1bba4SBjoern A. Zeeb 5019af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_MONITOR) 5029af1bba4SBjoern A. Zeeb iwl_mvm_mld_rm_snif_sta(mvm, vif); 5039af1bba4SBjoern A. Zeeb 5049af1bba4SBjoern A. Zeeb if (switching_chanctx) 5059af1bba4SBjoern A. Zeeb return; 5069af1bba4SBjoern A. Zeeb mvmvif->link[link_id]->phy_ctxt = NULL; 5079af1bba4SBjoern A. Zeeb iwl_mvm_power_update_mac(mvm); 5089af1bba4SBjoern A. Zeeb } 5099af1bba4SBjoern A. Zeeb 5109af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, 5119af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 5129af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 5139af1bba4SBjoern A. Zeeb struct ieee80211_chanctx_conf *ctx) 5149af1bba4SBjoern A. Zeeb { 515*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 5169af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 5179af1bba4SBjoern A. Zeeb 5189af1bba4SBjoern A. Zeeb mutex_lock(&mvm->mutex); 5199af1bba4SBjoern A. Zeeb __iwl_mvm_mld_unassign_vif_chanctx(mvm, vif, link_conf, ctx, false); 520*a4128aadSBjoern A. Zeeb /* in the non-MLD case, remove/re-add the link to clean up FW state */ 521*a4128aadSBjoern A. Zeeb if (!ieee80211_vif_is_mld(vif) && !mvmvif->ap_sta && 522*a4128aadSBjoern A. Zeeb !WARN_ON_ONCE(vif->cfg.assoc)) { 523*a4128aadSBjoern A. Zeeb iwl_mvm_remove_link(mvm, vif, link_conf); 524*a4128aadSBjoern A. Zeeb iwl_mvm_add_link(mvm, vif, link_conf); 525*a4128aadSBjoern A. Zeeb } 5269af1bba4SBjoern A. Zeeb mutex_unlock(&mvm->mutex); 527*a4128aadSBjoern A. Zeeb 528*a4128aadSBjoern A. Zeeb /* update EMLSR mode */ 529*a4128aadSBjoern A. Zeeb if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) 530*a4128aadSBjoern A. Zeeb iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, false); 531*a4128aadSBjoern A. Zeeb } 532*a4128aadSBjoern A. Zeeb 533*a4128aadSBjoern A. Zeeb static void 534*a4128aadSBjoern A. Zeeb iwl_mvm_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd, 535*a4128aadSBjoern A. Zeeb const struct ieee80211_bss_conf *bss_info) 536*a4128aadSBjoern A. Zeeb { 537*a4128aadSBjoern A. Zeeb u8 i; 538*a4128aadSBjoern A. Zeeb 539*a4128aadSBjoern A. Zeeb /* 540*a4128aadSBjoern A. Zeeb * NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT, 541*a4128aadSBjoern A. Zeeb * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE 542*a4128aadSBjoern A. Zeeb */ 543*a4128aadSBjoern A. Zeeb 544*a4128aadSBjoern A. Zeeb BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) != 545*a4128aadSBjoern A. Zeeb ARRAY_SIZE(bss_info->tpe.psd_local[0].power)); 546*a4128aadSBjoern A. Zeeb 547*a4128aadSBjoern A. Zeeb /* if not valid, mac80211 puts default (max value) */ 548*a4128aadSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++) 549*a4128aadSBjoern A. Zeeb cmd->psd_pwr[i] = min(bss_info->tpe.psd_local[0].power[i], 550*a4128aadSBjoern A. Zeeb bss_info->tpe.psd_reg_client[0].power[i]); 551*a4128aadSBjoern A. Zeeb 552*a4128aadSBjoern A. Zeeb BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) != 553*a4128aadSBjoern A. Zeeb ARRAY_SIZE(bss_info->tpe.max_local[0].power)); 554*a4128aadSBjoern A. Zeeb 555*a4128aadSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++) 556*a4128aadSBjoern A. Zeeb cmd->eirp_pwr[i] = min(bss_info->tpe.max_local[0].power[i], 557*a4128aadSBjoern A. Zeeb bss_info->tpe.max_reg_client[0].power[i]); 558*a4128aadSBjoern A. Zeeb } 559*a4128aadSBjoern A. Zeeb 560*a4128aadSBjoern A. Zeeb void 561*a4128aadSBjoern A. Zeeb iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm, 562*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 563*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *bss_conf, 564*a4128aadSBjoern A. Zeeb bool is_ap) 565*a4128aadSBjoern A. Zeeb { 566*a4128aadSBjoern A. Zeeb struct iwl_txpower_constraints_cmd cmd = {}; 567*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 568*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info = 569*a4128aadSBjoern A. Zeeb mvmvif->link[bss_conf->link_id]; 570*a4128aadSBjoern A. Zeeb u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, AP_TX_POWER_CONSTRAINTS_CMD); 571*a4128aadSBjoern A. Zeeb u32 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 572*a4128aadSBjoern A. Zeeb IWL_FW_CMD_VER_UNKNOWN); 573*a4128aadSBjoern A. Zeeb int ret; 574*a4128aadSBjoern A. Zeeb 575*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 576*a4128aadSBjoern A. Zeeb 577*a4128aadSBjoern A. Zeeb if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) 578*a4128aadSBjoern A. Zeeb return; 579*a4128aadSBjoern A. Zeeb 580*a4128aadSBjoern A. Zeeb if (!link_info->active || 581*a4128aadSBjoern A. Zeeb link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) 582*a4128aadSBjoern A. Zeeb return; 583*a4128aadSBjoern A. Zeeb 584*a4128aadSBjoern A. Zeeb if (bss_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ) 585*a4128aadSBjoern A. Zeeb return; 586*a4128aadSBjoern A. Zeeb 587*a4128aadSBjoern A. Zeeb cmd.link_id = cpu_to_le16(link_info->fw_link_id); 588*a4128aadSBjoern A. Zeeb memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr)); 589*a4128aadSBjoern A. Zeeb memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr)); 590*a4128aadSBjoern A. Zeeb 591*a4128aadSBjoern A. Zeeb if (is_ap) { 592*a4128aadSBjoern A. Zeeb cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP); 593*a4128aadSBjoern A. Zeeb } else if (bss_conf->power_type == IEEE80211_REG_UNSET_AP) { 594*a4128aadSBjoern A. Zeeb return; 595*a4128aadSBjoern A. Zeeb } else { 596*a4128aadSBjoern A. Zeeb cmd.ap_type = cpu_to_le16(bss_conf->power_type - 1); 597*a4128aadSBjoern A. Zeeb iwl_mvm_tpe_sta_cmd_data(&cmd, bss_conf); 598*a4128aadSBjoern A. Zeeb } 599*a4128aadSBjoern A. Zeeb 600*a4128aadSBjoern A. Zeeb ret = iwl_mvm_send_cmd_pdu(mvm, 601*a4128aadSBjoern A. Zeeb WIDE_ID(PHY_OPS_GROUP, 602*a4128aadSBjoern A. Zeeb AP_TX_POWER_CONSTRAINTS_CMD), 603*a4128aadSBjoern A. Zeeb 0, sizeof(cmd), &cmd); 604*a4128aadSBjoern A. Zeeb if (ret) 605*a4128aadSBjoern A. Zeeb IWL_ERR(mvm, 606*a4128aadSBjoern A. Zeeb "failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n", 607*a4128aadSBjoern A. Zeeb ret); 6089af1bba4SBjoern A. Zeeb } 6099af1bba4SBjoern A. Zeeb 6109af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, 6119af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 6129af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 6139af1bba4SBjoern A. Zeeb { 6149af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 6159af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 6169af1bba4SBjoern A. Zeeb int ret; 6179af1bba4SBjoern A. Zeeb 618*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 619*a4128aadSBjoern A. Zeeb 620*a4128aadSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP) 621*a4128aadSBjoern A. Zeeb iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif, 622*a4128aadSBjoern A. Zeeb link_conf, true); 623*a4128aadSBjoern A. Zeeb 6249af1bba4SBjoern A. Zeeb /* Send the beacon template */ 6259af1bba4SBjoern A. Zeeb ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf); 6269af1bba4SBjoern A. Zeeb if (ret) 627*a4128aadSBjoern A. Zeeb return ret; 6289af1bba4SBjoern A. Zeeb 6299af1bba4SBjoern A. Zeeb /* the link should be already activated when assigning chan context */ 6309af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, link_conf, 6319af1bba4SBjoern A. Zeeb LINK_CONTEXT_MODIFY_ALL & 6329af1bba4SBjoern A. Zeeb ~LINK_CONTEXT_MODIFY_ACTIVE, 6339af1bba4SBjoern A. Zeeb true); 6349af1bba4SBjoern A. Zeeb if (ret) 635*a4128aadSBjoern A. Zeeb return ret; 6369af1bba4SBjoern A. Zeeb 6379af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_add_mcast_sta(mvm, vif, link_conf); 6389af1bba4SBjoern A. Zeeb if (ret) 639*a4128aadSBjoern A. Zeeb return ret; 6409af1bba4SBjoern A. Zeeb 6419af1bba4SBjoern A. Zeeb /* Send the bcast station. At this stage the TBTT and DTIM time 6429af1bba4SBjoern A. Zeeb * events are added and applied to the scheduler 6439af1bba4SBjoern A. Zeeb */ 6449af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_add_bcast_sta(mvm, vif, link_conf); 6459af1bba4SBjoern A. Zeeb if (ret) 6469af1bba4SBjoern A. Zeeb goto out_rm_mcast; 6479af1bba4SBjoern A. Zeeb 6489af1bba4SBjoern A. Zeeb if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret)) 6499af1bba4SBjoern A. Zeeb goto out_failed; 6509af1bba4SBjoern A. Zeeb 6519af1bba4SBjoern A. Zeeb /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ 6529af1bba4SBjoern A. Zeeb if (vif->p2p && mvm->p2p_device_vif) 6539af1bba4SBjoern A. Zeeb iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); 6549af1bba4SBjoern A. Zeeb 6559af1bba4SBjoern A. Zeeb iwl_mvm_bt_coex_vif_change(mvm); 6569af1bba4SBjoern A. Zeeb 6579af1bba4SBjoern A. Zeeb /* we don't support TDLS during DCM */ 6589af1bba4SBjoern A. Zeeb if (iwl_mvm_phy_ctx_count(mvm) > 1) 6599af1bba4SBjoern A. Zeeb iwl_mvm_teardown_tdls_peers(mvm); 6609af1bba4SBjoern A. Zeeb 6619af1bba4SBjoern A. Zeeb iwl_mvm_ftm_restart_responder(mvm, vif, link_conf); 6629af1bba4SBjoern A. Zeeb 663*a4128aadSBjoern A. Zeeb return 0; 6649af1bba4SBjoern A. Zeeb 6659af1bba4SBjoern A. Zeeb out_failed: 6669af1bba4SBjoern A. Zeeb iwl_mvm_power_update_mac(mvm); 6679af1bba4SBjoern A. Zeeb mvmvif->ap_ibss_active = false; 6689af1bba4SBjoern A. Zeeb iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); 6699af1bba4SBjoern A. Zeeb out_rm_mcast: 6709af1bba4SBjoern A. Zeeb iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); 6719af1bba4SBjoern A. Zeeb return ret; 6729af1bba4SBjoern A. Zeeb } 6739af1bba4SBjoern A. Zeeb 6749af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_start_ap(struct ieee80211_hw *hw, 6759af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 6769af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 6779af1bba4SBjoern A. Zeeb { 6789af1bba4SBjoern A. Zeeb return iwl_mvm_mld_start_ap_ibss(hw, vif, link_conf); 6799af1bba4SBjoern A. Zeeb } 6809af1bba4SBjoern A. Zeeb 6819af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_start_ibss(struct ieee80211_hw *hw, 6829af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 6839af1bba4SBjoern A. Zeeb { 6849af1bba4SBjoern A. Zeeb return iwl_mvm_mld_start_ap_ibss(hw, vif, &vif->bss_conf); 6859af1bba4SBjoern A. Zeeb } 6869af1bba4SBjoern A. Zeeb 6879af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw, 6889af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 6899af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 6909af1bba4SBjoern A. Zeeb { 6919af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 6929af1bba4SBjoern A. Zeeb 693*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 6949af1bba4SBjoern A. Zeeb 6959af1bba4SBjoern A. Zeeb iwl_mvm_stop_ap_ibss_common(mvm, vif); 6969af1bba4SBjoern A. Zeeb 6979af1bba4SBjoern A. Zeeb /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ 6989af1bba4SBjoern A. Zeeb if (vif->p2p && mvm->p2p_device_vif) 6999af1bba4SBjoern A. Zeeb iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); 7009af1bba4SBjoern A. Zeeb 7019af1bba4SBjoern A. Zeeb iwl_mvm_ftm_responder_clear(mvm, vif); 7029af1bba4SBjoern A. Zeeb 7039af1bba4SBjoern A. Zeeb iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); 7049af1bba4SBjoern A. Zeeb iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); 7059af1bba4SBjoern A. Zeeb 7069af1bba4SBjoern A. Zeeb iwl_mvm_power_update_mac(mvm); 7079af1bba4SBjoern A. Zeeb } 7089af1bba4SBjoern A. Zeeb 7099af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_stop_ap(struct ieee80211_hw *hw, 7109af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 7119af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 7129af1bba4SBjoern A. Zeeb { 7139af1bba4SBjoern A. Zeeb iwl_mvm_mld_stop_ap_ibss(hw, vif, link_conf); 7149af1bba4SBjoern A. Zeeb } 7159af1bba4SBjoern A. Zeeb 7169af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_stop_ibss(struct ieee80211_hw *hw, 7179af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 7189af1bba4SBjoern A. Zeeb { 7199af1bba4SBjoern A. Zeeb iwl_mvm_mld_stop_ap_ibss(hw, vif, &vif->bss_conf); 7209af1bba4SBjoern A. Zeeb } 7219af1bba4SBjoern A. Zeeb 7229af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, 7239af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 7249af1bba4SBjoern A. Zeeb struct ieee80211_sta *sta, 7259af1bba4SBjoern A. Zeeb enum ieee80211_sta_state old_state, 7269af1bba4SBjoern A. Zeeb enum ieee80211_sta_state new_state) 7279af1bba4SBjoern A. Zeeb { 7289af1bba4SBjoern A. Zeeb static const struct iwl_mvm_sta_state_ops callbacks = { 7299af1bba4SBjoern A. Zeeb .add_sta = iwl_mvm_mld_add_sta, 7309af1bba4SBjoern A. Zeeb .update_sta = iwl_mvm_mld_update_sta, 7319af1bba4SBjoern A. Zeeb .rm_sta = iwl_mvm_mld_rm_sta, 7329af1bba4SBjoern A. Zeeb .mac_ctxt_changed = iwl_mvm_mld_mac_ctxt_changed, 7339af1bba4SBjoern A. Zeeb }; 7349af1bba4SBjoern A. Zeeb 7359af1bba4SBjoern A. Zeeb return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state, 7369af1bba4SBjoern A. Zeeb &callbacks); 7379af1bba4SBjoern A. Zeeb } 7389af1bba4SBjoern A. Zeeb 739*a4128aadSBjoern A. Zeeb static bool iwl_mvm_esr_bw_criteria(struct iwl_mvm *mvm, 740*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 741*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *link_conf) 742*a4128aadSBjoern A. Zeeb { 743*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *other_link; 744*a4128aadSBjoern A. Zeeb int link_id; 745*a4128aadSBjoern A. Zeeb 746*a4128aadSBjoern A. Zeeb /* Exit EMLSR if links don't have equal bandwidths */ 747*a4128aadSBjoern A. Zeeb for_each_vif_active_link(vif, other_link, link_id) { 748*a4128aadSBjoern A. Zeeb if (link_id == link_conf->link_id) 749*a4128aadSBjoern A. Zeeb continue; 750*a4128aadSBjoern A. Zeeb if (link_conf->chanreq.oper.width == 751*a4128aadSBjoern A. Zeeb other_link->chanreq.oper.width) 752*a4128aadSBjoern A. Zeeb return true; 753*a4128aadSBjoern A. Zeeb } 754*a4128aadSBjoern A. Zeeb 755*a4128aadSBjoern A. Zeeb return false; 756*a4128aadSBjoern A. Zeeb } 757*a4128aadSBjoern A. Zeeb 7589af1bba4SBjoern A. Zeeb static void 7599af1bba4SBjoern A. Zeeb iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, 7609af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 7619af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 7629af1bba4SBjoern A. Zeeb u64 changes) 7639af1bba4SBjoern A. Zeeb { 7649af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 7659af1bba4SBjoern A. Zeeb bool has_he, has_eht; 7669af1bba4SBjoern A. Zeeb u32 link_changes = 0; 7679af1bba4SBjoern A. Zeeb int ret; 7689af1bba4SBjoern A. Zeeb 7699af1bba4SBjoern A. Zeeb if (WARN_ON_ONCE(!mvmvif->link[link_conf->link_id])) 7709af1bba4SBjoern A. Zeeb return; 7719af1bba4SBjoern A. Zeeb 7729af1bba4SBjoern A. Zeeb has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; 7739af1bba4SBjoern A. Zeeb has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; 7749af1bba4SBjoern A. Zeeb 7759af1bba4SBjoern A. Zeeb /* Update EDCA params */ 7769af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos) 7779af1bba4SBjoern A. Zeeb link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; 7789af1bba4SBjoern A. Zeeb 7799af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_ERP_SLOT) 7809af1bba4SBjoern A. Zeeb link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; 7819af1bba4SBjoern A. Zeeb 7829af1bba4SBjoern A. Zeeb if (vif->cfg.assoc && (has_he || has_eht)) { 7839af1bba4SBjoern A. Zeeb IWL_DEBUG_MAC80211(mvm, "Associated in HE mode\n"); 7849af1bba4SBjoern A. Zeeb link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; 7859af1bba4SBjoern A. Zeeb } 7869af1bba4SBjoern A. Zeeb 787*a4128aadSBjoern A. Zeeb if ((changes & BSS_CHANGED_BANDWIDTH) && 788*a4128aadSBjoern A. Zeeb ieee80211_vif_link_active(vif, link_conf->link_id) && 789*a4128aadSBjoern A. Zeeb mvmvif->esr_active && 790*a4128aadSBjoern A. Zeeb !iwl_mvm_esr_bw_criteria(mvm, vif, link_conf)) 791*a4128aadSBjoern A. Zeeb iwl_mvm_exit_esr(mvm, vif, 792*a4128aadSBjoern A. Zeeb IWL_MVM_ESR_EXIT_BANDWIDTH, 793*a4128aadSBjoern A. Zeeb iwl_mvm_get_primary_link(vif)); 794*a4128aadSBjoern A. Zeeb 795*a4128aadSBjoern A. Zeeb /* if associated, maybe puncturing changed - we'll check later */ 796*a4128aadSBjoern A. Zeeb if (vif->cfg.assoc) 7979af1bba4SBjoern A. Zeeb link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; 7989af1bba4SBjoern A. Zeeb 7999af1bba4SBjoern A. Zeeb if (link_changes) { 8009af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, link_conf, link_changes, 8019af1bba4SBjoern A. Zeeb true); 8029af1bba4SBjoern A. Zeeb if (ret) 8039af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to update link\n"); 8049af1bba4SBjoern A. Zeeb } 8059af1bba4SBjoern A. Zeeb 8069af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); 8079af1bba4SBjoern A. Zeeb if (ret) 8089af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); 8099af1bba4SBjoern A. Zeeb 8109af1bba4SBjoern A. Zeeb memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid, 8119af1bba4SBjoern A. Zeeb ETH_ALEN); 8129af1bba4SBjoern A. Zeeb 8139af1bba4SBjoern A. Zeeb iwl_mvm_bss_info_changed_station_common(mvm, vif, link_conf, changes); 8149af1bba4SBjoern A. Zeeb } 8159af1bba4SBjoern A. Zeeb 8169af1bba4SBjoern A. Zeeb static bool iwl_mvm_mld_vif_have_valid_ap_sta(struct iwl_mvm_vif *mvmvif) 8179af1bba4SBjoern A. Zeeb { 8189af1bba4SBjoern A. Zeeb int i; 8199af1bba4SBjoern A. Zeeb 8209af1bba4SBjoern A. Zeeb for_each_mvm_vif_valid_link(mvmvif, i) { 8219af1bba4SBjoern A. Zeeb if (mvmvif->link[i]->ap_sta_id != IWL_MVM_INVALID_STA) 8229af1bba4SBjoern A. Zeeb return true; 8239af1bba4SBjoern A. Zeeb } 8249af1bba4SBjoern A. Zeeb 8259af1bba4SBjoern A. Zeeb return false; 8269af1bba4SBjoern A. Zeeb } 8279af1bba4SBjoern A. Zeeb 8289af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_vif_delete_all_stas(struct iwl_mvm *mvm, 8299af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 8309af1bba4SBjoern A. Zeeb { 8319af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 8329af1bba4SBjoern A. Zeeb int i, ret; 8339af1bba4SBjoern A. Zeeb 8349af1bba4SBjoern A. Zeeb if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 8359af1bba4SBjoern A. Zeeb return; 8369af1bba4SBjoern A. Zeeb 8379af1bba4SBjoern A. Zeeb for_each_mvm_vif_valid_link(mvmvif, i) { 8389af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *link = mvmvif->link[i]; 8399af1bba4SBjoern A. Zeeb 8409af1bba4SBjoern A. Zeeb if (!link) 8419af1bba4SBjoern A. Zeeb continue; 8429af1bba4SBjoern A. Zeeb 8439af1bba4SBjoern A. Zeeb iwl_mvm_sec_key_remove_ap(mvm, vif, link, i); 8449af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_rm_sta_id(mvm, link->ap_sta_id); 8459af1bba4SBjoern A. Zeeb if (ret) 8469af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to remove AP station\n"); 8479af1bba4SBjoern A. Zeeb 8489af1bba4SBjoern A. Zeeb link->ap_sta_id = IWL_MVM_INVALID_STA; 8499af1bba4SBjoern A. Zeeb } 8509af1bba4SBjoern A. Zeeb } 8519af1bba4SBjoern A. Zeeb 8529af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, 8539af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 8549af1bba4SBjoern A. Zeeb u64 changes) 8559af1bba4SBjoern A. Zeeb { 8569af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 8579af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf; 8589af1bba4SBjoern A. Zeeb bool protect = false; 8599af1bba4SBjoern A. Zeeb unsigned int i; 8609af1bba4SBjoern A. Zeeb int ret; 8619af1bba4SBjoern A. Zeeb 8629af1bba4SBjoern A. Zeeb /* This might get called without active links during the 8639af1bba4SBjoern A. Zeeb * chanctx switch, but we don't care about it anyway. 8649af1bba4SBjoern A. Zeeb */ 8659af1bba4SBjoern A. Zeeb if (changes == BSS_CHANGED_IDLE) 8669af1bba4SBjoern A. Zeeb return; 8679af1bba4SBjoern A. Zeeb 8689af1bba4SBjoern A. Zeeb ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); 8699af1bba4SBjoern A. Zeeb if (ret) 8709af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); 8719af1bba4SBjoern A. Zeeb 8729af1bba4SBjoern A. Zeeb mvmvif->associated = vif->cfg.assoc; 8739af1bba4SBjoern A. Zeeb 874*a4128aadSBjoern A. Zeeb if (changes & BSS_CHANGED_ASSOC) { 8759af1bba4SBjoern A. Zeeb if (vif->cfg.assoc) { 876*a4128aadSBjoern A. Zeeb mvmvif->session_prot_connection_loss = false; 877*a4128aadSBjoern A. Zeeb 8789af1bba4SBjoern A. Zeeb /* clear statistics to get clean beacon counter */ 8799af1bba4SBjoern A. Zeeb iwl_mvm_request_statistics(mvm, true); 8809af1bba4SBjoern A. Zeeb iwl_mvm_sf_update(mvm, vif, false); 8819af1bba4SBjoern A. Zeeb iwl_mvm_power_vif_assoc(mvm, vif); 8829af1bba4SBjoern A. Zeeb 8839af1bba4SBjoern A. Zeeb for_each_mvm_vif_valid_link(mvmvif, i) { 8849af1bba4SBjoern A. Zeeb memset(&mvmvif->link[i]->beacon_stats, 0, 8859af1bba4SBjoern A. Zeeb sizeof(mvmvif->link[i]->beacon_stats)); 8869af1bba4SBjoern A. Zeeb 8879af1bba4SBjoern A. Zeeb if (vif->p2p) { 8889af1bba4SBjoern A. Zeeb iwl_mvm_update_smps(mvm, vif, 8899af1bba4SBjoern A. Zeeb IWL_MVM_SMPS_REQ_PROT, 8909af1bba4SBjoern A. Zeeb IEEE80211_SMPS_DYNAMIC, i); 8919af1bba4SBjoern A. Zeeb } 8929af1bba4SBjoern A. Zeeb 8939af1bba4SBjoern A. Zeeb rcu_read_lock(); 8949af1bba4SBjoern A. Zeeb link_conf = rcu_dereference(vif->link_conf[i]); 8959af1bba4SBjoern A. Zeeb if (link_conf && !link_conf->dtim_period) 8969af1bba4SBjoern A. Zeeb protect = true; 8979af1bba4SBjoern A. Zeeb rcu_read_unlock(); 8989af1bba4SBjoern A. Zeeb } 8999af1bba4SBjoern A. Zeeb 9009af1bba4SBjoern A. Zeeb if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && 9019af1bba4SBjoern A. Zeeb protect) { 902*a4128aadSBjoern A. Zeeb /* We are in assoc so only one link is active- 903*a4128aadSBjoern A. Zeeb * The association link 904*a4128aadSBjoern A. Zeeb */ 905*a4128aadSBjoern A. Zeeb unsigned int link_id = 906*a4128aadSBjoern A. Zeeb ffs(vif->active_links) - 1; 907*a4128aadSBjoern A. Zeeb 9089af1bba4SBjoern A. Zeeb /* If we're not restarting and still haven't 9099af1bba4SBjoern A. Zeeb * heard a beacon (dtim period unknown) then 9109af1bba4SBjoern A. Zeeb * make sure we still have enough minimum time 9119af1bba4SBjoern A. Zeeb * remaining in the time event, since the auth 9129af1bba4SBjoern A. Zeeb * might actually have taken quite a while 9139af1bba4SBjoern A. Zeeb * (especially for SAE) and so the remaining 9149af1bba4SBjoern A. Zeeb * time could be small without us having heard 9159af1bba4SBjoern A. Zeeb * a beacon yet. 9169af1bba4SBjoern A. Zeeb */ 917*a4128aadSBjoern A. Zeeb iwl_mvm_protect_assoc(mvm, vif, 0, link_id); 9189af1bba4SBjoern A. Zeeb } 9199af1bba4SBjoern A. Zeeb 9209af1bba4SBjoern A. Zeeb iwl_mvm_sf_update(mvm, vif, false); 9219af1bba4SBjoern A. Zeeb 9229af1bba4SBjoern A. Zeeb /* FIXME: need to decide about misbehaving AP handling */ 9239af1bba4SBjoern A. Zeeb iwl_mvm_power_vif_assoc(mvm, vif); 9249af1bba4SBjoern A. Zeeb } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { 9259af1bba4SBjoern A. Zeeb iwl_mvm_mei_host_disassociated(mvm); 9269af1bba4SBjoern A. Zeeb 9279af1bba4SBjoern A. Zeeb /* If update fails - SF might be running in associated 9289af1bba4SBjoern A. Zeeb * mode while disassociated - which is forbidden. 9299af1bba4SBjoern A. Zeeb */ 9309af1bba4SBjoern A. Zeeb ret = iwl_mvm_sf_update(mvm, vif, false); 9319af1bba4SBjoern A. Zeeb WARN_ONCE(ret && 9329af1bba4SBjoern A. Zeeb !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, 9339af1bba4SBjoern A. Zeeb &mvm->status), 9349af1bba4SBjoern A. Zeeb "Failed to update SF upon disassociation\n"); 9359af1bba4SBjoern A. Zeeb 9369af1bba4SBjoern A. Zeeb /* If we get an assert during the connection (after the 9379af1bba4SBjoern A. Zeeb * station has been added, but before the vif is set 9389af1bba4SBjoern A. Zeeb * to associated), mac80211 will re-add the station and 9399af1bba4SBjoern A. Zeeb * then configure the vif. Since the vif is not 9409af1bba4SBjoern A. Zeeb * associated, we would remove the station here and 9419af1bba4SBjoern A. Zeeb * this would fail the recovery. 9429af1bba4SBjoern A. Zeeb */ 9439af1bba4SBjoern A. Zeeb iwl_mvm_mld_vif_delete_all_stas(mvm, vif); 9449af1bba4SBjoern A. Zeeb } 9459af1bba4SBjoern A. Zeeb 9469af1bba4SBjoern A. Zeeb iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); 9479af1bba4SBjoern A. Zeeb } 9489af1bba4SBjoern A. Zeeb 949*a4128aadSBjoern A. Zeeb if (changes & BSS_CHANGED_PS) { 950*a4128aadSBjoern A. Zeeb ret = iwl_mvm_power_update_mac(mvm); 951*a4128aadSBjoern A. Zeeb if (ret) 952*a4128aadSBjoern A. Zeeb IWL_ERR(mvm, "failed to update power mode\n"); 953*a4128aadSBjoern A. Zeeb } 954*a4128aadSBjoern A. Zeeb 955*a4128aadSBjoern A. Zeeb if (changes & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM) && 956*a4128aadSBjoern A. Zeeb ieee80211_vif_is_mld(vif) && mvmvif->authorized) 957*a4128aadSBjoern A. Zeeb wiphy_delayed_work_queue(mvm->hw->wiphy, 958*a4128aadSBjoern A. Zeeb &mvmvif->mlo_int_scan_wk, 0); 959*a4128aadSBjoern A. Zeeb } 960*a4128aadSBjoern A. Zeeb 9619af1bba4SBjoern A. Zeeb static void 9629af1bba4SBjoern A. Zeeb iwl_mvm_mld_link_info_changed_ap_ibss(struct iwl_mvm *mvm, 9639af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 9649af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 9659af1bba4SBjoern A. Zeeb u64 changes) 9669af1bba4SBjoern A. Zeeb { 9679af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 9689af1bba4SBjoern A. Zeeb u32 link_changes = LINK_CONTEXT_MODIFY_PROTECT_FLAGS | 9699af1bba4SBjoern A. Zeeb LINK_CONTEXT_MODIFY_QOS_PARAMS; 9709af1bba4SBjoern A. Zeeb 9719af1bba4SBjoern A. Zeeb /* Changes will be applied when the AP/IBSS is started */ 9729af1bba4SBjoern A. Zeeb if (!mvmvif->ap_ibss_active) 9739af1bba4SBjoern A. Zeeb return; 9749af1bba4SBjoern A. Zeeb 9759af1bba4SBjoern A. Zeeb if (link_conf->he_support) 9769af1bba4SBjoern A. Zeeb link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; 9779af1bba4SBjoern A. Zeeb 9789af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_ERP_SLOT) 9799af1bba4SBjoern A. Zeeb link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; 9809af1bba4SBjoern A. Zeeb 9819af1bba4SBjoern A. Zeeb if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_SLOT | 9829af1bba4SBjoern A. Zeeb BSS_CHANGED_HT | 9839af1bba4SBjoern A. Zeeb BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS | 9849af1bba4SBjoern A. Zeeb BSS_CHANGED_HE_BSS_COLOR) && 9859af1bba4SBjoern A. Zeeb iwl_mvm_link_changed(mvm, vif, link_conf, 9869af1bba4SBjoern A. Zeeb link_changes, true)) 9879af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); 9889af1bba4SBjoern A. Zeeb 9899af1bba4SBjoern A. Zeeb /* Need to send a new beacon template to the FW */ 9909af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_BEACON && 9919af1bba4SBjoern A. Zeeb iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf)) 9929af1bba4SBjoern A. Zeeb IWL_WARN(mvm, "Failed updating beacon data\n"); 9939af1bba4SBjoern A. Zeeb 9949af1bba4SBjoern A. Zeeb /* FIXME: need to decide if we need FTM responder per link */ 9959af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_FTM_RESPONDER) { 9969af1bba4SBjoern A. Zeeb int ret = iwl_mvm_ftm_start_responder(mvm, vif, link_conf); 9979af1bba4SBjoern A. Zeeb 9989af1bba4SBjoern A. Zeeb if (ret) 9999af1bba4SBjoern A. Zeeb IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n", 10009af1bba4SBjoern A. Zeeb ret); 10019af1bba4SBjoern A. Zeeb } 10029af1bba4SBjoern A. Zeeb } 10039af1bba4SBjoern A. Zeeb 10049af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw, 10059af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 10069af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf, 10079af1bba4SBjoern A. Zeeb u64 changes) 10089af1bba4SBjoern A. Zeeb { 10099af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 10109af1bba4SBjoern A. Zeeb 1011*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 10129af1bba4SBjoern A. Zeeb 10139af1bba4SBjoern A. Zeeb switch (vif->type) { 10149af1bba4SBjoern A. Zeeb case NL80211_IFTYPE_STATION: 10159af1bba4SBjoern A. Zeeb iwl_mvm_mld_link_info_changed_station(mvm, vif, link_conf, 10169af1bba4SBjoern A. Zeeb changes); 10179af1bba4SBjoern A. Zeeb break; 10189af1bba4SBjoern A. Zeeb case NL80211_IFTYPE_AP: 10199af1bba4SBjoern A. Zeeb case NL80211_IFTYPE_ADHOC: 10209af1bba4SBjoern A. Zeeb iwl_mvm_mld_link_info_changed_ap_ibss(mvm, vif, link_conf, 10219af1bba4SBjoern A. Zeeb changes); 10229af1bba4SBjoern A. Zeeb break; 10239af1bba4SBjoern A. Zeeb case NL80211_IFTYPE_MONITOR: 10249af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_MU_GROUPS) 10259af1bba4SBjoern A. Zeeb iwl_mvm_update_mu_groups(mvm, vif); 10269af1bba4SBjoern A. Zeeb break; 10279af1bba4SBjoern A. Zeeb default: 10289af1bba4SBjoern A. Zeeb /* shouldn't happen */ 10299af1bba4SBjoern A. Zeeb WARN_ON_ONCE(1); 10309af1bba4SBjoern A. Zeeb } 10319af1bba4SBjoern A. Zeeb 10329af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_TXPOWER) { 10339af1bba4SBjoern A. Zeeb IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n", 10349af1bba4SBjoern A. Zeeb link_conf->txpower); 10359af1bba4SBjoern A. Zeeb iwl_mvm_set_tx_power(mvm, vif, link_conf->txpower); 10369af1bba4SBjoern A. Zeeb } 10379af1bba4SBjoern A. Zeeb } 10389af1bba4SBjoern A. Zeeb 10399af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_vif_cfg_changed(struct ieee80211_hw *hw, 10409af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 10419af1bba4SBjoern A. Zeeb u64 changes) 10429af1bba4SBjoern A. Zeeb { 10439af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 10449af1bba4SBjoern A. Zeeb 1045*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 10469af1bba4SBjoern A. Zeeb 10479af1bba4SBjoern A. Zeeb if (changes & BSS_CHANGED_IDLE && !vif->cfg.idle) 10489af1bba4SBjoern A. Zeeb iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); 10499af1bba4SBjoern A. Zeeb 10509af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_STATION) 10519af1bba4SBjoern A. Zeeb iwl_mvm_mld_vif_cfg_changed_station(mvm, vif, changes); 10529af1bba4SBjoern A. Zeeb } 10539af1bba4SBjoern A. Zeeb 10549af1bba4SBjoern A. Zeeb static int 10559af1bba4SBjoern A. Zeeb iwl_mvm_mld_switch_vif_chanctx(struct ieee80211_hw *hw, 10569af1bba4SBjoern A. Zeeb struct ieee80211_vif_chanctx_switch *vifs, 10579af1bba4SBjoern A. Zeeb int n_vifs, 10589af1bba4SBjoern A. Zeeb enum ieee80211_chanctx_switch_mode mode) 10599af1bba4SBjoern A. Zeeb { 10609af1bba4SBjoern A. Zeeb static const struct iwl_mvm_switch_vif_chanctx_ops ops = { 10619af1bba4SBjoern A. Zeeb .__assign_vif_chanctx = __iwl_mvm_mld_assign_vif_chanctx, 10629af1bba4SBjoern A. Zeeb .__unassign_vif_chanctx = __iwl_mvm_mld_unassign_vif_chanctx, 10639af1bba4SBjoern A. Zeeb }; 10649af1bba4SBjoern A. Zeeb 10659af1bba4SBjoern A. Zeeb return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops); 10669af1bba4SBjoern A. Zeeb } 10679af1bba4SBjoern A. Zeeb 10689af1bba4SBjoern A. Zeeb static void iwl_mvm_mld_config_iface_filter(struct ieee80211_hw *hw, 10699af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 10709af1bba4SBjoern A. Zeeb unsigned int filter_flags, 10719af1bba4SBjoern A. Zeeb unsigned int changed_flags) 10729af1bba4SBjoern A. Zeeb { 10739af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 10749af1bba4SBjoern A. Zeeb 10759af1bba4SBjoern A. Zeeb /* We support only filter for probe requests */ 10769af1bba4SBjoern A. Zeeb if (!(changed_flags & FIF_PROBE_REQ)) 10779af1bba4SBjoern A. Zeeb return; 10789af1bba4SBjoern A. Zeeb 10799af1bba4SBjoern A. Zeeb /* Supported only for p2p client interfaces */ 10809af1bba4SBjoern A. Zeeb if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc || 10819af1bba4SBjoern A. Zeeb !vif->p2p) 10829af1bba4SBjoern A. Zeeb return; 10839af1bba4SBjoern A. Zeeb 1084*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 10859af1bba4SBjoern A. Zeeb iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); 10869af1bba4SBjoern A. Zeeb } 10879af1bba4SBjoern A. Zeeb 10889af1bba4SBjoern A. Zeeb static int 10899af1bba4SBjoern A. Zeeb iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw, 10909af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 10919af1bba4SBjoern A. Zeeb unsigned int link_id, u16 ac, 10929af1bba4SBjoern A. Zeeb const struct ieee80211_tx_queue_params *params) 10939af1bba4SBjoern A. Zeeb { 10949af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 10959af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 10969af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *mvm_link = mvmvif->link[link_id]; 10979af1bba4SBjoern A. Zeeb 10989af1bba4SBjoern A. Zeeb if (!mvm_link) 10999af1bba4SBjoern A. Zeeb return -EINVAL; 11009af1bba4SBjoern A. Zeeb 11019af1bba4SBjoern A. Zeeb mvm_link->queue_params[ac] = *params; 11029af1bba4SBjoern A. Zeeb 11039af1bba4SBjoern A. Zeeb /* No need to update right away, we'll get BSS_CHANGED_QOS 11049af1bba4SBjoern A. Zeeb * The exception is P2P_DEVICE interface which needs immediate update. 11059af1bba4SBjoern A. Zeeb */ 11069af1bba4SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 1107*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 1108*a4128aadSBjoern A. Zeeb return iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 11099af1bba4SBjoern A. Zeeb LINK_CONTEXT_MODIFY_QOS_PARAMS, 11109af1bba4SBjoern A. Zeeb true); 11119af1bba4SBjoern A. Zeeb } 11129af1bba4SBjoern A. Zeeb return 0; 11139af1bba4SBjoern A. Zeeb } 11149af1bba4SBjoern A. Zeeb 1115*a4128aadSBjoern A. Zeeb static int iwl_mvm_mld_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 11169af1bba4SBjoern A. Zeeb { 1117*a4128aadSBjoern A. Zeeb int ret; 11189af1bba4SBjoern A. Zeeb 11199af1bba4SBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 11209af1bba4SBjoern A. Zeeb 1121*a4128aadSBjoern A. Zeeb /* The PHY context ID might have changed so need to set it */ 11229af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 0, false); 1123*a4128aadSBjoern A. Zeeb if (WARN(ret, "Failed to set PHY context ID\n")) 11249af1bba4SBjoern A. Zeeb return ret; 11259af1bba4SBjoern A. Zeeb 11269af1bba4SBjoern A. Zeeb ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 1127*a4128aadSBjoern A. Zeeb LINK_CONTEXT_MODIFY_ACTIVE | 1128*a4128aadSBjoern A. Zeeb LINK_CONTEXT_MODIFY_RATES_INFO, 1129*a4128aadSBjoern A. Zeeb true); 1130*a4128aadSBjoern A. Zeeb 1131*a4128aadSBjoern A. Zeeb if (WARN(ret, "Failed linking P2P_DEVICE\n")) 11329af1bba4SBjoern A. Zeeb return ret; 1133*a4128aadSBjoern A. Zeeb 1134*a4128aadSBjoern A. Zeeb /* The station and queue allocation must be done only after the linking 1135*a4128aadSBjoern A. Zeeb * is done, as otherwise the FW might incorrectly configure its state. 1136*a4128aadSBjoern A. Zeeb */ 1137*a4128aadSBjoern A. Zeeb return iwl_mvm_mld_add_bcast_sta(mvm, vif, &vif->bss_conf); 11389af1bba4SBjoern A. Zeeb } 11399af1bba4SBjoern A. Zeeb 11409af1bba4SBjoern A. Zeeb static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 11419af1bba4SBjoern A. Zeeb struct ieee80211_channel *channel, int duration, 11429af1bba4SBjoern A. Zeeb enum ieee80211_roc_type type) 11439af1bba4SBjoern A. Zeeb { 11449af1bba4SBjoern A. Zeeb static const struct iwl_mvm_roc_ops ops = { 11459af1bba4SBjoern A. Zeeb .add_aux_sta_for_hs20 = iwl_mvm_mld_add_aux_sta, 1146*a4128aadSBjoern A. Zeeb .link = iwl_mvm_mld_roc_link, 11479af1bba4SBjoern A. Zeeb }; 11489af1bba4SBjoern A. Zeeb 11499af1bba4SBjoern A. Zeeb return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); 11509af1bba4SBjoern A. Zeeb } 11519af1bba4SBjoern A. Zeeb 11529af1bba4SBjoern A. Zeeb static int 11539af1bba4SBjoern A. Zeeb iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, 11549af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 11559af1bba4SBjoern A. Zeeb u16 old_links, u16 new_links, 11569af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) 11579af1bba4SBjoern A. Zeeb { 11589af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {}; 11599af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 11609af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 11619af1bba4SBjoern A. Zeeb u16 removed = old_links & ~new_links; 11629af1bba4SBjoern A. Zeeb u16 added = new_links & ~old_links; 11639af1bba4SBjoern A. Zeeb int err, i; 11649af1bba4SBjoern A. Zeeb 11659af1bba4SBjoern A. Zeeb for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { 11669af1bba4SBjoern A. Zeeb int r; 11679af1bba4SBjoern A. Zeeb 11689af1bba4SBjoern A. Zeeb if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 11699af1bba4SBjoern A. Zeeb break; 11709af1bba4SBjoern A. Zeeb 11719af1bba4SBjoern A. Zeeb if (!(added & BIT(i))) 11729af1bba4SBjoern A. Zeeb continue; 11739af1bba4SBjoern A. Zeeb new_link[i] = kzalloc(sizeof(*new_link[i]), GFP_KERNEL); 11749af1bba4SBjoern A. Zeeb if (!new_link[i]) { 11759af1bba4SBjoern A. Zeeb err = -ENOMEM; 11769af1bba4SBjoern A. Zeeb goto free; 11779af1bba4SBjoern A. Zeeb } 11789af1bba4SBjoern A. Zeeb 11799af1bba4SBjoern A. Zeeb new_link[i]->bcast_sta.sta_id = IWL_MVM_INVALID_STA; 11809af1bba4SBjoern A. Zeeb new_link[i]->mcast_sta.sta_id = IWL_MVM_INVALID_STA; 11819af1bba4SBjoern A. Zeeb new_link[i]->ap_sta_id = IWL_MVM_INVALID_STA; 11829af1bba4SBjoern A. Zeeb new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; 11839af1bba4SBjoern A. Zeeb 11849af1bba4SBjoern A. Zeeb for (r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++) 11859af1bba4SBjoern A. Zeeb new_link[i]->smps_requests[r] = 11869af1bba4SBjoern A. Zeeb IEEE80211_SMPS_AUTOMATIC; 11879af1bba4SBjoern A. Zeeb } 11889af1bba4SBjoern A. Zeeb 11899af1bba4SBjoern A. Zeeb mutex_lock(&mvm->mutex); 11909af1bba4SBjoern A. Zeeb 11919af1bba4SBjoern A. Zeeb if (old_links == 0) { 11929af1bba4SBjoern A. Zeeb err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); 11939af1bba4SBjoern A. Zeeb if (err) 11949af1bba4SBjoern A. Zeeb goto out_err; 11959af1bba4SBjoern A. Zeeb mvmvif->link[0] = NULL; 11969af1bba4SBjoern A. Zeeb } 11979af1bba4SBjoern A. Zeeb 11989af1bba4SBjoern A. Zeeb for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { 11999af1bba4SBjoern A. Zeeb if (removed & BIT(i)) { 12009af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf = old[i]; 12019af1bba4SBjoern A. Zeeb 12029af1bba4SBjoern A. Zeeb err = iwl_mvm_disable_link(mvm, vif, link_conf); 12039af1bba4SBjoern A. Zeeb if (err) 12049af1bba4SBjoern A. Zeeb goto out_err; 12059af1bba4SBjoern A. Zeeb kfree(mvmvif->link[i]); 12069af1bba4SBjoern A. Zeeb mvmvif->link[i] = NULL; 12079af1bba4SBjoern A. Zeeb } else if (added & BIT(i)) { 12089af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf; 12099af1bba4SBjoern A. Zeeb 12109af1bba4SBjoern A. Zeeb link_conf = link_conf_dereference_protected(vif, i); 12119af1bba4SBjoern A. Zeeb if (WARN_ON(!link_conf)) 12129af1bba4SBjoern A. Zeeb continue; 12139af1bba4SBjoern A. Zeeb 12149af1bba4SBjoern A. Zeeb if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, 12159af1bba4SBjoern A. Zeeb &mvm->status)) 12169af1bba4SBjoern A. Zeeb mvmvif->link[i] = new_link[i]; 12179af1bba4SBjoern A. Zeeb new_link[i] = NULL; 12189af1bba4SBjoern A. Zeeb err = iwl_mvm_add_link(mvm, vif, link_conf); 12199af1bba4SBjoern A. Zeeb if (err) 12209af1bba4SBjoern A. Zeeb goto out_err; 12219af1bba4SBjoern A. Zeeb } 12229af1bba4SBjoern A. Zeeb } 12239af1bba4SBjoern A. Zeeb 12249af1bba4SBjoern A. Zeeb err = 0; 12259af1bba4SBjoern A. Zeeb if (new_links == 0) { 12269af1bba4SBjoern A. Zeeb mvmvif->link[0] = &mvmvif->deflink; 12279af1bba4SBjoern A. Zeeb err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); 1228*a4128aadSBjoern A. Zeeb if (err == 0) 1229*a4128aadSBjoern A. Zeeb mvmvif->primary_link = 0; 1230*a4128aadSBjoern A. Zeeb } else if (!(new_links & BIT(mvmvif->primary_link))) { 1231*a4128aadSBjoern A. Zeeb /* 1232*a4128aadSBjoern A. Zeeb * Ensure we always have a valid primary_link, the real 1233*a4128aadSBjoern A. Zeeb * decision happens later when PHY is activated. 1234*a4128aadSBjoern A. Zeeb */ 1235*a4128aadSBjoern A. Zeeb mvmvif->primary_link = __ffs(new_links); 12369af1bba4SBjoern A. Zeeb } 12379af1bba4SBjoern A. Zeeb 12389af1bba4SBjoern A. Zeeb out_err: 12399af1bba4SBjoern A. Zeeb /* we really don't have a good way to roll back here ... */ 12409af1bba4SBjoern A. Zeeb mutex_unlock(&mvm->mutex); 12419af1bba4SBjoern A. Zeeb 12429af1bba4SBjoern A. Zeeb free: 12439af1bba4SBjoern A. Zeeb for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) 12449af1bba4SBjoern A. Zeeb kfree(new_link[i]); 12459af1bba4SBjoern A. Zeeb return err; 12469af1bba4SBjoern A. Zeeb } 12479af1bba4SBjoern A. Zeeb 12489af1bba4SBjoern A. Zeeb static int 12499af1bba4SBjoern A. Zeeb iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, 12509af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 12519af1bba4SBjoern A. Zeeb struct ieee80211_sta *sta, 12529af1bba4SBjoern A. Zeeb u16 old_links, u16 new_links) 12539af1bba4SBjoern A. Zeeb { 12549af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 1255*a4128aadSBjoern A. Zeeb 1256*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 1257*a4128aadSBjoern A. Zeeb return iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links); 1258*a4128aadSBjoern A. Zeeb } 1259*a4128aadSBjoern A. Zeeb 1260*a4128aadSBjoern A. Zeeb bool iwl_mvm_vif_has_esr_cap(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 1261*a4128aadSBjoern A. Zeeb { 1262*a4128aadSBjoern A. Zeeb const struct wiphy_iftype_ext_capab *ext_capa; 1263*a4128aadSBjoern A. Zeeb 1264*a4128aadSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1265*a4128aadSBjoern A. Zeeb 1266*a4128aadSBjoern A. Zeeb if (!ieee80211_vif_is_mld(vif) || !vif->cfg.assoc || 1267*a4128aadSBjoern A. Zeeb hweight16(ieee80211_vif_usable_links(vif)) == 1) 1268*a4128aadSBjoern A. Zeeb return false; 1269*a4128aadSBjoern A. Zeeb 1270*a4128aadSBjoern A. Zeeb if (!(vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP)) 1271*a4128aadSBjoern A. Zeeb return false; 1272*a4128aadSBjoern A. Zeeb 1273*a4128aadSBjoern A. Zeeb ext_capa = cfg80211_get_iftype_ext_capa(mvm->hw->wiphy, 1274*a4128aadSBjoern A. Zeeb ieee80211_vif_type_p2p(vif)); 1275*a4128aadSBjoern A. Zeeb return (ext_capa && 1276*a4128aadSBjoern A. Zeeb (ext_capa->eml_capabilities & IEEE80211_EML_CAP_EMLSR_SUPP)); 1277*a4128aadSBjoern A. Zeeb } 1278*a4128aadSBjoern A. Zeeb 1279*a4128aadSBjoern A. Zeeb static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw, 1280*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 1281*a4128aadSBjoern A. Zeeb u16 desired_links) 1282*a4128aadSBjoern A. Zeeb { 1283*a4128aadSBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 1284*a4128aadSBjoern A. Zeeb int n_links = hweight16(desired_links); 1285*a4128aadSBjoern A. Zeeb 1286*a4128aadSBjoern A. Zeeb if (n_links <= 1) 1287*a4128aadSBjoern A. Zeeb return true; 1288*a4128aadSBjoern A. Zeeb 1289*a4128aadSBjoern A. Zeeb guard(mvm)(mvm); 1290*a4128aadSBjoern A. Zeeb 1291*a4128aadSBjoern A. Zeeb /* Check if HW supports the wanted number of links */ 1292*a4128aadSBjoern A. Zeeb if (n_links > iwl_mvm_max_active_links(mvm, vif)) 1293*a4128aadSBjoern A. Zeeb return false; 1294*a4128aadSBjoern A. Zeeb 1295*a4128aadSBjoern A. Zeeb /* If it is an eSR device, check that we can enter eSR */ 1296*a4128aadSBjoern A. Zeeb return iwl_mvm_is_esr_supported(mvm->fwrt.trans) && 1297*a4128aadSBjoern A. Zeeb iwl_mvm_vif_has_esr_cap(mvm, vif); 1298*a4128aadSBjoern A. Zeeb } 1299*a4128aadSBjoern A. Zeeb 1300*a4128aadSBjoern A. Zeeb static enum ieee80211_neg_ttlm_res 1301*a4128aadSBjoern A. Zeeb iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 1302*a4128aadSBjoern A. Zeeb struct ieee80211_neg_ttlm *neg_ttlm) 1303*a4128aadSBjoern A. Zeeb { 1304*a4128aadSBjoern A. Zeeb u16 map; 1305*a4128aadSBjoern A. Zeeb u8 i; 1306*a4128aadSBjoern A. Zeeb 1307*a4128aadSBjoern A. Zeeb /* Verify all TIDs are mapped to the same links set */ 1308*a4128aadSBjoern A. Zeeb map = neg_ttlm->downlink[0]; 1309*a4128aadSBjoern A. Zeeb for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) { 1310*a4128aadSBjoern A. Zeeb if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] || 1311*a4128aadSBjoern A. Zeeb neg_ttlm->uplink[i] != map) 1312*a4128aadSBjoern A. Zeeb return NEG_TTLM_RES_REJECT; 1313*a4128aadSBjoern A. Zeeb } 1314*a4128aadSBjoern A. Zeeb 1315*a4128aadSBjoern A. Zeeb return NEG_TTLM_RES_ACCEPT; 1316*a4128aadSBjoern A. Zeeb } 1317*a4128aadSBjoern A. Zeeb 1318*a4128aadSBjoern A. Zeeb static int 1319*a4128aadSBjoern A. Zeeb iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw, 1320*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 1321*a4128aadSBjoern A. Zeeb struct ieee80211_channel_switch *chsw) 1322*a4128aadSBjoern A. Zeeb { 1323*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1324*a4128aadSBjoern A. Zeeb struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 13259af1bba4SBjoern A. Zeeb int ret; 13269af1bba4SBjoern A. Zeeb 13279af1bba4SBjoern A. Zeeb mutex_lock(&mvm->mutex); 1328*a4128aadSBjoern A. Zeeb if (mvmvif->esr_active) { 1329*a4128aadSBjoern A. Zeeb u8 primary = iwl_mvm_get_primary_link(vif); 1330*a4128aadSBjoern A. Zeeb int selected; 1331*a4128aadSBjoern A. Zeeb 1332*a4128aadSBjoern A. Zeeb /* prefer primary unless quiet CSA on it */ 1333*a4128aadSBjoern A. Zeeb if (chsw->link_id == primary && chsw->block_tx) 1334*a4128aadSBjoern A. Zeeb selected = iwl_mvm_get_other_link(vif, primary); 1335*a4128aadSBjoern A. Zeeb else 1336*a4128aadSBjoern A. Zeeb selected = primary; 1337*a4128aadSBjoern A. Zeeb 1338*a4128aadSBjoern A. Zeeb iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_CSA, selected); 1339*a4128aadSBjoern A. Zeeb mutex_unlock(&mvm->mutex); 1340*a4128aadSBjoern A. Zeeb 1341*a4128aadSBjoern A. Zeeb /* 1342*a4128aadSBjoern A. Zeeb * If we've not kept the link active that's doing the CSA 1343*a4128aadSBjoern A. Zeeb * then we don't need to do anything else, just return. 1344*a4128aadSBjoern A. Zeeb */ 1345*a4128aadSBjoern A. Zeeb if (selected != chsw->link_id) 1346*a4128aadSBjoern A. Zeeb return 0; 1347*a4128aadSBjoern A. Zeeb 1348*a4128aadSBjoern A. Zeeb mutex_lock(&mvm->mutex); 1349*a4128aadSBjoern A. Zeeb } 1350*a4128aadSBjoern A. Zeeb 1351*a4128aadSBjoern A. Zeeb ret = iwl_mvm_pre_channel_switch(mvm, vif, chsw); 13529af1bba4SBjoern A. Zeeb mutex_unlock(&mvm->mutex); 13539af1bba4SBjoern A. Zeeb 13549af1bba4SBjoern A. Zeeb return ret; 13559af1bba4SBjoern A. Zeeb } 13569af1bba4SBjoern A. Zeeb 13579af1bba4SBjoern A. Zeeb const struct ieee80211_ops iwl_mvm_mld_hw_ops = { 13589af1bba4SBjoern A. Zeeb .tx = iwl_mvm_mac_tx, 13599af1bba4SBjoern A. Zeeb .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, 13609af1bba4SBjoern A. Zeeb .ampdu_action = iwl_mvm_mac_ampdu_action, 13619af1bba4SBjoern A. Zeeb .get_antenna = iwl_mvm_op_get_antenna, 1362*a4128aadSBjoern A. Zeeb .set_antenna = iwl_mvm_op_set_antenna, 13639af1bba4SBjoern A. Zeeb .start = iwl_mvm_mac_start, 13649af1bba4SBjoern A. Zeeb .reconfig_complete = iwl_mvm_mac_reconfig_complete, 13659af1bba4SBjoern A. Zeeb .stop = iwl_mvm_mac_stop, 13669af1bba4SBjoern A. Zeeb .add_interface = iwl_mvm_mld_mac_add_interface, 13679af1bba4SBjoern A. Zeeb .remove_interface = iwl_mvm_mld_mac_remove_interface, 13689af1bba4SBjoern A. Zeeb .config = iwl_mvm_mac_config, 13699af1bba4SBjoern A. Zeeb .prepare_multicast = iwl_mvm_prepare_multicast, 13709af1bba4SBjoern A. Zeeb .configure_filter = iwl_mvm_configure_filter, 13719af1bba4SBjoern A. Zeeb .config_iface_filter = iwl_mvm_mld_config_iface_filter, 13729af1bba4SBjoern A. Zeeb .link_info_changed = iwl_mvm_mld_link_info_changed, 13739af1bba4SBjoern A. Zeeb .vif_cfg_changed = iwl_mvm_mld_vif_cfg_changed, 13749af1bba4SBjoern A. Zeeb .hw_scan = iwl_mvm_mac_hw_scan, 13759af1bba4SBjoern A. Zeeb .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, 13769af1bba4SBjoern A. Zeeb .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, 13779af1bba4SBjoern A. Zeeb .sta_state = iwl_mvm_mld_mac_sta_state, 13789af1bba4SBjoern A. Zeeb .sta_notify = iwl_mvm_mac_sta_notify, 13799af1bba4SBjoern A. Zeeb .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, 13809af1bba4SBjoern A. Zeeb .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, 13819af1bba4SBjoern A. Zeeb .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, 13829af1bba4SBjoern A. Zeeb .sta_rc_update = iwl_mvm_sta_rc_update, 13839af1bba4SBjoern A. Zeeb .conf_tx = iwl_mvm_mld_mac_conf_tx, 13849af1bba4SBjoern A. Zeeb .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, 13859af1bba4SBjoern A. Zeeb .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, 13869af1bba4SBjoern A. Zeeb .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, 13879af1bba4SBjoern A. Zeeb .flush = iwl_mvm_mac_flush, 13889af1bba4SBjoern A. Zeeb .flush_sta = iwl_mvm_mac_flush_sta, 13899af1bba4SBjoern A. Zeeb .sched_scan_start = iwl_mvm_mac_sched_scan_start, 13909af1bba4SBjoern A. Zeeb .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, 13919af1bba4SBjoern A. Zeeb .set_key = iwl_mvm_mac_set_key, 13929af1bba4SBjoern A. Zeeb .update_tkip_key = iwl_mvm_mac_update_tkip_key, 13939af1bba4SBjoern A. Zeeb .remain_on_channel = iwl_mvm_mld_roc, 13949af1bba4SBjoern A. Zeeb .cancel_remain_on_channel = iwl_mvm_cancel_roc, 13959af1bba4SBjoern A. Zeeb .add_chanctx = iwl_mvm_add_chanctx, 13969af1bba4SBjoern A. Zeeb .remove_chanctx = iwl_mvm_remove_chanctx, 13979af1bba4SBjoern A. Zeeb .change_chanctx = iwl_mvm_change_chanctx, 13989af1bba4SBjoern A. Zeeb .assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx, 13999af1bba4SBjoern A. Zeeb .unassign_vif_chanctx = iwl_mvm_mld_unassign_vif_chanctx, 14009af1bba4SBjoern A. Zeeb .switch_vif_chanctx = iwl_mvm_mld_switch_vif_chanctx, 14019af1bba4SBjoern A. Zeeb 14029af1bba4SBjoern A. Zeeb .start_ap = iwl_mvm_mld_start_ap, 14039af1bba4SBjoern A. Zeeb .stop_ap = iwl_mvm_mld_stop_ap, 14049af1bba4SBjoern A. Zeeb .join_ibss = iwl_mvm_mld_start_ibss, 14059af1bba4SBjoern A. Zeeb .leave_ibss = iwl_mvm_mld_stop_ibss, 14069af1bba4SBjoern A. Zeeb 14079af1bba4SBjoern A. Zeeb .tx_last_beacon = iwl_mvm_tx_last_beacon, 14089af1bba4SBjoern A. Zeeb 14099af1bba4SBjoern A. Zeeb .channel_switch = iwl_mvm_channel_switch, 1410*a4128aadSBjoern A. Zeeb .pre_channel_switch = iwl_mvm_mld_mac_pre_channel_switch, 14119af1bba4SBjoern A. Zeeb .post_channel_switch = iwl_mvm_post_channel_switch, 14129af1bba4SBjoern A. Zeeb .abort_channel_switch = iwl_mvm_abort_channel_switch, 14139af1bba4SBjoern A. Zeeb .channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon, 14149af1bba4SBjoern A. Zeeb 14159af1bba4SBjoern A. Zeeb .tdls_channel_switch = iwl_mvm_tdls_channel_switch, 14169af1bba4SBjoern A. Zeeb .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, 14179af1bba4SBjoern A. Zeeb .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, 14189af1bba4SBjoern A. Zeeb 14199af1bba4SBjoern A. Zeeb .event_callback = iwl_mvm_mac_event_callback, 14209af1bba4SBjoern A. Zeeb 14219af1bba4SBjoern A. Zeeb .sync_rx_queues = iwl_mvm_sync_rx_queues, 14229af1bba4SBjoern A. Zeeb 14239af1bba4SBjoern A. Zeeb CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) 14249af1bba4SBjoern A. Zeeb 14259af1bba4SBjoern A. Zeeb #ifdef CONFIG_PM_SLEEP 14269af1bba4SBjoern A. Zeeb /* look at d3.c */ 14279af1bba4SBjoern A. Zeeb .suspend = iwl_mvm_suspend, 14289af1bba4SBjoern A. Zeeb .resume = iwl_mvm_resume, 14299af1bba4SBjoern A. Zeeb .set_wakeup = iwl_mvm_set_wakeup, 14309af1bba4SBjoern A. Zeeb .set_rekey_data = iwl_mvm_set_rekey_data, 14319af1bba4SBjoern A. Zeeb #if IS_ENABLED(CONFIG_IPV6) 14329af1bba4SBjoern A. Zeeb .ipv6_addr_change = iwl_mvm_ipv6_addr_change, 14339af1bba4SBjoern A. Zeeb #endif 14349af1bba4SBjoern A. Zeeb .set_default_unicast_key = iwl_mvm_set_default_unicast_key, 14359af1bba4SBjoern A. Zeeb #endif 14369af1bba4SBjoern A. Zeeb .get_survey = iwl_mvm_mac_get_survey, 14379af1bba4SBjoern A. Zeeb .sta_statistics = iwl_mvm_mac_sta_statistics, 14389af1bba4SBjoern A. Zeeb .get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats, 14399af1bba4SBjoern A. Zeeb .start_pmsr = iwl_mvm_start_pmsr, 14409af1bba4SBjoern A. Zeeb .abort_pmsr = iwl_mvm_abort_pmsr, 14419af1bba4SBjoern A. Zeeb 14429af1bba4SBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS 1443*a4128aadSBjoern A. Zeeb .vif_add_debugfs = iwl_mvm_vif_add_debugfs, 1444*a4128aadSBjoern A. Zeeb .link_add_debugfs = iwl_mvm_link_add_debugfs, 14459af1bba4SBjoern A. Zeeb .link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs, 14469af1bba4SBjoern A. Zeeb #endif 14479af1bba4SBjoern A. Zeeb .set_hw_timestamp = iwl_mvm_set_hw_timestamp, 14489af1bba4SBjoern A. Zeeb 14499af1bba4SBjoern A. Zeeb .change_vif_links = iwl_mvm_mld_change_vif_links, 14509af1bba4SBjoern A. Zeeb .change_sta_links = iwl_mvm_mld_change_sta_links, 1451*a4128aadSBjoern A. Zeeb .can_activate_links = iwl_mvm_mld_can_activate_links, 1452*a4128aadSBjoern A. Zeeb .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm, 14539af1bba4SBjoern A. Zeeb }; 1454