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