1d1e879ecSMiri Korenblit /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ 2d1e879ecSMiri Korenblit /* 3d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation 4d1e879ecSMiri Korenblit */ 5d1e879ecSMiri Korenblit #ifndef __iwl_mld_mlo_h__ 6d1e879ecSMiri Korenblit #define __iwl_mld_mlo_h__ 7d1e879ecSMiri Korenblit 8d1e879ecSMiri Korenblit #include <linux/ieee80211.h> 9d1e879ecSMiri Korenblit #include <linux/types.h> 10d1e879ecSMiri Korenblit #include <net/mac80211.h> 11d1e879ecSMiri Korenblit #include "iwl-config.h" 12d1e879ecSMiri Korenblit #include "iwl-trans.h" 13d1e879ecSMiri Korenblit #include "iface.h" 144d7236f9SMiri Korenblit #include "phy.h" 15d1e879ecSMiri Korenblit 16d1e879ecSMiri Korenblit struct iwl_mld; 17d1e879ecSMiri Korenblit 18d1e879ecSMiri Korenblit void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk); 19d1e879ecSMiri Korenblit void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, 20d1e879ecSMiri Korenblit struct wiphy_work *wk); 21d1e879ecSMiri Korenblit 22d1e879ecSMiri Korenblit static inline bool iwl_mld_emlsr_active(struct ieee80211_vif *vif) 23d1e879ecSMiri Korenblit { 24d1e879ecSMiri Korenblit /* Set on phy context activation, so should be a good proxy */ 25d1e879ecSMiri Korenblit return !!(vif->driver_flags & IEEE80211_VIF_EML_ACTIVE); 26d1e879ecSMiri Korenblit } 27d1e879ecSMiri Korenblit 28d1e879ecSMiri Korenblit static inline bool iwl_mld_vif_has_emlsr_cap(struct ieee80211_vif *vif) 29d1e879ecSMiri Korenblit { 30d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 31d1e879ecSMiri Korenblit 32d1e879ecSMiri Korenblit /* We only track/permit EMLSR state once authorized */ 33d1e879ecSMiri Korenblit if (!mld_vif->authorized) 34d1e879ecSMiri Korenblit return false; 35d1e879ecSMiri Korenblit 36d1e879ecSMiri Korenblit /* No EMLSR on dual radio devices */ 37d1e879ecSMiri Korenblit return ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION && 38d1e879ecSMiri Korenblit ieee80211_vif_is_mld(vif) && 39d1e879ecSMiri Korenblit vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP && 40d1e879ecSMiri Korenblit !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->hw_rf_id); 41d1e879ecSMiri Korenblit } 42d1e879ecSMiri Korenblit 43d1e879ecSMiri Korenblit static inline int 44d1e879ecSMiri Korenblit iwl_mld_max_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif) 45d1e879ecSMiri Korenblit { 46d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_AP) 47d1e879ecSMiri Korenblit return mld->fw->ucode_capa.num_beacons; 48d1e879ecSMiri Korenblit 49d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) 50d1e879ecSMiri Korenblit return IWL_FW_MAX_ACTIVE_LINKS_NUM; 51d1e879ecSMiri Korenblit 52d1e879ecSMiri Korenblit /* For now, do not accept more links on other interface types */ 53d1e879ecSMiri Korenblit return 1; 54d1e879ecSMiri Korenblit } 55d1e879ecSMiri Korenblit 56d1e879ecSMiri Korenblit static inline int 57d1e879ecSMiri Korenblit iwl_mld_count_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif) 58d1e879ecSMiri Korenblit { 59d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 60d1e879ecSMiri Korenblit struct iwl_mld_link *mld_link; 61d1e879ecSMiri Korenblit int n_active = 0; 62d1e879ecSMiri Korenblit 63d1e879ecSMiri Korenblit for_each_mld_vif_valid_link(mld_vif, mld_link) { 64d1e879ecSMiri Korenblit if (rcu_access_pointer(mld_link->chan_ctx)) 65d1e879ecSMiri Korenblit n_active++; 66d1e879ecSMiri Korenblit } 67d1e879ecSMiri Korenblit 68d1e879ecSMiri Korenblit return n_active; 69d1e879ecSMiri Korenblit } 70d1e879ecSMiri Korenblit 71d1e879ecSMiri Korenblit static inline u8 iwl_mld_get_primary_link(struct ieee80211_vif *vif) 72d1e879ecSMiri Korenblit { 73d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 74d1e879ecSMiri Korenblit 75d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld_vif->mld->wiphy); 76d1e879ecSMiri Korenblit 77d1e879ecSMiri Korenblit if (!ieee80211_vif_is_mld(vif) || WARN_ON(!vif->active_links)) 78d1e879ecSMiri Korenblit return 0; 79d1e879ecSMiri Korenblit 80d1e879ecSMiri Korenblit /* In AP mode, there is no primary link */ 81d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_AP) 82d1e879ecSMiri Korenblit return __ffs(vif->active_links); 83d1e879ecSMiri Korenblit 84d1e879ecSMiri Korenblit if (iwl_mld_emlsr_active(vif) && 85d1e879ecSMiri Korenblit !WARN_ON(!(BIT(mld_vif->emlsr.primary) & vif->active_links))) 86d1e879ecSMiri Korenblit return mld_vif->emlsr.primary; 87d1e879ecSMiri Korenblit 88d1e879ecSMiri Korenblit return __ffs(vif->active_links); 89d1e879ecSMiri Korenblit } 90d1e879ecSMiri Korenblit 91d1e879ecSMiri Korenblit /* 92d1e879ecSMiri Korenblit * For non-MLO/single link, this will return the deflink/single active link, 93d1e879ecSMiri Korenblit * respectively 94d1e879ecSMiri Korenblit */ 95d1e879ecSMiri Korenblit static inline u8 iwl_mld_get_other_link(struct ieee80211_vif *vif, u8 link_id) 96d1e879ecSMiri Korenblit { 97d1e879ecSMiri Korenblit switch (hweight16(vif->active_links)) { 98d1e879ecSMiri Korenblit case 0: 99d1e879ecSMiri Korenblit return 0; 100d1e879ecSMiri Korenblit default: 101d1e879ecSMiri Korenblit WARN_ON(1); 102d1e879ecSMiri Korenblit fallthrough; 103d1e879ecSMiri Korenblit case 1: 104d1e879ecSMiri Korenblit return __ffs(vif->active_links); 105d1e879ecSMiri Korenblit case 2: 106d1e879ecSMiri Korenblit return __ffs(vif->active_links & ~BIT(link_id)); 107d1e879ecSMiri Korenblit } 108d1e879ecSMiri Korenblit } 109d1e879ecSMiri Korenblit 110d1e879ecSMiri Korenblit s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, 111d1e879ecSMiri Korenblit const struct cfg80211_chan_def *chandef, 112d1e879ecSMiri Korenblit bool low); 113d1e879ecSMiri Korenblit 114d1e879ecSMiri Korenblit /* EMLSR block/unblock and exit */ 115d1e879ecSMiri Korenblit void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, 116d1e879ecSMiri Korenblit enum iwl_mld_emlsr_blocked reason, u8 link_to_keep); 117d1e879ecSMiri Korenblit int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, 118d1e879ecSMiri Korenblit enum iwl_mld_emlsr_blocked reason, u8 link_to_keep); 119d1e879ecSMiri Korenblit void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, 120d1e879ecSMiri Korenblit enum iwl_mld_emlsr_blocked reason); 121d1e879ecSMiri Korenblit void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, 122d1e879ecSMiri Korenblit enum iwl_mld_emlsr_exit exit, u8 link_to_keep); 123d1e879ecSMiri Korenblit 124d1e879ecSMiri Korenblit int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld, 125d1e879ecSMiri Korenblit int pending_link_changes); 126d1e879ecSMiri Korenblit 127d1e879ecSMiri Korenblit void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld, 128d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt); 129d1e879ecSMiri Korenblit void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld, 130d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt); 131d1e879ecSMiri Korenblit 132d1e879ecSMiri Korenblit void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk); 133d1e879ecSMiri Korenblit void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk); 134d1e879ecSMiri Korenblit 135d1e879ecSMiri Korenblit void iwl_mld_select_links(struct iwl_mld *mld); 136d1e879ecSMiri Korenblit 137d1e879ecSMiri Korenblit void iwl_mld_emlsr_check_bt(struct iwl_mld *mld); 138d1e879ecSMiri Korenblit 1394d7236f9SMiri Korenblit void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, 1404d7236f9SMiri Korenblit struct iwl_mld_phy *phy, 1414d7236f9SMiri Korenblit u32 prev_chan_load_not_by_us); 142d1e879ecSMiri Korenblit 143d1e879ecSMiri Korenblit /** 144d1e879ecSMiri Korenblit * iwl_mld_retry_emlsr - Retry entering EMLSR 145d1e879ecSMiri Korenblit * @mld: MLD context 146d1e879ecSMiri Korenblit * @vif: VIF to retry EMLSR on 147d1e879ecSMiri Korenblit * 148d1e879ecSMiri Korenblit * Retry entering EMLSR on the given VIF. 149d1e879ecSMiri Korenblit * Use this if one of the parameters that can prevent EMLSR has changed. 150d1e879ecSMiri Korenblit */ 151d1e879ecSMiri Korenblit void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif); 152d1e879ecSMiri Korenblit 153*cf6efe89SMiri Korenblit struct iwl_mld_link_sel_data { 154*cf6efe89SMiri Korenblit u8 link_id; 155*cf6efe89SMiri Korenblit const struct cfg80211_chan_def *chandef; 156*cf6efe89SMiri Korenblit s32 signal; 157*cf6efe89SMiri Korenblit u16 grade; 158*cf6efe89SMiri Korenblit }; 159*cf6efe89SMiri Korenblit 160*cf6efe89SMiri Korenblit #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) 161*cf6efe89SMiri Korenblit bool iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, 162*cf6efe89SMiri Korenblit struct ieee80211_vif *vif, 163*cf6efe89SMiri Korenblit const struct iwl_mld_link_sel_data *a, 164*cf6efe89SMiri Korenblit const struct iwl_mld_link_sel_data *b); 165*cf6efe89SMiri Korenblit #endif 166*cf6efe89SMiri Korenblit 167d1e879ecSMiri Korenblit #endif /* __iwl_mld_mlo_h__ */ 168