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