xref: /linux/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c (revision e34a79b96ab9d49ed8b605fee11099cf3efbb428)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2022 - 2025 Intel Corporation
4  */
5 #include "mvm.h"
6 
iwl_mvm_mld_set_he_support(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_mac_config_cmd * cmd,int cmd_ver)7 static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
8 				       struct ieee80211_vif *vif,
9 				       struct iwl_mac_config_cmd *cmd,
10 				       int cmd_ver)
11 {
12 	if (vif->type == NL80211_IFTYPE_AP) {
13 		if (cmd_ver == 2)
14 			cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1);
15 		else
16 			cmd->wifi_gen.he_ap_support = 1;
17 	} else {
18 		if (cmd_ver == 2)
19 			cmd->wifi_gen_v2.he_support = cpu_to_le16(1);
20 		else
21 			cmd->wifi_gen.he_support = 1;
22 	}
23 }
24 
iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_mac_config_cmd * cmd,u32 action)25 static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
26 					    struct ieee80211_vif *vif,
27 					    struct iwl_mac_config_cmd *cmd,
28 					    u32 action)
29 {
30 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
31 	struct ieee80211_bss_conf *link_conf;
32 	unsigned int link_id;
33 	int cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
34 					    WIDE_ID(MAC_CONF_GROUP,
35 						    MAC_CONFIG_CMD), 1);
36 
37 	if (WARN_ON(cmd_ver > 3))
38 		return;
39 
40 	cmd->id_and_color = cpu_to_le32(mvmvif->id);
41 	cmd->action = cpu_to_le32(action);
42 
43 	cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif));
44 
45 	memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN);
46 
47 	cmd->wifi_gen_v2.he_support = 0;
48 	cmd->wifi_gen_v2.eht_support = 0;
49 
50 	/* should be set by specific context type handler */
51 	cmd->filter_flags = 0;
52 
53 	cmd->nic_not_ack_enabled =
54 		cpu_to_le32(!iwl_mvm_is_nic_ack_enabled(mvm, vif));
55 
56 	if (iwlwifi_mod_params.disable_11ax)
57 		return;
58 
59 	/* If we have MLO enabled, then the firmware needs to enable
60 	 * address translation for the station(s) we add. That depends
61 	 * on having EHT enabled in firmware, which in turn depends on
62 	 * mac80211 in the code below.
63 	 * However, mac80211 doesn't enable HE/EHT until it has parsed
64 	 * the association response successfully, so just skip all that
65 	 * and enable both when we have MLO.
66 	 */
67 	if (ieee80211_vif_is_mld(vif)) {
68 		iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver);
69 		if (cmd_ver == 2)
70 			cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);
71 		else
72 			cmd->wifi_gen.eht_support = 1;
73 		return;
74 	}
75 
76 	rcu_read_lock();
77 	for (link_id = 0; link_id < ARRAY_SIZE((vif)->link_conf); link_id++) {
78 		link_conf = rcu_dereference(vif->link_conf[link_id]);
79 		if (!link_conf)
80 			continue;
81 
82 		if (link_conf->he_support)
83 			iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver);
84 
85 		/* It's not reasonable to have EHT without HE and FW API doesn't
86 		 * support it. Ignore EHT in this case.
87 		 */
88 		if (!link_conf->he_support && link_conf->eht_support)
89 			continue;
90 
91 		if (link_conf->eht_support) {
92 			if (cmd_ver == 2)
93 				cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);
94 			else
95 				cmd->wifi_gen.eht_support = 1;
96 			break;
97 		}
98 	}
99 	rcu_read_unlock();
100 }
101 
iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm * mvm,struct iwl_mac_config_cmd * cmd)102 static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
103 					 struct iwl_mac_config_cmd *cmd)
104 {
105 	int ret = iwl_mvm_send_cmd_pdu(mvm,
106 				       WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
107 				       0, sizeof(*cmd), cmd);
108 	if (ret)
109 		IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n",
110 			le32_to_cpu(cmd->action), ret);
111 	return ret;
112 }
113 
iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u32 action,bool force_assoc_off)114 static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
115 					struct ieee80211_vif *vif,
116 					u32 action, bool force_assoc_off)
117 {
118 	struct iwl_mac_config_cmd cmd = {};
119 	u16 esr_transition_timeout;
120 
121 	WARN_ON(vif->type != NL80211_IFTYPE_STATION);
122 
123 	/* Fill the common data for all mac context types */
124 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
125 
126 	/*
127 	 * We always want to hear MCAST frames, if we're not authorized yet,
128 	 * we'll drop them.
129 	 */
130 	cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP);
131 
132 	if (vif->p2p)
133 		cmd.client.ctwin =
134 			iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif);
135 
136 	if (vif->cfg.assoc && !force_assoc_off) {
137 		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
138 
139 		cmd.client.is_assoc = 1;
140 
141 		if (!mvmvif->authorized &&
142 		    fw_has_capa(&mvm->fw->ucode_capa,
143 				IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO))
144 			cmd.client.data_policy |=
145 				cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE);
146 
147 	} else {
148 		cmd.client.is_assoc = 0;
149 
150 		/* Allow beacons to pass through as long as we are not
151 		 * associated, or we do not have dtim period information.
152 		 */
153 		cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
154 	}
155 
156 	cmd.client.assoc_id = cpu_to_le16(vif->cfg.aid);
157 	if (ieee80211_vif_is_mld(vif)) {
158 		esr_transition_timeout =
159 			u16_get_bits(vif->cfg.eml_cap,
160 				     IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
161 
162 		cmd.client.esr_transition_timeout =
163 			min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU,
164 			      esr_transition_timeout);
165 		cmd.client.medium_sync_delay =
166 			cpu_to_le16(vif->cfg.eml_med_sync_delay);
167 	}
168 
169 	if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p)
170 		cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
171 
172 	if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)
173 		cmd.client.data_policy |=
174 			cpu_to_le16(iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif));
175 
176 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
177 }
178 
iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u32 action)179 static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
180 					     struct ieee80211_vif *vif,
181 					     u32 action)
182 {
183 	struct iwl_mac_config_cmd cmd = {};
184 
185 	WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
186 
187 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
188 
189 	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC |
190 				       MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT |
191 				       MAC_CFG_FILTER_ACCEPT_BEACON |
192 				       MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
193 				       MAC_CFG_FILTER_ACCEPT_GRP);
194 
195 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
196 }
197 
iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u32 action)198 static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
199 					 struct ieee80211_vif *vif,
200 					 u32 action)
201 {
202 	struct iwl_mac_config_cmd cmd = {};
203 
204 	WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
205 
206 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
207 
208 	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON |
209 				       MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
210 				       MAC_CFG_FILTER_ACCEPT_GRP);
211 
212 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
213 }
214 
iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u32 action)215 static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
216 					       struct ieee80211_vif *vif,
217 					       u32 action)
218 {
219 	struct iwl_mac_config_cmd cmd = {};
220 
221 	WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
222 
223 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
224 
225 	cmd.p2p_dev.is_disc_extended =
226 		iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif);
227 
228 	/* Override the filter flags to accept all management frames. This is
229 	 * needed to support both P2P device discovery using probe requests and
230 	 * P2P service discovery using action frames
231 	 */
232 	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT);
233 
234 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
235 }
236 
iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u32 action)237 static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm,
238 					  struct ieee80211_vif *vif,
239 					  u32 action)
240 {
241 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
242 	struct iwl_mac_config_cmd cmd = {};
243 
244 	WARN_ON(vif->type != NL80211_IFTYPE_AP);
245 
246 	/* Fill the common data for all mac context types */
247 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
248 
249 	iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif,
250 						 &cmd.filter_flags,
251 						 MAC_CFG_FILTER_ACCEPT_PROBE_REQ,
252 						 MAC_CFG_FILTER_ACCEPT_BEACON);
253 
254 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
255 }
256 
iwl_mvm_mld_mac_ctx_send(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u32 action,bool force_assoc_off)257 static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm,
258 				    struct ieee80211_vif *vif,
259 				    u32 action, bool force_assoc_off)
260 {
261 	switch (vif->type) {
262 	case NL80211_IFTYPE_STATION:
263 		return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action,
264 						    force_assoc_off);
265 	case NL80211_IFTYPE_AP:
266 		return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action);
267 	case NL80211_IFTYPE_MONITOR:
268 		return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action);
269 	case NL80211_IFTYPE_P2P_DEVICE:
270 		return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action);
271 	case NL80211_IFTYPE_ADHOC:
272 		return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action);
273 	default:
274 		break;
275 	}
276 
277 	return -EOPNOTSUPP;
278 }
279 
iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm * mvm,struct ieee80211_vif * vif)280 int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
281 {
282 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
283 	int ret;
284 
285 	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
286 		return -EOPNOTSUPP;
287 
288 	if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
289 		      vif->addr, ieee80211_vif_type_p2p(vif)))
290 		return -EIO;
291 
292 	ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
293 				       true);
294 	if (ret)
295 		return ret;
296 
297 	/* will only do anything at resume from D3 time */
298 	iwl_mvm_set_last_nonqos_seq(mvm, vif);
299 
300 	mvmvif->uploaded = true;
301 	return 0;
302 }
303 
iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm * mvm,struct ieee80211_vif * vif,bool force_assoc_off)304 int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm,
305 				 struct ieee80211_vif *vif,
306 				 bool force_assoc_off)
307 {
308 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
309 
310 	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
311 		return -EOPNOTSUPP;
312 
313 	if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
314 		      vif->addr, ieee80211_vif_type_p2p(vif)))
315 		return -EIO;
316 
317 	return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
318 					force_assoc_off);
319 }
320 
iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm * mvm,struct ieee80211_vif * vif)321 int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
322 {
323 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
324 	struct iwl_mac_config_cmd cmd = {
325 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
326 		.id_and_color = cpu_to_le32(mvmvif->id),
327 	};
328 	int ret;
329 
330 	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
331 		return -EOPNOTSUPP;
332 
333 	if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
334 		      vif->addr, ieee80211_vif_type_p2p(vif)))
335 		return -EIO;
336 
337 	ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
338 	if (ret)
339 		return ret;
340 
341 	mvmvif->uploaded = false;
342 
343 	return 0;
344 }
345