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