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