File Index
Files and directories
Sign in
FILE

iface.c

File preview with syntax highlighting.

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