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