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