xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/mlo.h (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
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