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