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