1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2022 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 (vif->valid_links) {
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 
100 	WARN_ON(vif->type != NL80211_IFTYPE_STATION);
101 
102 	/* Fill the common data for all mac context types */
103 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
104 
105 	/*
106 	 * We always want to hear MCAST frames, if we're not authorized yet,
107 	 * we'll drop them.
108 	 */
109 	cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP);
110 
111 	if (vif->p2p)
112 		cmd.client.ctwin =
113 			iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif);
114 
115 	if (vif->cfg.assoc && !force_assoc_off) {
116 		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
117 
118 		cmd.client.is_assoc = cpu_to_le32(1);
119 
120 		if (!mvmvif->authorized &&
121 		    fw_has_capa(&mvm->fw->ucode_capa,
122 				IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO))
123 			cmd.client.data_policy |=
124 				cpu_to_le32(COEX_HIGH_PRIORITY_ENABLE);
125 
126 	} else {
127 		cmd.client.is_assoc = cpu_to_le32(0);
128 
129 		/* Allow beacons to pass through as long as we are not
130 		 * associated, or we do not have dtim period information.
131 		 */
132 		cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
133 	}
134 
135 	cmd.client.assoc_id = cpu_to_le32(vif->cfg.aid);
136 
137 	if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p)
138 		cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
139 
140 	if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)
141 		cmd.client.data_policy |=
142 			iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif);
143 
144 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
145 }
146 
147 static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
148 					     struct ieee80211_vif *vif,
149 					     u32 action)
150 {
151 	struct iwl_mac_config_cmd cmd = {};
152 
153 	WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
154 
155 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
156 
157 	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC |
158 				       MAC_FILTER_IN_CONTROL_AND_MGMT |
159 				       MAC_CFG_FILTER_ACCEPT_BEACON |
160 				       MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
161 				       MAC_CFG_FILTER_ACCEPT_GRP);
162 
163 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
164 }
165 
166 static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
167 					 struct ieee80211_vif *vif,
168 					 u32 action)
169 {
170 	struct iwl_mac_config_cmd cmd = {};
171 
172 	WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
173 
174 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
175 
176 	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON |
177 				       MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
178 				       MAC_CFG_FILTER_ACCEPT_GRP);
179 
180 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
181 }
182 
183 static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
184 					       struct ieee80211_vif *vif,
185 					       u32 action)
186 {
187 	struct iwl_mac_config_cmd cmd = {};
188 
189 	WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
190 
191 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
192 
193 	cmd.p2p_dev.is_disc_extended =
194 		iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif);
195 
196 	/* Override the filter flags to accept only probe requests */
197 	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
198 
199 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
200 }
201 
202 static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm,
203 					  struct ieee80211_vif *vif,
204 					  u32 action)
205 {
206 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
207 	struct iwl_mac_config_cmd cmd = {};
208 
209 	WARN_ON(vif->type != NL80211_IFTYPE_AP);
210 
211 	/* Fill the common data for all mac context types */
212 	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
213 
214 	iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif,
215 						 &cmd.filter_flags,
216 						 MAC_CFG_FILTER_ACCEPT_PROBE_REQ,
217 						 MAC_CFG_FILTER_ACCEPT_BEACON);
218 
219 	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
220 }
221 
222 static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm,
223 				    struct ieee80211_vif *vif,
224 				    u32 action, bool force_assoc_off)
225 {
226 	switch (vif->type) {
227 	case NL80211_IFTYPE_STATION:
228 		return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action,
229 						    force_assoc_off);
230 	case NL80211_IFTYPE_AP:
231 		return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action);
232 	case NL80211_IFTYPE_MONITOR:
233 		return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action);
234 	case NL80211_IFTYPE_P2P_DEVICE:
235 		return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action);
236 	case NL80211_IFTYPE_ADHOC:
237 		return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action);
238 	default:
239 		break;
240 	}
241 
242 	return -EOPNOTSUPP;
243 }
244 
245 int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
246 {
247 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
248 	int ret;
249 
250 	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
251 		return -EOPNOTSUPP;
252 
253 	if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
254 		      vif->addr, ieee80211_vif_type_p2p(vif)))
255 		return -EIO;
256 
257 	ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
258 				       true);
259 	if (ret)
260 		return ret;
261 
262 	/* will only do anything at resume from D3 time */
263 	iwl_mvm_set_last_nonqos_seq(mvm, vif);
264 
265 	mvmvif->uploaded = true;
266 	return 0;
267 }
268 
269 int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm,
270 				 struct ieee80211_vif *vif,
271 				 bool force_assoc_off)
272 {
273 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
274 
275 	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
276 		return -EOPNOTSUPP;
277 
278 	if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
279 		      vif->addr, ieee80211_vif_type_p2p(vif)))
280 		return -EIO;
281 
282 	return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
283 					force_assoc_off);
284 }
285 
286 int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
287 {
288 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
289 	struct iwl_mac_config_cmd cmd = {
290 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
291 		.id_and_color = cpu_to_le32(mvmvif->id),
292 	};
293 	int ret;
294 
295 	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
296 		return -EOPNOTSUPP;
297 
298 	if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
299 		      vif->addr, ieee80211_vif_type_p2p(vif)))
300 		return -EIO;
301 
302 	ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
303 	if (ret)
304 		return ret;
305 
306 	mvmvif->uploaded = false;
307 
308 	return 0;
309 }
310