1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2024-2025 Intel Corporation
4 */
5 #include <net/cfg80211.h>
6
7 #include "iface.h"
8 #include "hcmd.h"
9 #include "key.h"
10 #include "mlo.h"
11 #include "mac80211.h"
12
13 #include "fw/api/context.h"
14 #include "fw/api/mac.h"
15 #include "fw/api/time-event.h"
16 #include "fw/api/datapath.h"
17
18 /* Cleanup function for struct iwl_mld_vif, will be called in restart */
iwl_mld_cleanup_vif(void * data,u8 * mac,struct ieee80211_vif * vif)19 void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
20 {
21 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
22 struct iwl_mld *mld = mld_vif->mld;
23 struct iwl_mld_link *link;
24
25 /* EMLSR is turned back on during recovery */
26 vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE;
27
28 mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
29
30 for_each_mld_vif_valid_link(mld_vif, link) {
31 iwl_mld_cleanup_link(mld_vif->mld, link);
32
33 /* Correctly allocated primary link in non-MLO mode */
34 if (!ieee80211_vif_is_mld(vif) &&
35 link_id == 0 && link == &mld_vif->deflink)
36 continue;
37
38 if (vif->active_links & BIT(link_id))
39 continue;
40
41 /* Should not happen as link removal should always succeed */
42 WARN_ON(1);
43 if (link != &mld_vif->deflink)
44 kfree_rcu(link, rcu_head);
45 RCU_INIT_POINTER(mld_vif->link[link_id], NULL);
46 }
47
48 ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
49
50 CLEANUP_STRUCT(mld_vif);
51 }
52
iwl_mld_send_mac_cmd(struct iwl_mld * mld,struct iwl_mac_config_cmd * cmd)53 static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
54 struct iwl_mac_config_cmd *cmd)
55 {
56 int ret;
57
58 lockdep_assert_wiphy(mld->wiphy);
59
60 ret = iwl_mld_send_cmd_pdu(mld,
61 WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
62 cmd);
63 if (ret)
64 IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret);
65
66 return ret;
67 }
68
iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif * vif)69 int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
70 {
71 switch (vif->type) {
72 case NL80211_IFTYPE_STATION:
73 return vif->p2p ? FW_MAC_TYPE_P2P_STA : FW_MAC_TYPE_BSS_STA;
74 case NL80211_IFTYPE_AP:
75 return FW_MAC_TYPE_GO;
76 case NL80211_IFTYPE_MONITOR:
77 return FW_MAC_TYPE_LISTENER;
78 case NL80211_IFTYPE_P2P_DEVICE:
79 return FW_MAC_TYPE_P2P_DEVICE;
80 case NL80211_IFTYPE_ADHOC:
81 return FW_MAC_TYPE_IBSS;
82 default:
83 WARN_ON_ONCE(1);
84 }
85 return FW_MAC_TYPE_BSS_STA;
86 }
87
iwl_mld_is_nic_ack_enabled(struct iwl_mld * mld,struct ieee80211_vif * vif)88 static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
89 struct ieee80211_vif *vif)
90 {
91 const struct ieee80211_supported_band *sband;
92 const struct ieee80211_sta_he_cap *own_he_cap;
93
94 lockdep_assert_wiphy(mld->wiphy);
95
96 /* This capability is the same for all bands,
97 * so take it from one of them.
98 */
99 sband = mld->hw->wiphy->bands[NL80211_BAND_2GHZ];
100 own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
101
102 return own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] &
103 IEEE80211_HE_MAC_CAP2_ACK_EN);
104 }
105
106 /* fill the common part for all interface types */
iwl_mld_mac_cmd_fill_common(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mac_config_cmd * cmd,u32 action)107 static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
108 struct ieee80211_vif *vif,
109 struct iwl_mac_config_cmd *cmd,
110 u32 action)
111 {
112 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
113 struct ieee80211_bss_conf *link_conf;
114 unsigned int link_id;
115
116 lockdep_assert_wiphy(mld->wiphy);
117
118 cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);
119 cmd->action = cpu_to_le32(action);
120
121 cmd->mac_type =
122 cpu_to_le32(iwl_mld_mac80211_iftype_to_fw(vif));
123
124 memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN);
125
126 if (iwlwifi_mod_params.disable_11ax)
127 return;
128
129 cmd->nic_not_ack_enabled =
130 cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));
131
132 /* If we have MLO enabled, then the firmware needs to enable
133 * address translation for the station(s) we add. That depends
134 * on having EHT enabled in firmware, which in turn depends on
135 * mac80211 in the code below.
136 * However, mac80211 doesn't enable HE/EHT until it has parsed
137 * the association response successfully, so just skip all that
138 * and enable both when we have MLO.
139 */
140 if (ieee80211_vif_is_mld(vif)) {
141 if (vif->type == NL80211_IFTYPE_AP)
142 cmd->he_ap_support = cpu_to_le16(1);
143 else
144 cmd->he_support = cpu_to_le16(1);
145
146 cmd->eht_support = cpu_to_le32(1);
147 return;
148 }
149
150 for_each_vif_active_link(vif, link_conf, link_id) {
151 if (!link_conf->he_support)
152 continue;
153
154 if (vif->type == NL80211_IFTYPE_AP)
155 cmd->he_ap_support = cpu_to_le16(1);
156 else
157 cmd->he_support = cpu_to_le16(1);
158
159 /* EHT, if supported, was already set above */
160 break;
161 }
162 }
163
iwl_mld_fill_mac_cmd_sta(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 action,struct iwl_mac_config_cmd * cmd)164 static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
165 struct ieee80211_vif *vif, u32 action,
166 struct iwl_mac_config_cmd *cmd)
167 {
168 struct ieee80211_bss_conf *link;
169 u32 twt_policy = 0;
170 int link_id;
171
172 lockdep_assert_wiphy(mld->wiphy);
173
174 WARN_ON(vif->type != NL80211_IFTYPE_STATION);
175
176 /* We always want to hear MCAST frames, if we're not authorized yet,
177 * we'll drop them.
178 */
179 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP);
180
181 /* Adding a MAC ctxt with is_assoc set is not allowed in fw
182 * (and shouldn't happen)
183 */
184 if (vif->cfg.assoc && action != FW_CTXT_ACTION_ADD) {
185 cmd->client.is_assoc = 1;
186
187 if (!iwl_mld_vif_from_mac80211(vif)->authorized)
188 cmd->client.data_policy |=
189 cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE);
190 } else {
191 /* Allow beacons to pass through as long as we are not
192 * associated
193 */
194 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
195 }
196
197 cmd->client.assoc_id = cpu_to_le16(vif->cfg.aid);
198
199 if (ieee80211_vif_is_mld(vif)) {
200 u16 esr_transition_timeout =
201 u16_get_bits(vif->cfg.eml_cap,
202 IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
203
204 cmd->client.esr_transition_timeout =
205 min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU,
206 esr_transition_timeout);
207 cmd->client.medium_sync_delay =
208 cpu_to_le16(vif->cfg.eml_med_sync_delay);
209 }
210
211 for_each_vif_active_link(vif, link, link_id) {
212 if (!link->he_support)
213 continue;
214
215 if (link->twt_requester)
216 twt_policy |= TWT_SUPPORTED;
217 if (link->twt_protected)
218 twt_policy |= PROTECTED_TWT_SUPPORTED;
219 if (link->twt_broadcast)
220 twt_policy |= BROADCAST_TWT_SUPPORTED;
221 }
222
223 if (!iwlwifi_mod_params.disable_11ax)
224 cmd->client.data_policy |= cpu_to_le16(twt_policy);
225
226 if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p)
227 cmd->filter_flags |=
228 cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
229
230 if (vif->p2p)
231 cmd->client.ctwin =
232 cpu_to_le32(vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
233 IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
234 }
235
iwl_mld_fill_mac_cmd_ap(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mac_config_cmd * cmd)236 static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld,
237 struct ieee80211_vif *vif,
238 struct iwl_mac_config_cmd *cmd)
239 {
240 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
241
242 lockdep_assert_wiphy(mld->wiphy);
243
244 WARN_ON(vif->type != NL80211_IFTYPE_AP);
245
246 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
247
248 /* in AP mode, pass beacons from other APs (needed for ht protection).
249 * When there're no any associated station, which means that we are not
250 * TXing anyway, don't ask FW to pass beacons to prevent unnecessary
251 * wake-ups.
252 */
253 if (mld_vif->num_associated_stas)
254 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
255 }
256
iwl_mld_go_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)257 static void iwl_mld_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
258 {
259 bool *go_active = _data;
260
261 if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO &&
262 iwl_mld_vif_from_mac80211(vif)->ap_ibss_active)
263 *go_active = true;
264 }
265
iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld * mld)266 static bool iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld *mld)
267 {
268 bool go_active = false;
269
270 /* This flag should be set to true when the P2P Device is
271 * discoverable and there is at least a P2P GO. Setting
272 * this flag will allow the P2P Device to be discoverable on other
273 * channels in addition to its listen channel.
274 * Note that this flag should not be set in other cases as it opens the
275 * Rx filters on all MAC and increases the number of interrupts.
276 */
277 ieee80211_iterate_active_interfaces(mld->hw,
278 IEEE80211_IFACE_ITER_RESUME_ALL,
279 iwl_mld_go_iterator, &go_active);
280
281 return go_active;
282 }
283
iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mac_config_cmd * cmd)284 static void iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld *mld,
285 struct ieee80211_vif *vif,
286 struct iwl_mac_config_cmd *cmd)
287 {
288 bool ext_disc = iwl_mld_p2p_dev_has_extended_disc(mld);
289
290 lockdep_assert_wiphy(mld->wiphy);
291
292 /* Override the filter flags to accept all management frames. This is
293 * needed to support both P2P device discovery using probe requests and
294 * P2P service discovery using action frames
295 */
296 cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT);
297
298 if (ext_disc)
299 cmd->p2p_dev.is_disc_extended = cpu_to_le32(1);
300 }
301
iwl_mld_fill_mac_cmd_ibss(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mac_config_cmd * cmd)302 static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld,
303 struct ieee80211_vif *vif,
304 struct iwl_mac_config_cmd *cmd)
305 {
306 lockdep_assert_wiphy(mld->wiphy);
307
308 WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
309
310 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON |
311 MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
312 MAC_CFG_FILTER_ACCEPT_GRP);
313 }
314
315 static int
iwl_mld_rm_mac_from_fw(struct iwl_mld * mld,struct ieee80211_vif * vif)316 iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)
317 {
318 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
319 struct iwl_mac_config_cmd cmd = {
320 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
321 .id_and_color = cpu_to_le32(mld_vif->fw_id),
322 };
323
324 return iwl_mld_send_mac_cmd(mld, &cmd);
325 }
326
iwl_mld_mac_fw_action(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 action)327 int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
328 u32 action)
329 {
330 struct iwl_mac_config_cmd cmd = {};
331
332 lockdep_assert_wiphy(mld->wiphy);
333
334 if (action == FW_CTXT_ACTION_REMOVE)
335 return iwl_mld_rm_mac_from_fw(mld, vif);
336
337 iwl_mld_mac_cmd_fill_common(mld, vif, &cmd, action);
338
339 switch (vif->type) {
340 case NL80211_IFTYPE_STATION:
341 iwl_mld_fill_mac_cmd_sta(mld, vif, action, &cmd);
342 break;
343 case NL80211_IFTYPE_AP:
344 iwl_mld_fill_mac_cmd_ap(mld, vif, &cmd);
345 break;
346 case NL80211_IFTYPE_MONITOR:
347 cmd.filter_flags =
348 cpu_to_le32(MAC_CFG_FILTER_PROMISC |
349 MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT |
350 MAC_CFG_FILTER_ACCEPT_BEACON |
351 MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
352 MAC_CFG_FILTER_ACCEPT_GRP);
353 break;
354 case NL80211_IFTYPE_P2P_DEVICE:
355 iwl_mld_fill_mac_cmd_p2p_dev(mld, vif, &cmd);
356 break;
357 case NL80211_IFTYPE_ADHOC:
358 iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd);
359 break;
360 default:
361 WARN(1, "not supported yet\n");
362 return -EOPNOTSUPP;
363 }
364
365 return iwl_mld_send_mac_cmd(mld, &cmd);
366 }
367
IWL_MLD_ALLOC_FN(vif,vif)368 IWL_MLD_ALLOC_FN(vif, vif)
369
370 /* Constructor function for struct iwl_mld_vif */
371 static int
372 iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
373 {
374 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
375 int ret;
376
377 lockdep_assert_wiphy(mld->wiphy);
378
379 mld_vif->mld = mld;
380 mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
381
382 ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
383 if (ret)
384 return ret;
385
386 if (!mld->fw_status.in_hw_restart) {
387 wiphy_work_init(&mld_vif->emlsr.unblock_tpt_wk,
388 iwl_mld_emlsr_unblock_tpt_wk);
389 wiphy_delayed_work_init(&mld_vif->emlsr.check_tpt_wk,
390 iwl_mld_emlsr_check_tpt);
391 wiphy_delayed_work_init(&mld_vif->emlsr.prevent_done_wk,
392 iwl_mld_emlsr_prevent_done_wk);
393 wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk,
394 iwl_mld_emlsr_tmp_non_bss_done_wk);
395 }
396
397 return 0;
398 }
399
iwl_mld_add_vif(struct iwl_mld * mld,struct ieee80211_vif * vif)400 int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
401 {
402 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
403 int ret;
404
405 lockdep_assert_wiphy(mld->wiphy);
406
407 ret = iwl_mld_init_vif(mld, vif);
408 if (ret)
409 return ret;
410
411 ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD);
412 if (ret)
413 RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
414
415 return ret;
416 }
417
iwl_mld_rm_vif(struct iwl_mld * mld,struct ieee80211_vif * vif)418 int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
419 {
420 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
421 int ret;
422
423 lockdep_assert_wiphy(mld->wiphy);
424
425 ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
426
427 if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
428 return -EINVAL;
429
430 RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
431
432 iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF,
433 mld_vif->fw_id);
434
435 return ret;
436 }
437
iwl_mld_set_vif_associated(struct iwl_mld * mld,struct ieee80211_vif * vif)438 void iwl_mld_set_vif_associated(struct iwl_mld *mld,
439 struct ieee80211_vif *vif)
440 {
441 struct ieee80211_bss_conf *link;
442 unsigned int link_id;
443
444 for_each_vif_active_link(vif, link, link_id) {
445 if (iwl_mld_link_set_associated(mld, vif, link))
446 IWL_ERR(mld, "failed to update link %d\n", link_id);
447 }
448
449 iwl_mld_recalc_multicast_filter(mld);
450 }
451
iwl_mld_get_fw_id_bss_bitmap_iter(void * _data,u8 * mac,struct ieee80211_vif * vif)452 static void iwl_mld_get_fw_id_bss_bitmap_iter(void *_data, u8 *mac,
453 struct ieee80211_vif *vif)
454 {
455 u8 *fw_id_bitmap = _data;
456 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
457
458 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
459 return;
460
461 *fw_id_bitmap |= BIT(mld_vif->fw_id);
462 }
463
iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld * mld)464 u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld)
465 {
466 u8 fw_id_bitmap = 0;
467
468 ieee80211_iterate_active_interfaces_mtx(mld->hw,
469 IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER,
470 iwl_mld_get_fw_id_bss_bitmap_iter,
471 &fw_id_bitmap);
472
473 return fw_id_bitmap;
474 }
475
iwl_mld_handle_probe_resp_data_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)476 void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
477 struct iwl_rx_packet *pkt)
478 {
479 const struct iwl_probe_resp_data_notif *notif = (void *)pkt->data;
480 struct iwl_probe_resp_data *old_data, *new_data;
481 struct ieee80211_vif *vif;
482 struct iwl_mld_link *mld_link;
483
484 IWL_DEBUG_INFO(mld, "Probe response data notif: noa %d, csa %d\n",
485 notif->noa_active, notif->csa_counter);
486
487 if (IWL_FW_CHECK(mld, le32_to_cpu(notif->mac_id) >=
488 ARRAY_SIZE(mld->fw_id_to_vif),
489 "mac id is invalid: %d\n",
490 le32_to_cpu(notif->mac_id)))
491 return;
492
493 vif = wiphy_dereference(mld->wiphy,
494 mld->fw_id_to_vif[le32_to_cpu(notif->mac_id)]);
495
496 /* the firmware gives us the mac_id (and not the link_id), mac80211
497 * gets a vif and not a link, bottom line, this flow is not MLD ready
498 * yet.
499 */
500 if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))
501 return;
502
503 if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA &&
504 notif->csa_counter >= 1)
505 ieee80211_beacon_set_cntdwn(vif, notif->csa_counter);
506
507 if (!vif->p2p)
508 return;
509
510 mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink;
511
512 new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
513 if (!new_data)
514 return;
515
516 memcpy(&new_data->notif, notif, sizeof(new_data->notif));
517
518 /* noa_attr contains 1 reserved byte, need to substruct it */
519 new_data->noa_len = sizeof(struct ieee80211_vendor_ie) +
520 sizeof(new_data->notif.noa_attr) - 1;
521
522 /*
523 * If it's a one time NoA, only one descriptor is needed,
524 * adjust the length according to len_low.
525 */
526 if (new_data->notif.noa_attr.len_low ==
527 sizeof(struct ieee80211_p2p_noa_desc) + 2)
528 new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc);
529
530 old_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data);
531 rcu_assign_pointer(mld_link->probe_resp_data, new_data);
532
533 if (old_data)
534 kfree_rcu(old_data, rcu_head);
535 }
536
iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)537 void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
538 struct iwl_rx_packet *pkt)
539 {
540 struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
541 struct ieee80211_vif *vif;
542
543 if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif),
544 "mac id is invalid: %d\n", notif->mac_id))
545 return;
546
547 vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]);
548
549 if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))
550 return;
551
552 IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid);
553 }
554
iwl_mld_handle_datapath_monitor_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)555 void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
556 struct iwl_rx_packet *pkt)
557 {
558 struct iwl_datapath_monitor_notif *notif = (void *)pkt->data;
559 struct ieee80211_bss_conf *link;
560 struct ieee80211_supported_band *sband;
561 const struct ieee80211_sta_he_cap *he_cap;
562 struct ieee80211_vif *vif;
563 struct iwl_mld_vif *mld_vif;
564
565 if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA))
566 return;
567
568 link = iwl_mld_fw_id_to_link_conf(mld, notif->link_id);
569 if (WARN_ON(!link))
570 return;
571
572 vif = link->vif;
573 if (WARN_ON(!vif) || vif->type != NL80211_IFTYPE_STATION ||
574 !vif->cfg.assoc)
575 return;
576
577 if (!link->chanreq.oper.chan ||
578 link->chanreq.oper.chan->band != NL80211_BAND_2GHZ ||
579 link->chanreq.oper.width < NL80211_CHAN_WIDTH_40)
580 return;
581
582 mld_vif = iwl_mld_vif_from_mac80211(vif);
583
584 /* this shouldn't happen *again*, ignore it */
585 if (mld_vif->cca_40mhz_workaround != CCA_40_MHZ_WA_NONE)
586 return;
587
588 mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RECONNECT;
589
590 /*
591 * This capability manipulation isn't really ideal, but it's the
592 * easiest choice - otherwise we'd have to do some major changes
593 * in mac80211 to support this, which isn't worth it. This does
594 * mean that userspace may have outdated information, but that's
595 * actually not an issue at all.
596 */
597 sband = mld->wiphy->bands[NL80211_BAND_2GHZ];
598
599 WARN_ON(!sband->ht_cap.ht_supported);
600 WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40));
601 sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
602
603 he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
604
605 if (he_cap) {
606 /* we know that ours is writable */
607 struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap;
608
609 WARN_ON(!he->has_he);
610 WARN_ON(!(he->he_cap_elem.phy_cap_info[0] &
611 IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G));
612 he->he_cap_elem.phy_cap_info[0] &=
613 ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
614 }
615
616 ieee80211_disconnect(vif, true);
617 }
618
iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld * mld,struct ieee80211_vif * vif)619 void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld,
620 struct ieee80211_vif *vif)
621 {
622 struct ieee80211_supported_band *sband;
623 const struct ieee80211_sta_he_cap *he_cap;
624 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
625
626 if (vif->type != NL80211_IFTYPE_STATION)
627 return;
628
629 if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_NONE)
630 return;
631
632 /* Now we are just reconnecting with the new capabilities,
633 * but remember to reset the capabilities when we disconnect for real
634 */
635 if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_RECONNECT) {
636 mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RESET;
637 return;
638 }
639
640 /* Now cca_40mhz_workaround == CCA_40_MHZ_WA_RESET */
641
642 sband = mld->wiphy->bands[NL80211_BAND_2GHZ];
643
644 sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
645
646 he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
647
648 if (he_cap) {
649 /* we know that ours is writable */
650 struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap;
651
652 he->he_cap_elem.phy_cap_info[0] |=
653 IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
654 }
655
656 mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_NONE;
657 }
658
iwl_mld_get_bss_vif(struct iwl_mld * mld)659 struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld)
660 {
661 unsigned long fw_id_bitmap = iwl_mld_get_fw_bss_vifs_ids(mld);
662 int fw_id;
663
664 if (hweight8(fw_id_bitmap) != 1)
665 return NULL;
666
667 fw_id = __ffs(fw_id_bitmap);
668
669 return wiphy_dereference(mld->wiphy,
670 mld->fw_id_to_vif[fw_id]);
671 }
672