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 <linux/crc32.h>
6d1e879ecSMiri Korenblit
7d1e879ecSMiri Korenblit #include "mld.h"
8d1e879ecSMiri Korenblit #include "scan.h"
9d1e879ecSMiri Korenblit #include "hcmd.h"
10d1e879ecSMiri Korenblit #include "iface.h"
11d1e879ecSMiri Korenblit #include "phy.h"
12d1e879ecSMiri Korenblit #include "mlo.h"
13d1e879ecSMiri Korenblit
14d1e879ecSMiri Korenblit #include "fw/api/scan.h"
15d1e879ecSMiri Korenblit #include "fw/dbg.h"
16d1e879ecSMiri Korenblit
17d1e879ecSMiri Korenblit #define IWL_SCAN_DWELL_ACTIVE 10
18d1e879ecSMiri Korenblit #define IWL_SCAN_DWELL_PASSIVE 110
19d1e879ecSMiri Korenblit #define IWL_SCAN_NUM_OF_FRAGS 3
20d1e879ecSMiri Korenblit
21d1e879ecSMiri Korenblit /* adaptive dwell max budget time [TU] for full scan */
22d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300
23d1e879ecSMiri Korenblit
24d1e879ecSMiri Korenblit /* adaptive dwell max budget time [TU] for directed scan */
25d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100
26d1e879ecSMiri Korenblit
27d1e879ecSMiri Korenblit /* adaptive dwell default high band APs number */
28d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8
29d1e879ecSMiri Korenblit
30d1e879ecSMiri Korenblit /* adaptive dwell default low band APs number */
31d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2
32d1e879ecSMiri Korenblit
33d1e879ecSMiri Korenblit /* adaptive dwell default APs number for P2P social channels (1, 6, 11) */
34d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10
35d1e879ecSMiri Korenblit
36d1e879ecSMiri Korenblit /* adaptive dwell number of APs override for P2P friendly GO channels */
37d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY 10
38d1e879ecSMiri Korenblit
39d1e879ecSMiri Korenblit /* adaptive dwell number of APs override for P2P social channels */
40d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS 2
41d1e879ecSMiri Korenblit
42d1e879ecSMiri Korenblit /* adaptive dwell number of APs override mask for p2p friendly GO */
43d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT BIT(20)
44d1e879ecSMiri Korenblit
45d1e879ecSMiri Korenblit /* adaptive dwell number of APs override mask for social channels */
46d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21)
47d1e879ecSMiri Korenblit
48d1e879ecSMiri Korenblit #define SCAN_TIMEOUT_MSEC (30000 * HZ)
49d1e879ecSMiri Korenblit
50d1e879ecSMiri Korenblit /* minimal number of 2GHz and 5GHz channels in the regular scan request */
51d1e879ecSMiri Korenblit #define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4
52d1e879ecSMiri Korenblit
53d1e879ecSMiri Korenblit enum iwl_mld_scan_type {
54d1e879ecSMiri Korenblit IWL_SCAN_TYPE_NOT_SET,
55d1e879ecSMiri Korenblit IWL_SCAN_TYPE_UNASSOC,
56d1e879ecSMiri Korenblit IWL_SCAN_TYPE_WILD,
57d1e879ecSMiri Korenblit IWL_SCAN_TYPE_MILD,
58d1e879ecSMiri Korenblit IWL_SCAN_TYPE_FRAGMENTED,
59d1e879ecSMiri Korenblit IWL_SCAN_TYPE_FAST_BALANCE,
60d1e879ecSMiri Korenblit };
61d1e879ecSMiri Korenblit
62d1e879ecSMiri Korenblit struct iwl_mld_scan_timing_params {
63d1e879ecSMiri Korenblit u32 suspend_time;
64d1e879ecSMiri Korenblit u32 max_out_time;
65d1e879ecSMiri Korenblit };
66d1e879ecSMiri Korenblit
67d1e879ecSMiri Korenblit static const struct iwl_mld_scan_timing_params scan_timing[] = {
68d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_UNASSOC] = {
69d1e879ecSMiri Korenblit .suspend_time = 0,
70d1e879ecSMiri Korenblit .max_out_time = 0,
71d1e879ecSMiri Korenblit },
72d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_WILD] = {
73d1e879ecSMiri Korenblit .suspend_time = 30,
74d1e879ecSMiri Korenblit .max_out_time = 120,
75d1e879ecSMiri Korenblit },
76d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_MILD] = {
77d1e879ecSMiri Korenblit .suspend_time = 120,
78d1e879ecSMiri Korenblit .max_out_time = 120,
79d1e879ecSMiri Korenblit },
80d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_FRAGMENTED] = {
81d1e879ecSMiri Korenblit .suspend_time = 95,
82d1e879ecSMiri Korenblit .max_out_time = 44,
83d1e879ecSMiri Korenblit },
84d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_FAST_BALANCE] = {
85d1e879ecSMiri Korenblit .suspend_time = 30,
86d1e879ecSMiri Korenblit .max_out_time = 37,
87d1e879ecSMiri Korenblit },
88d1e879ecSMiri Korenblit };
89d1e879ecSMiri Korenblit
90d1e879ecSMiri Korenblit struct iwl_mld_scan_params {
91d1e879ecSMiri Korenblit enum iwl_mld_scan_type type;
92d1e879ecSMiri Korenblit u32 n_channels;
93d1e879ecSMiri Korenblit u16 delay;
94d1e879ecSMiri Korenblit int n_ssids;
95d1e879ecSMiri Korenblit struct cfg80211_ssid *ssids;
96d1e879ecSMiri Korenblit struct ieee80211_channel **channels;
97d1e879ecSMiri Korenblit u32 flags;
98d1e879ecSMiri Korenblit u8 *mac_addr;
99d1e879ecSMiri Korenblit u8 *mac_addr_mask;
100d1e879ecSMiri Korenblit bool no_cck;
101d1e879ecSMiri Korenblit bool pass_all;
102d1e879ecSMiri Korenblit int n_match_sets;
103d1e879ecSMiri Korenblit struct iwl_scan_probe_req preq;
104d1e879ecSMiri Korenblit struct cfg80211_match_set *match_sets;
105d1e879ecSMiri Korenblit int n_scan_plans;
106d1e879ecSMiri Korenblit struct cfg80211_sched_scan_plan *scan_plans;
107d1e879ecSMiri Korenblit bool iter_notif;
108d1e879ecSMiri Korenblit bool respect_p2p_go;
109d1e879ecSMiri Korenblit u8 fw_link_id;
110d1e879ecSMiri Korenblit struct cfg80211_scan_6ghz_params *scan_6ghz_params;
111d1e879ecSMiri Korenblit u32 n_6ghz_params;
112d1e879ecSMiri Korenblit bool scan_6ghz;
113d1e879ecSMiri Korenblit bool enable_6ghz_passive;
114d1e879ecSMiri Korenblit u8 bssid[ETH_ALEN] __aligned(2);
115d1e879ecSMiri Korenblit };
116d1e879ecSMiri Korenblit
117d1e879ecSMiri Korenblit struct iwl_mld_scan_respect_p2p_go_iter_data {
118d1e879ecSMiri Korenblit struct ieee80211_vif *current_vif;
119d1e879ecSMiri Korenblit bool p2p_go;
120d1e879ecSMiri Korenblit };
121d1e879ecSMiri Korenblit
iwl_mld_scan_respect_p2p_go_iter(void * _data,u8 * mac,struct ieee80211_vif * vif)122d1e879ecSMiri Korenblit static void iwl_mld_scan_respect_p2p_go_iter(void *_data, u8 *mac,
123d1e879ecSMiri Korenblit struct ieee80211_vif *vif)
124d1e879ecSMiri Korenblit {
125d1e879ecSMiri Korenblit struct iwl_mld_scan_respect_p2p_go_iter_data *data = _data;
126d1e879ecSMiri Korenblit
127d1e879ecSMiri Korenblit /* exclude the given vif */
128d1e879ecSMiri Korenblit if (vif == data->current_vif)
129d1e879ecSMiri Korenblit return;
130d1e879ecSMiri Korenblit
131d1e879ecSMiri Korenblit /* TODO: CDB check the band of the GO */
132d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO &&
133d1e879ecSMiri Korenblit iwl_mld_vif_from_mac80211(vif)->ap_ibss_active)
134d1e879ecSMiri Korenblit data->p2p_go = true;
135d1e879ecSMiri Korenblit }
136d1e879ecSMiri Korenblit
iwl_mld_get_respect_p2p_go(struct iwl_mld * mld,struct ieee80211_vif * vif,bool low_latency)137d1e879ecSMiri Korenblit static bool iwl_mld_get_respect_p2p_go(struct iwl_mld *mld,
138d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
139d1e879ecSMiri Korenblit bool low_latency)
140d1e879ecSMiri Korenblit {
141d1e879ecSMiri Korenblit struct iwl_mld_scan_respect_p2p_go_iter_data data = {
142d1e879ecSMiri Korenblit .current_vif = vif,
143d1e879ecSMiri Korenblit .p2p_go = false,
144d1e879ecSMiri Korenblit };
145d1e879ecSMiri Korenblit
146d1e879ecSMiri Korenblit if (!low_latency)
147d1e879ecSMiri Korenblit return false;
148d1e879ecSMiri Korenblit
149d1e879ecSMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw,
150d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL,
151d1e879ecSMiri Korenblit iwl_mld_scan_respect_p2p_go_iter,
152d1e879ecSMiri Korenblit &data);
153d1e879ecSMiri Korenblit
154d1e879ecSMiri Korenblit return data.p2p_go;
155d1e879ecSMiri Korenblit }
156d1e879ecSMiri Korenblit
157d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data {
158d1e879ecSMiri Korenblit struct ieee80211_vif *current_vif;
159d1e879ecSMiri Korenblit bool active_vif;
160d1e879ecSMiri Korenblit bool is_dcm_with_p2p_go;
161d1e879ecSMiri Korenblit bool global_low_latency;
162d1e879ecSMiri Korenblit };
163d1e879ecSMiri Korenblit
iwl_mld_scan_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)164d1e879ecSMiri Korenblit static void iwl_mld_scan_iterator(void *_data, u8 *mac,
165d1e879ecSMiri Korenblit struct ieee80211_vif *vif)
166d1e879ecSMiri Korenblit {
167d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data *data = _data;
168d1e879ecSMiri Korenblit struct ieee80211_vif *curr_vif = data->current_vif;
169d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
170d1e879ecSMiri Korenblit struct iwl_mld_vif *curr_mld_vif;
171d1e879ecSMiri Korenblit unsigned long curr_vif_active_links;
172d1e879ecSMiri Korenblit u16 link_id;
173d1e879ecSMiri Korenblit
174d1e879ecSMiri Korenblit data->global_low_latency |= iwl_mld_vif_low_latency(mld_vif);
175d1e879ecSMiri Korenblit
176d1e879ecSMiri Korenblit if ((ieee80211_vif_is_mld(vif) && vif->active_links) ||
177d1e879ecSMiri Korenblit (vif->type != NL80211_IFTYPE_P2P_DEVICE &&
178d1e879ecSMiri Korenblit mld_vif->deflink.active))
179d1e879ecSMiri Korenblit data->active_vif = true;
180d1e879ecSMiri Korenblit
181d1e879ecSMiri Korenblit if (vif == curr_vif)
182d1e879ecSMiri Korenblit return;
183d1e879ecSMiri Korenblit
184d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_GO)
185d1e879ecSMiri Korenblit return;
186d1e879ecSMiri Korenblit
187d1e879ecSMiri Korenblit /* Currently P2P GO can't be AP MLD so the logic below assumes that */
188d1e879ecSMiri Korenblit WARN_ON_ONCE(ieee80211_vif_is_mld(vif));
189d1e879ecSMiri Korenblit
190d1e879ecSMiri Korenblit curr_vif_active_links =
191d1e879ecSMiri Korenblit ieee80211_vif_is_mld(curr_vif) ? curr_vif->active_links : 1;
192d1e879ecSMiri Korenblit
193d1e879ecSMiri Korenblit curr_mld_vif = iwl_mld_vif_from_mac80211(curr_vif);
194d1e879ecSMiri Korenblit
195d1e879ecSMiri Korenblit for_each_set_bit(link_id, &curr_vif_active_links,
196d1e879ecSMiri Korenblit IEEE80211_MLD_MAX_NUM_LINKS) {
197d1e879ecSMiri Korenblit struct iwl_mld_link *curr_mld_link =
198d1e879ecSMiri Korenblit iwl_mld_link_dereference_check(curr_mld_vif, link_id);
199d1e879ecSMiri Korenblit
200d1e879ecSMiri Korenblit if (WARN_ON(!curr_mld_link))
201d1e879ecSMiri Korenblit return;
202d1e879ecSMiri Korenblit
203d1e879ecSMiri Korenblit if (rcu_access_pointer(curr_mld_link->chan_ctx) &&
204d1e879ecSMiri Korenblit rcu_access_pointer(mld_vif->deflink.chan_ctx) !=
205d1e879ecSMiri Korenblit rcu_access_pointer(curr_mld_link->chan_ctx)) {
206d1e879ecSMiri Korenblit data->is_dcm_with_p2p_go = true;
207d1e879ecSMiri Korenblit return;
208d1e879ecSMiri Korenblit }
209d1e879ecSMiri Korenblit }
210d1e879ecSMiri Korenblit }
211d1e879ecSMiri Korenblit
212d1e879ecSMiri Korenblit static enum
iwl_mld_get_scan_type(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_scan_iter_data * data)213d1e879ecSMiri Korenblit iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld,
214d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
215d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data *data)
216d1e879ecSMiri Korenblit {
217d1e879ecSMiri Korenblit enum iwl_mld_traffic_load load = mld->scan.traffic_load.status;
218d1e879ecSMiri Korenblit
219d1e879ecSMiri Korenblit /* A scanning AP interface probably wants to generate a survey to do
220d1e879ecSMiri Korenblit * ACS (automatic channel selection).
221d1e879ecSMiri Korenblit * Force a non-fragmented scan in that case.
222d1e879ecSMiri Korenblit */
223d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP)
224d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_WILD;
225d1e879ecSMiri Korenblit
226d1e879ecSMiri Korenblit if (!data->active_vif)
227d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_UNASSOC;
228d1e879ecSMiri Korenblit
229d1e879ecSMiri Korenblit if ((load == IWL_MLD_TRAFFIC_HIGH || data->global_low_latency) &&
230d1e879ecSMiri Korenblit vif->type != NL80211_IFTYPE_P2P_DEVICE)
231d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_FRAGMENTED;
232d1e879ecSMiri Korenblit
233d1e879ecSMiri Korenblit /* In case of DCM with P2P GO set all scan requests as
234d1e879ecSMiri Korenblit * fast-balance scan
235d1e879ecSMiri Korenblit */
236d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_STATION &&
237d1e879ecSMiri Korenblit data->is_dcm_with_p2p_go)
238d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_FAST_BALANCE;
239d1e879ecSMiri Korenblit
240d1e879ecSMiri Korenblit if (load >= IWL_MLD_TRAFFIC_MEDIUM || data->global_low_latency)
241d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_MILD;
242d1e879ecSMiri Korenblit
243d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_WILD;
244d1e879ecSMiri Korenblit }
245d1e879ecSMiri Korenblit
246d1e879ecSMiri Korenblit static u8 *
iwl_mld_scan_add_2ghz_elems(struct iwl_mld * mld,const u8 * ies,size_t len,u8 * const pos)247d1e879ecSMiri Korenblit iwl_mld_scan_add_2ghz_elems(struct iwl_mld *mld, const u8 *ies,
248d1e879ecSMiri Korenblit size_t len, u8 *const pos)
249d1e879ecSMiri Korenblit {
250d1e879ecSMiri Korenblit static const u8 before_ds_params[] = {
251d1e879ecSMiri Korenblit WLAN_EID_SSID,
252d1e879ecSMiri Korenblit WLAN_EID_SUPP_RATES,
253d1e879ecSMiri Korenblit WLAN_EID_REQUEST,
254d1e879ecSMiri Korenblit WLAN_EID_EXT_SUPP_RATES,
255d1e879ecSMiri Korenblit };
256d1e879ecSMiri Korenblit size_t offs;
257d1e879ecSMiri Korenblit u8 *newpos = pos;
258d1e879ecSMiri Korenblit
259d1e879ecSMiri Korenblit offs = ieee80211_ie_split(ies, len,
260d1e879ecSMiri Korenblit before_ds_params,
261d1e879ecSMiri Korenblit ARRAY_SIZE(before_ds_params),
262d1e879ecSMiri Korenblit 0);
263d1e879ecSMiri Korenblit
264d1e879ecSMiri Korenblit memcpy(newpos, ies, offs);
265d1e879ecSMiri Korenblit newpos += offs;
266d1e879ecSMiri Korenblit
267d1e879ecSMiri Korenblit /* Add a placeholder for DS Parameter Set element */
268d1e879ecSMiri Korenblit *newpos++ = WLAN_EID_DS_PARAMS;
269d1e879ecSMiri Korenblit *newpos++ = 1;
270d1e879ecSMiri Korenblit *newpos++ = 0;
271d1e879ecSMiri Korenblit
272d1e879ecSMiri Korenblit memcpy(newpos, ies + offs, len - offs);
273d1e879ecSMiri Korenblit newpos += len - offs;
274d1e879ecSMiri Korenblit
275d1e879ecSMiri Korenblit return newpos;
276d1e879ecSMiri Korenblit }
277d1e879ecSMiri Korenblit
278d1e879ecSMiri Korenblit static void
iwl_mld_scan_add_tpc_report_elem(u8 * pos)279d1e879ecSMiri Korenblit iwl_mld_scan_add_tpc_report_elem(u8 *pos)
280d1e879ecSMiri Korenblit {
281d1e879ecSMiri Korenblit pos[0] = WLAN_EID_VENDOR_SPECIFIC;
282d1e879ecSMiri Korenblit pos[1] = WFA_TPC_IE_LEN - 2;
283d1e879ecSMiri Korenblit pos[2] = (WLAN_OUI_MICROSOFT >> 16) & 0xff;
284d1e879ecSMiri Korenblit pos[3] = (WLAN_OUI_MICROSOFT >> 8) & 0xff;
285d1e879ecSMiri Korenblit pos[4] = WLAN_OUI_MICROSOFT & 0xff;
286d1e879ecSMiri Korenblit pos[5] = WLAN_OUI_TYPE_MICROSOFT_TPC;
287d1e879ecSMiri Korenblit pos[6] = 0;
288d1e879ecSMiri Korenblit /* pos[7] - tx power will be inserted by the FW */
289d1e879ecSMiri Korenblit pos[7] = 0;
290d1e879ecSMiri Korenblit pos[8] = 0;
291d1e879ecSMiri Korenblit }
292d1e879ecSMiri Korenblit
293d1e879ecSMiri Korenblit static u32
iwl_mld_scan_ooc_priority(enum iwl_mld_scan_status scan_status)294d1e879ecSMiri Korenblit iwl_mld_scan_ooc_priority(enum iwl_mld_scan_status scan_status)
295d1e879ecSMiri Korenblit {
296d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_REGULAR)
297d1e879ecSMiri Korenblit return IWL_SCAN_PRIORITY_EXT_6;
298d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_INT_MLO)
299d1e879ecSMiri Korenblit return IWL_SCAN_PRIORITY_EXT_4;
300d1e879ecSMiri Korenblit
301d1e879ecSMiri Korenblit return IWL_SCAN_PRIORITY_EXT_2;
302d1e879ecSMiri Korenblit }
303d1e879ecSMiri Korenblit
304d1e879ecSMiri Korenblit static bool
iwl_mld_scan_is_regular(struct iwl_mld_scan_params * params)305d1e879ecSMiri Korenblit iwl_mld_scan_is_regular(struct iwl_mld_scan_params *params)
306d1e879ecSMiri Korenblit {
307d1e879ecSMiri Korenblit return params->n_scan_plans == 1 &&
308d1e879ecSMiri Korenblit params->scan_plans[0].iterations == 1;
309d1e879ecSMiri Korenblit }
310d1e879ecSMiri Korenblit
311d1e879ecSMiri Korenblit static bool
iwl_mld_scan_is_fragmented(enum iwl_mld_scan_type type)312d1e879ecSMiri Korenblit iwl_mld_scan_is_fragmented(enum iwl_mld_scan_type type)
313d1e879ecSMiri Korenblit {
314d1e879ecSMiri Korenblit return (type == IWL_SCAN_TYPE_FRAGMENTED ||
315d1e879ecSMiri Korenblit type == IWL_SCAN_TYPE_FAST_BALANCE);
316d1e879ecSMiri Korenblit }
317d1e879ecSMiri Korenblit
318d1e879ecSMiri Korenblit static int
iwl_mld_scan_uid_by_status(struct iwl_mld * mld,int status)319d1e879ecSMiri Korenblit iwl_mld_scan_uid_by_status(struct iwl_mld *mld, int status)
320d1e879ecSMiri Korenblit {
321d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++)
322d1e879ecSMiri Korenblit if (mld->scan.uid_status[i] == status)
323d1e879ecSMiri Korenblit return i;
324d1e879ecSMiri Korenblit
325d1e879ecSMiri Korenblit return -ENOENT;
326d1e879ecSMiri Korenblit }
327d1e879ecSMiri Korenblit
328d1e879ecSMiri Korenblit static const char *
iwl_mld_scan_ebs_status_str(enum iwl_scan_ebs_status status)329d1e879ecSMiri Korenblit iwl_mld_scan_ebs_status_str(enum iwl_scan_ebs_status status)
330d1e879ecSMiri Korenblit {
331d1e879ecSMiri Korenblit switch (status) {
332d1e879ecSMiri Korenblit case IWL_SCAN_EBS_SUCCESS:
333d1e879ecSMiri Korenblit return "successful";
334d1e879ecSMiri Korenblit case IWL_SCAN_EBS_INACTIVE:
335d1e879ecSMiri Korenblit return "inactive";
336d1e879ecSMiri Korenblit case IWL_SCAN_EBS_FAILED:
337d1e879ecSMiri Korenblit case IWL_SCAN_EBS_CHAN_NOT_FOUND:
338d1e879ecSMiri Korenblit default:
339d1e879ecSMiri Korenblit return "failed";
340d1e879ecSMiri Korenblit }
341d1e879ecSMiri Korenblit }
342d1e879ecSMiri Korenblit
343d1e879ecSMiri Korenblit static int
iwl_mld_scan_ssid_exist(u8 * ssid,u8 ssid_len,struct iwl_ssid_ie * ssid_list)344d1e879ecSMiri Korenblit iwl_mld_scan_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
345d1e879ecSMiri Korenblit {
346d1e879ecSMiri Korenblit for (int i = 0; i < PROBE_OPTION_MAX; i++) {
347d1e879ecSMiri Korenblit if (!ssid_list[i].len)
348d1e879ecSMiri Korenblit return -1;
349d1e879ecSMiri Korenblit if (ssid_list[i].len == ssid_len &&
350d1e879ecSMiri Korenblit !memcmp(ssid_list[i].ssid, ssid, ssid_len))
351d1e879ecSMiri Korenblit return i;
352d1e879ecSMiri Korenblit }
353d1e879ecSMiri Korenblit
354d1e879ecSMiri Korenblit return -1;
355d1e879ecSMiri Korenblit }
356d1e879ecSMiri Korenblit
357d1e879ecSMiri Korenblit static bool
iwl_mld_scan_fits(struct iwl_mld * mld,int n_ssids,struct ieee80211_scan_ies * ies,int n_channels)358d1e879ecSMiri Korenblit iwl_mld_scan_fits(struct iwl_mld *mld, int n_ssids,
359d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies, int n_channels)
360d1e879ecSMiri Korenblit {
361d1e879ecSMiri Korenblit return ((n_ssids <= PROBE_OPTION_MAX) &&
362d1e879ecSMiri Korenblit (n_channels <= mld->fw->ucode_capa.n_scan_channels) &
363d1e879ecSMiri Korenblit (ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] +
364d1e879ecSMiri Korenblit ies->len[NL80211_BAND_5GHZ] + ies->len[NL80211_BAND_6GHZ] <=
365d1e879ecSMiri Korenblit iwl_mld_scan_max_template_size()));
366d1e879ecSMiri Korenblit }
367d1e879ecSMiri Korenblit
368d1e879ecSMiri Korenblit static void
iwl_mld_scan_build_probe_req(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_scan_ies * ies,struct iwl_mld_scan_params * params)369d1e879ecSMiri Korenblit iwl_mld_scan_build_probe_req(struct iwl_mld *mld, struct ieee80211_vif *vif,
370d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies,
371d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params)
372d1e879ecSMiri Korenblit {
373d1e879ecSMiri Korenblit struct ieee80211_mgmt *frame = (void *)params->preq.buf;
374d1e879ecSMiri Korenblit u8 *pos, *newpos;
375d1e879ecSMiri Korenblit const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
376d1e879ecSMiri Korenblit params->mac_addr : NULL;
377d1e879ecSMiri Korenblit
378d1e879ecSMiri Korenblit if (mac_addr)
379d1e879ecSMiri Korenblit get_random_mask_addr(frame->sa, mac_addr,
380d1e879ecSMiri Korenblit params->mac_addr_mask);
381d1e879ecSMiri Korenblit else
382d1e879ecSMiri Korenblit memcpy(frame->sa, vif->addr, ETH_ALEN);
383d1e879ecSMiri Korenblit
384d1e879ecSMiri Korenblit frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
385d1e879ecSMiri Korenblit eth_broadcast_addr(frame->da);
386d1e879ecSMiri Korenblit ether_addr_copy(frame->bssid, params->bssid);
387d1e879ecSMiri Korenblit frame->seq_ctrl = 0;
388d1e879ecSMiri Korenblit
389d1e879ecSMiri Korenblit pos = frame->u.probe_req.variable;
390d1e879ecSMiri Korenblit *pos++ = WLAN_EID_SSID;
391d1e879ecSMiri Korenblit *pos++ = 0;
392d1e879ecSMiri Korenblit
393d1e879ecSMiri Korenblit params->preq.mac_header.offset = 0;
394d1e879ecSMiri Korenblit params->preq.mac_header.len = cpu_to_le16(24 + 2);
395d1e879ecSMiri Korenblit
396d1e879ecSMiri Korenblit /* Insert DS parameter set element on 2.4 GHz band */
397d1e879ecSMiri Korenblit newpos = iwl_mld_scan_add_2ghz_elems(mld,
398d1e879ecSMiri Korenblit ies->ies[NL80211_BAND_2GHZ],
399d1e879ecSMiri Korenblit ies->len[NL80211_BAND_2GHZ],
400d1e879ecSMiri Korenblit pos);
401d1e879ecSMiri Korenblit params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf);
402d1e879ecSMiri Korenblit params->preq.band_data[0].len = cpu_to_le16(newpos - pos);
403d1e879ecSMiri Korenblit pos = newpos;
404d1e879ecSMiri Korenblit
405d1e879ecSMiri Korenblit memcpy(pos, ies->ies[NL80211_BAND_5GHZ],
406d1e879ecSMiri Korenblit ies->len[NL80211_BAND_5GHZ]);
407d1e879ecSMiri Korenblit params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf);
408d1e879ecSMiri Korenblit params->preq.band_data[1].len =
409d1e879ecSMiri Korenblit cpu_to_le16(ies->len[NL80211_BAND_5GHZ]);
410d1e879ecSMiri Korenblit pos += ies->len[NL80211_BAND_5GHZ];
411d1e879ecSMiri Korenblit
412d1e879ecSMiri Korenblit memcpy(pos, ies->ies[NL80211_BAND_6GHZ],
413d1e879ecSMiri Korenblit ies->len[NL80211_BAND_6GHZ]);
414d1e879ecSMiri Korenblit params->preq.band_data[2].offset = cpu_to_le16(pos - params->preq.buf);
415d1e879ecSMiri Korenblit params->preq.band_data[2].len =
416d1e879ecSMiri Korenblit cpu_to_le16(ies->len[NL80211_BAND_6GHZ]);
417d1e879ecSMiri Korenblit pos += ies->len[NL80211_BAND_6GHZ];
418d1e879ecSMiri Korenblit
419d1e879ecSMiri Korenblit memcpy(pos, ies->common_ies, ies->common_ie_len);
420d1e879ecSMiri Korenblit params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf);
421d1e879ecSMiri Korenblit
422d1e879ecSMiri Korenblit iwl_mld_scan_add_tpc_report_elem(pos + ies->common_ie_len);
423d1e879ecSMiri Korenblit params->preq.common_data.len = cpu_to_le16(ies->common_ie_len +
424d1e879ecSMiri Korenblit WFA_TPC_IE_LEN);
425d1e879ecSMiri Korenblit }
426d1e879ecSMiri Korenblit
427d1e879ecSMiri Korenblit static u16
iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif,enum iwl_mld_scan_status scan_status)428d1e879ecSMiri Korenblit iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
429d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
430d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
431d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status)
432d1e879ecSMiri Korenblit {
433d1e879ecSMiri Korenblit u16 flags = 0;
434d1e879ecSMiri Korenblit
435d1e879ecSMiri Korenblit /* If no direct SSIDs are provided perform a passive scan. Otherwise,
436d1e879ecSMiri Korenblit * if there is a single SSID which is not the broadcast SSID, assume
437d1e879ecSMiri Korenblit * that the scan is intended for roaming purposes and thus enable Rx on
438d1e879ecSMiri Korenblit * all chains to improve chances of hearing the beacons/probe responses.
439d1e879ecSMiri Korenblit */
440d1e879ecSMiri Korenblit if (params->n_ssids == 0)
441d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE;
442d1e879ecSMiri Korenblit else if (params->n_ssids == 1 && params->ssids[0].ssid_len)
443d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_USE_ALL_RX_CHAINS;
444d1e879ecSMiri Korenblit
445d1e879ecSMiri Korenblit if (params->pass_all)
446d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL;
447d1e879ecSMiri Korenblit else
448d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH;
449d1e879ecSMiri Korenblit
450d1e879ecSMiri Korenblit if (iwl_mld_scan_is_fragmented(params->type))
451d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1;
452d1e879ecSMiri Korenblit
453d1e879ecSMiri Korenblit if (!iwl_mld_scan_is_regular(params))
454d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC;
455d1e879ecSMiri Korenblit
456d1e879ecSMiri Korenblit if (params->iter_notif ||
457d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED)
458d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE;
459d1e879ecSMiri Korenblit
460d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_SCHED ||
461d1e879ecSMiri Korenblit scan_status == IWL_MLD_SCAN_NETDETECT)
462d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE;
463d1e879ecSMiri Korenblit
464d1e879ecSMiri Korenblit if (params->flags & (NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP |
465d1e879ecSMiri Korenblit NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE |
466d1e879ecSMiri Korenblit NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME))
467d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_OCE;
468d1e879ecSMiri Korenblit
469d1e879ecSMiri Korenblit if ((scan_status == IWL_MLD_SCAN_SCHED ||
470d1e879ecSMiri Korenblit scan_status == IWL_MLD_SCAN_NETDETECT) &&
471d1e879ecSMiri Korenblit params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
472d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN;
473d1e879ecSMiri Korenblit
474d1e879ecSMiri Korenblit if (params->enable_6ghz_passive)
475d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN;
476d1e879ecSMiri Korenblit
477d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL;
478d1e879ecSMiri Korenblit
479d1e879ecSMiri Korenblit return flags;
480d1e879ecSMiri Korenblit }
481d1e879ecSMiri Korenblit
482d1e879ecSMiri Korenblit static u8
iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif,u16 gen_flags)483d1e879ecSMiri Korenblit iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
484d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
485d1e879ecSMiri Korenblit struct ieee80211_vif *vif, u16 gen_flags)
486d1e879ecSMiri Korenblit {
487d1e879ecSMiri Korenblit u8 flags = 0;
488d1e879ecSMiri Korenblit
489d1e879ecSMiri Korenblit /* TODO: CDB */
490d1e879ecSMiri Korenblit if (params->respect_p2p_go)
491d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB |
492d1e879ecSMiri Korenblit IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB;
493d1e879ecSMiri Korenblit
494d1e879ecSMiri Korenblit if (params->scan_6ghz)
495d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT;
496d1e879ecSMiri Korenblit
497d1e879ecSMiri Korenblit return flags;
498d1e879ecSMiri Korenblit }
499d1e879ecSMiri Korenblit
500d1e879ecSMiri Korenblit static void
iwl_mld_scan_cmd_set_dwell(struct iwl_mld * mld,struct iwl_scan_general_params_v11 * gp,struct iwl_mld_scan_params * params)501d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld,
502d1e879ecSMiri Korenblit struct iwl_scan_general_params_v11 *gp,
503d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params)
504d1e879ecSMiri Korenblit {
505d1e879ecSMiri Korenblit const struct iwl_mld_scan_timing_params *timing =
506d1e879ecSMiri Korenblit &scan_timing[params->type];
507d1e879ecSMiri Korenblit
508d1e879ecSMiri Korenblit gp->adwell_default_social_chn =
509d1e879ecSMiri Korenblit IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL;
510d1e879ecSMiri Korenblit gp->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS;
511d1e879ecSMiri Korenblit gp->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS;
512d1e879ecSMiri Korenblit
513d1e879ecSMiri Korenblit if (params->n_ssids && params->ssids[0].ssid_len)
514d1e879ecSMiri Korenblit gp->adwell_max_budget =
515d1e879ecSMiri Korenblit cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN);
516d1e879ecSMiri Korenblit else
517d1e879ecSMiri Korenblit gp->adwell_max_budget =
518d1e879ecSMiri Korenblit cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN);
519d1e879ecSMiri Korenblit
520d1e879ecSMiri Korenblit gp->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
521d1e879ecSMiri Korenblit
522d1e879ecSMiri Korenblit gp->max_out_of_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->max_out_time);
523d1e879ecSMiri Korenblit gp->suspend_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->suspend_time);
524d1e879ecSMiri Korenblit
525d1e879ecSMiri Korenblit gp->active_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE;
526d1e879ecSMiri Korenblit gp->passive_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE;
527d1e879ecSMiri Korenblit gp->active_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE;
528d1e879ecSMiri Korenblit gp->passive_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE;
529d1e879ecSMiri Korenblit
530d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
531d1e879ecSMiri Korenblit "Scan: adwell_max_budget=%d max_out_of_time=%d suspend_time=%d\n",
532d1e879ecSMiri Korenblit gp->adwell_max_budget,
533d1e879ecSMiri Korenblit gp->max_out_of_time[SCAN_LB_LMAC_IDX],
534d1e879ecSMiri Korenblit gp->suspend_time[SCAN_LB_LMAC_IDX]);
535d1e879ecSMiri Korenblit }
536d1e879ecSMiri Korenblit
537d1e879ecSMiri Korenblit static void
iwl_mld_scan_cmd_set_gen_params(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif,struct iwl_scan_general_params_v11 * gp,enum iwl_mld_scan_status scan_status)538d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
539d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
540d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
541d1e879ecSMiri Korenblit struct iwl_scan_general_params_v11 *gp,
542d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status)
543d1e879ecSMiri Korenblit {
544d1e879ecSMiri Korenblit u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
545d1e879ecSMiri Korenblit scan_status);
546d1e879ecSMiri Korenblit u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif,
547d1e879ecSMiri Korenblit gen_flags);
548d1e879ecSMiri Korenblit
549d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n",
550d1e879ecSMiri Korenblit gen_flags, gen_flags2);
551d1e879ecSMiri Korenblit
552d1e879ecSMiri Korenblit gp->flags = cpu_to_le16(gen_flags);
553d1e879ecSMiri Korenblit gp->flags2 = gen_flags2;
554d1e879ecSMiri Korenblit
555d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_dwell(mld, gp, params);
556d1e879ecSMiri Korenblit
557d1e879ecSMiri Korenblit if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1)
558d1e879ecSMiri Korenblit gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS;
559d1e879ecSMiri Korenblit
560d1e879ecSMiri Korenblit if (params->fw_link_id != IWL_MLD_INVALID_FW_ID)
561d1e879ecSMiri Korenblit gp->scan_start_mac_or_link_id = params->fw_link_id;
562d1e879ecSMiri Korenblit }
563d1e879ecSMiri Korenblit
564d1e879ecSMiri Korenblit static int
iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params * params,struct iwl_scan_umac_schedule * schedule,__le16 * delay)565d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params,
566d1e879ecSMiri Korenblit struct iwl_scan_umac_schedule *schedule,
567d1e879ecSMiri Korenblit __le16 *delay)
568d1e879ecSMiri Korenblit {
569d1e879ecSMiri Korenblit if (WARN_ON(!params->n_scan_plans ||
570d1e879ecSMiri Korenblit params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
571d1e879ecSMiri Korenblit return -EINVAL;
572d1e879ecSMiri Korenblit
573d1e879ecSMiri Korenblit for (int i = 0; i < params->n_scan_plans; i++) {
574d1e879ecSMiri Korenblit struct cfg80211_sched_scan_plan *scan_plan =
575d1e879ecSMiri Korenblit ¶ms->scan_plans[i];
576d1e879ecSMiri Korenblit
577d1e879ecSMiri Korenblit schedule[i].iter_count = scan_plan->iterations;
578d1e879ecSMiri Korenblit schedule[i].interval =
579d1e879ecSMiri Korenblit cpu_to_le16(scan_plan->interval);
580d1e879ecSMiri Korenblit }
581d1e879ecSMiri Korenblit
582d1e879ecSMiri Korenblit /* If the number of iterations of the last scan plan is set to zero,
583d1e879ecSMiri Korenblit * it should run infinitely. However, this is not always the case.
584d1e879ecSMiri Korenblit * For example, when regular scan is requested the driver sets one scan
585d1e879ecSMiri Korenblit * plan with one iteration.
586d1e879ecSMiri Korenblit */
587d1e879ecSMiri Korenblit if (!schedule[params->n_scan_plans - 1].iter_count)
588d1e879ecSMiri Korenblit schedule[params->n_scan_plans - 1].iter_count = 0xff;
589d1e879ecSMiri Korenblit
590d1e879ecSMiri Korenblit *delay = cpu_to_le16(params->delay);
591d1e879ecSMiri Korenblit
592d1e879ecSMiri Korenblit return 0;
593d1e879ecSMiri Korenblit }
594d1e879ecSMiri Korenblit
595d1e879ecSMiri Korenblit /* We insert the SSIDs in an inverted order, because the FW will
596d1e879ecSMiri Korenblit * invert it back.
597d1e879ecSMiri Korenblit */
598d1e879ecSMiri Korenblit static void
iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params * params,struct iwl_ssid_ie * ssids,u32 * ssid_bitmap)599d1e879ecSMiri Korenblit iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params,
600d1e879ecSMiri Korenblit struct iwl_ssid_ie *ssids, u32 *ssid_bitmap)
601d1e879ecSMiri Korenblit {
602d1e879ecSMiri Korenblit int i, j;
603d1e879ecSMiri Korenblit int index;
604d1e879ecSMiri Korenblit u32 tmp_bitmap = 0;
605d1e879ecSMiri Korenblit
606d1e879ecSMiri Korenblit /* copy SSIDs from match list. iwl_config_sched_scan_profiles()
607d1e879ecSMiri Korenblit * uses the order of these ssids to config match list.
608d1e879ecSMiri Korenblit */
609d1e879ecSMiri Korenblit for (i = 0, j = params->n_match_sets - 1;
610d1e879ecSMiri Korenblit j >= 0 && i < PROBE_OPTION_MAX;
611d1e879ecSMiri Korenblit i++, j--) {
612d1e879ecSMiri Korenblit /* skip empty SSID match_sets */
613d1e879ecSMiri Korenblit if (!params->match_sets[j].ssid.ssid_len)
614d1e879ecSMiri Korenblit continue;
615d1e879ecSMiri Korenblit
616d1e879ecSMiri Korenblit ssids[i].id = WLAN_EID_SSID;
617d1e879ecSMiri Korenblit ssids[i].len = params->match_sets[j].ssid.ssid_len;
618d1e879ecSMiri Korenblit memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid,
619d1e879ecSMiri Korenblit ssids[i].len);
620d1e879ecSMiri Korenblit }
621d1e879ecSMiri Korenblit
622d1e879ecSMiri Korenblit /* add SSIDs from scan SSID list */
623d1e879ecSMiri Korenblit for (j = params->n_ssids - 1;
624d1e879ecSMiri Korenblit j >= 0 && i < PROBE_OPTION_MAX;
625d1e879ecSMiri Korenblit i++, j--) {
626d1e879ecSMiri Korenblit index = iwl_mld_scan_ssid_exist(params->ssids[j].ssid,
627d1e879ecSMiri Korenblit params->ssids[j].ssid_len,
628d1e879ecSMiri Korenblit ssids);
629d1e879ecSMiri Korenblit if (index < 0) {
630d1e879ecSMiri Korenblit ssids[i].id = WLAN_EID_SSID;
631d1e879ecSMiri Korenblit ssids[i].len = params->ssids[j].ssid_len;
632d1e879ecSMiri Korenblit memcpy(ssids[i].ssid, params->ssids[j].ssid,
633d1e879ecSMiri Korenblit ssids[i].len);
634d1e879ecSMiri Korenblit tmp_bitmap |= BIT(i);
635d1e879ecSMiri Korenblit } else {
636d1e879ecSMiri Korenblit tmp_bitmap |= BIT(index);
637d1e879ecSMiri Korenblit }
638d1e879ecSMiri Korenblit }
639d1e879ecSMiri Korenblit
640d1e879ecSMiri Korenblit if (ssid_bitmap)
641d1e879ecSMiri Korenblit *ssid_bitmap = tmp_bitmap;
642d1e879ecSMiri Korenblit }
643d1e879ecSMiri Korenblit
644d1e879ecSMiri Korenblit static void
iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params * params,struct iwl_scan_probe_params_v4 * pp)645d1e879ecSMiri Korenblit iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params,
646d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *pp)
647d1e879ecSMiri Korenblit {
648d1e879ecSMiri Korenblit int j, idex_s = 0, idex_b = 0;
649d1e879ecSMiri Korenblit struct cfg80211_scan_6ghz_params *scan_6ghz_params =
650d1e879ecSMiri Korenblit params->scan_6ghz_params;
651d1e879ecSMiri Korenblit
652d1e879ecSMiri Korenblit for (j = 0;
653d1e879ecSMiri Korenblit j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE;
654d1e879ecSMiri Korenblit j++) {
655d1e879ecSMiri Korenblit if (!params->ssids[j].ssid_len)
656d1e879ecSMiri Korenblit continue;
657d1e879ecSMiri Korenblit
658d1e879ecSMiri Korenblit pp->short_ssid[idex_s] =
659d1e879ecSMiri Korenblit cpu_to_le32(~crc32_le(~0, params->ssids[j].ssid,
660d1e879ecSMiri Korenblit params->ssids[j].ssid_len));
661d1e879ecSMiri Korenblit
662d1e879ecSMiri Korenblit /* hidden 6ghz scan */
663d1e879ecSMiri Korenblit pp->direct_scan[idex_s].id = WLAN_EID_SSID;
664d1e879ecSMiri Korenblit pp->direct_scan[idex_s].len = params->ssids[j].ssid_len;
665d1e879ecSMiri Korenblit memcpy(pp->direct_scan[idex_s].ssid, params->ssids[j].ssid,
666d1e879ecSMiri Korenblit params->ssids[j].ssid_len);
667d1e879ecSMiri Korenblit idex_s++;
668d1e879ecSMiri Korenblit }
669d1e879ecSMiri Korenblit
670d1e879ecSMiri Korenblit /* Populate the arrays of the short SSIDs and the BSSIDs using the 6GHz
671d1e879ecSMiri Korenblit * collocated parameters. This might not be optimal, as this processing
672d1e879ecSMiri Korenblit * does not (yet) correspond to the actual channels, so it is possible
673d1e879ecSMiri Korenblit * that some entries would be left out.
674d1e879ecSMiri Korenblit */
675d1e879ecSMiri Korenblit for (j = 0; j < params->n_6ghz_params; j++) {
676d1e879ecSMiri Korenblit int k;
677d1e879ecSMiri Korenblit
678d1e879ecSMiri Korenblit /* First, try to place the short SSID */
679d1e879ecSMiri Korenblit if (scan_6ghz_params[j].short_ssid_valid) {
680d1e879ecSMiri Korenblit for (k = 0; k < idex_s; k++) {
681d1e879ecSMiri Korenblit if (pp->short_ssid[k] ==
682d1e879ecSMiri Korenblit cpu_to_le32(scan_6ghz_params[j].short_ssid))
683d1e879ecSMiri Korenblit break;
684d1e879ecSMiri Korenblit }
685d1e879ecSMiri Korenblit
686d1e879ecSMiri Korenblit if (k == idex_s && idex_s < SCAN_SHORT_SSID_MAX_SIZE) {
687d1e879ecSMiri Korenblit pp->short_ssid[idex_s++] =
688d1e879ecSMiri Korenblit cpu_to_le32(scan_6ghz_params[j].short_ssid);
689d1e879ecSMiri Korenblit }
690d1e879ecSMiri Korenblit }
691d1e879ecSMiri Korenblit
692d1e879ecSMiri Korenblit /* try to place BSSID for the same entry */
693d1e879ecSMiri Korenblit for (k = 0; k < idex_b; k++) {
694d1e879ecSMiri Korenblit if (!memcmp(&pp->bssid_array[k],
695d1e879ecSMiri Korenblit scan_6ghz_params[j].bssid, ETH_ALEN))
696d1e879ecSMiri Korenblit break;
697d1e879ecSMiri Korenblit }
698d1e879ecSMiri Korenblit
699d1e879ecSMiri Korenblit if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE &&
700d1e879ecSMiri Korenblit !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid),
701d1e879ecSMiri Korenblit "scan: invalid BSSID at index %u, index_b=%u\n",
702d1e879ecSMiri Korenblit j, idex_b)) {
703d1e879ecSMiri Korenblit memcpy(&pp->bssid_array[idex_b++],
704d1e879ecSMiri Korenblit scan_6ghz_params[j].bssid, ETH_ALEN);
705d1e879ecSMiri Korenblit }
706d1e879ecSMiri Korenblit }
707d1e879ecSMiri Korenblit
708d1e879ecSMiri Korenblit pp->short_ssid_num = idex_s;
709d1e879ecSMiri Korenblit pp->bssid_num = idex_b;
710d1e879ecSMiri Korenblit }
711d1e879ecSMiri Korenblit
712d1e879ecSMiri Korenblit static void
iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params * params,struct iwl_scan_probe_params_v4 * pp,u32 * bitmap_ssid)713d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params,
714d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *pp,
715d1e879ecSMiri Korenblit u32 *bitmap_ssid)
716d1e879ecSMiri Korenblit {
717d1e879ecSMiri Korenblit pp->preq = params->preq;
718d1e879ecSMiri Korenblit
719d1e879ecSMiri Korenblit if (params->scan_6ghz) {
720d1e879ecSMiri Korenblit iwl_mld_scan_fill_6g_chan_list(params, pp);
721d1e879ecSMiri Korenblit return;
722d1e879ecSMiri Korenblit }
723d1e879ecSMiri Korenblit
724d1e879ecSMiri Korenblit /* relevant only for 2.4 GHz /5 GHz scan */
725d1e879ecSMiri Korenblit iwl_mld_scan_cmd_build_ssids(params, pp->direct_scan, bitmap_ssid);
726d1e879ecSMiri Korenblit }
727d1e879ecSMiri Korenblit
728d1e879ecSMiri Korenblit static bool
iwl_mld_scan_use_ebs(struct iwl_mld * mld,struct ieee80211_vif * vif,bool low_latency)729d1e879ecSMiri Korenblit iwl_mld_scan_use_ebs(struct iwl_mld *mld, struct ieee80211_vif *vif,
730d1e879ecSMiri Korenblit bool low_latency)
731d1e879ecSMiri Korenblit {
732d1e879ecSMiri Korenblit const struct iwl_ucode_capabilities *capa = &mld->fw->ucode_capa;
733d1e879ecSMiri Korenblit
734d1e879ecSMiri Korenblit /* We can only use EBS if:
735d1e879ecSMiri Korenblit * 1. the feature is supported.
736d1e879ecSMiri Korenblit * 2. the last EBS was successful.
737d1e879ecSMiri Korenblit * 3. it's not a p2p find operation.
738d1e879ecSMiri Korenblit * 4. we are not in low latency mode,
739d1e879ecSMiri Korenblit * or if fragmented ebs is supported by the FW
740d1e879ecSMiri Korenblit * 5. the VIF is not an AP interface (scan wants survey results)
741d1e879ecSMiri Korenblit */
742d1e879ecSMiri Korenblit return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
743d1e879ecSMiri Korenblit !mld->scan.last_ebs_failed &&
744d1e879ecSMiri Korenblit vif->type != NL80211_IFTYPE_P2P_DEVICE &&
745d1e879ecSMiri Korenblit (!low_latency || fw_has_api(capa, IWL_UCODE_TLV_API_FRAG_EBS)) &&
746d1e879ecSMiri Korenblit ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_AP);
747d1e879ecSMiri Korenblit }
748d1e879ecSMiri Korenblit
749d1e879ecSMiri Korenblit static u8
iwl_mld_scan_cmd_set_chan_flags(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif,bool low_latency)750d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_chan_flags(struct iwl_mld *mld,
751d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
752d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
753d1e879ecSMiri Korenblit bool low_latency)
754d1e879ecSMiri Korenblit {
755d1e879ecSMiri Korenblit u8 flags = 0;
756d1e879ecSMiri Korenblit
757d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER;
758d1e879ecSMiri Korenblit
759d1e879ecSMiri Korenblit if (iwl_mld_scan_use_ebs(mld, vif, low_latency))
760d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_EBS |
761d1e879ecSMiri Korenblit IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
762d1e879ecSMiri Korenblit IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
763d1e879ecSMiri Korenblit
764d1e879ecSMiri Korenblit /* set fragmented ebs for fragmented scan */
765d1e879ecSMiri Korenblit if (iwl_mld_scan_is_fragmented(params->type))
766d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG;
767d1e879ecSMiri Korenblit
768d1e879ecSMiri Korenblit /* Force EBS in case the scan is a fragmented and there is a need
769d1e879ecSMiri Korenblit * to take P2P GO operation into consideration during scan operation.
770d1e879ecSMiri Korenblit */
771d1e879ecSMiri Korenblit /* TODO: CDB */
772d1e879ecSMiri Korenblit if (iwl_mld_scan_is_fragmented(params->type) &&
773d1e879ecSMiri Korenblit params->respect_p2p_go) {
774d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Respect P2P GO. Force EBS\n");
775d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_FORCE_EBS;
776d1e879ecSMiri Korenblit }
777d1e879ecSMiri Korenblit
778d1e879ecSMiri Korenblit return flags;
779d1e879ecSMiri Korenblit }
780d1e879ecSMiri Korenblit
781d1e879ecSMiri Korenblit static const u8 p2p_go_friendly_chs[] = {
782d1e879ecSMiri Korenblit 36, 40, 44, 48, 149, 153, 157, 161, 165,
783d1e879ecSMiri Korenblit };
784d1e879ecSMiri Korenblit
785d1e879ecSMiri Korenblit static const u8 social_chs[] = {
786d1e879ecSMiri Korenblit 1, 6, 11
787d1e879ecSMiri Korenblit };
788d1e879ecSMiri Korenblit
iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type,u8 ch_id)789d1e879ecSMiri Korenblit static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id)
790d1e879ecSMiri Korenblit {
791d1e879ecSMiri Korenblit if (vif_type != NL80211_IFTYPE_P2P_DEVICE)
792d1e879ecSMiri Korenblit return 0;
793d1e879ecSMiri Korenblit
794d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) {
795d1e879ecSMiri Korenblit if (ch_id == p2p_go_friendly_chs[i])
796d1e879ecSMiri Korenblit return IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT;
797d1e879ecSMiri Korenblit }
798d1e879ecSMiri Korenblit
799d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(social_chs); i++) {
800d1e879ecSMiri Korenblit if (ch_id == social_chs[i])
801d1e879ecSMiri Korenblit return IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT;
802d1e879ecSMiri Korenblit }
803d1e879ecSMiri Korenblit
804d1e879ecSMiri Korenblit return 0;
805d1e879ecSMiri Korenblit }
806d1e879ecSMiri Korenblit
807d1e879ecSMiri Korenblit static void
iwl_mld_scan_cmd_set_channels(struct iwl_mld * mld,struct ieee80211_channel ** channels,struct iwl_scan_channel_params_v7 * cp,int n_channels,u32 flags,enum nl80211_iftype vif_type)808d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld,
809d1e879ecSMiri Korenblit struct ieee80211_channel **channels,
810d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *cp,
811d1e879ecSMiri Korenblit int n_channels, u32 flags,
812d1e879ecSMiri Korenblit enum nl80211_iftype vif_type)
813d1e879ecSMiri Korenblit {
814d1e879ecSMiri Korenblit for (int i = 0; i < n_channels; i++) {
815d1e879ecSMiri Korenblit enum nl80211_band band = channels[i]->band;
816d1e879ecSMiri Korenblit struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i];
817d1e879ecSMiri Korenblit u8 iwl_band = iwl_mld_nl80211_band_to_fw(band);
818d1e879ecSMiri Korenblit u32 n_aps_flag =
819d1e879ecSMiri Korenblit iwl_mld_scan_ch_n_aps_flag(vif_type,
820d1e879ecSMiri Korenblit channels[i]->hw_value);
821d1e879ecSMiri Korenblit
822d1e879ecSMiri Korenblit if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE)
823d1e879ecSMiri Korenblit n_aps_flag = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT;
824d1e879ecSMiri Korenblit
825d1e879ecSMiri Korenblit cfg->flags = cpu_to_le32(flags | n_aps_flag);
826d1e879ecSMiri Korenblit cfg->channel_num = channels[i]->hw_value;
827d1e879ecSMiri Korenblit if (cfg80211_channel_is_psc(channels[i]))
828d1e879ecSMiri Korenblit cfg->flags = 0;
829d1e879ecSMiri Korenblit
830d1e879ecSMiri Korenblit if (band == NL80211_BAND_6GHZ) {
831d1e879ecSMiri Korenblit /* 6 GHz channels should only appear in a scan request
832d1e879ecSMiri Korenblit * that has scan_6ghz set. The only exception is MLO
833d1e879ecSMiri Korenblit * scan, which has to be passive.
834d1e879ecSMiri Korenblit */
835d1e879ecSMiri Korenblit WARN_ON_ONCE(cfg->flags != 0);
836d1e879ecSMiri Korenblit cfg->flags =
837d1e879ecSMiri Korenblit cpu_to_le32(IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE);
838d1e879ecSMiri Korenblit }
839d1e879ecSMiri Korenblit
840d1e879ecSMiri Korenblit cfg->v2.iter_count = 1;
841d1e879ecSMiri Korenblit cfg->v2.iter_interval = 0;
842d1e879ecSMiri Korenblit cfg->flags |= cpu_to_le32(iwl_band <<
843d1e879ecSMiri Korenblit IWL_CHAN_CFG_FLAGS_BAND_POS);
844d1e879ecSMiri Korenblit }
845d1e879ecSMiri Korenblit }
846d1e879ecSMiri Korenblit
847d1e879ecSMiri Korenblit static u8
iwl_mld_scan_cfg_channels_6g(struct iwl_mld * mld,struct iwl_mld_scan_params * params,u32 n_channels,struct iwl_scan_probe_params_v4 * pp,struct iwl_scan_channel_params_v7 * cp,enum nl80211_iftype vif_type)848d1e879ecSMiri Korenblit iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld,
849d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
850d1e879ecSMiri Korenblit u32 n_channels,
851d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *pp,
852d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *cp,
853d1e879ecSMiri Korenblit enum nl80211_iftype vif_type)
854d1e879ecSMiri Korenblit {
855d1e879ecSMiri Korenblit struct cfg80211_scan_6ghz_params *scan_6ghz_params =
856d1e879ecSMiri Korenblit params->scan_6ghz_params;
857d1e879ecSMiri Korenblit u32 i;
858d1e879ecSMiri Korenblit u8 ch_cnt;
859d1e879ecSMiri Korenblit
860d1e879ecSMiri Korenblit for (i = 0, ch_cnt = 0; i < params->n_channels; i++) {
861d1e879ecSMiri Korenblit struct iwl_scan_channel_cfg_umac *cfg =
862d1e879ecSMiri Korenblit &cp->channel_config[ch_cnt];
863d1e879ecSMiri Korenblit
864d1e879ecSMiri Korenblit u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0;
865d1e879ecSMiri Korenblit u8 k, n_s_ssids = 0, n_bssids = 0;
866d1e879ecSMiri Korenblit u8 max_s_ssids, max_bssids;
867d1e879ecSMiri Korenblit bool force_passive = false, found = false, allow_passive = true,
868d1e879ecSMiri Korenblit unsolicited_probe_on_chan = false, psc_no_listen = false;
869d1e879ecSMiri Korenblit s8 psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED;
870d1e879ecSMiri Korenblit
871d1e879ecSMiri Korenblit /* Avoid performing passive scan on non PSC channels unless the
872d1e879ecSMiri Korenblit * scan is specifically a passive scan, i.e., no SSIDs
873d1e879ecSMiri Korenblit * configured in the scan command.
874d1e879ecSMiri Korenblit */
875d1e879ecSMiri Korenblit if (!cfg80211_channel_is_psc(params->channels[i]) &&
876d1e879ecSMiri Korenblit !params->n_6ghz_params && params->n_ssids)
877d1e879ecSMiri Korenblit continue;
878d1e879ecSMiri Korenblit
879d1e879ecSMiri Korenblit cfg->channel_num = params->channels[i]->hw_value;
880d1e879ecSMiri Korenblit cfg->flags |=
881d1e879ecSMiri Korenblit cpu_to_le32(PHY_BAND_6 << IWL_CHAN_CFG_FLAGS_BAND_POS);
882d1e879ecSMiri Korenblit
883d1e879ecSMiri Korenblit cfg->v5.iter_count = 1;
884d1e879ecSMiri Korenblit cfg->v5.iter_interval = 0;
885d1e879ecSMiri Korenblit
886d1e879ecSMiri Korenblit for (u32 j = 0; j < params->n_6ghz_params; j++) {
887d1e879ecSMiri Korenblit s8 tmp_psd_20;
888d1e879ecSMiri Korenblit
889d1e879ecSMiri Korenblit if (!(scan_6ghz_params[j].channel_idx == i))
890d1e879ecSMiri Korenblit continue;
891d1e879ecSMiri Korenblit
892d1e879ecSMiri Korenblit unsolicited_probe_on_chan |=
893d1e879ecSMiri Korenblit scan_6ghz_params[j].unsolicited_probe;
894d1e879ecSMiri Korenblit
895d1e879ecSMiri Korenblit /* Use the highest PSD value allowed as advertised by
896d1e879ecSMiri Korenblit * APs for this channel
897d1e879ecSMiri Korenblit */
898d1e879ecSMiri Korenblit tmp_psd_20 = scan_6ghz_params[j].psd_20;
899d1e879ecSMiri Korenblit if (tmp_psd_20 !=
900d1e879ecSMiri Korenblit IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED &&
901d1e879ecSMiri Korenblit (psd_20 ==
902d1e879ecSMiri Korenblit IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED ||
903d1e879ecSMiri Korenblit psd_20 < tmp_psd_20))
904d1e879ecSMiri Korenblit psd_20 = tmp_psd_20;
905d1e879ecSMiri Korenblit
906d1e879ecSMiri Korenblit psc_no_listen |= scan_6ghz_params[j].psc_no_listen;
907d1e879ecSMiri Korenblit }
908d1e879ecSMiri Korenblit
909d1e879ecSMiri Korenblit /* In the following cases apply passive scan:
910d1e879ecSMiri Korenblit * 1. Non fragmented scan:
911d1e879ecSMiri Korenblit * - PSC channel with NO_LISTEN_FLAG on should be treated
912d1e879ecSMiri Korenblit * like non PSC channel
913d1e879ecSMiri Korenblit * - Non PSC channel with more than 3 short SSIDs or more
914d1e879ecSMiri Korenblit * than 9 BSSIDs.
915d1e879ecSMiri Korenblit * - Non PSC Channel with unsolicited probe response and
916d1e879ecSMiri Korenblit * more than 2 short SSIDs or more than 6 BSSIDs.
917d1e879ecSMiri Korenblit * - PSC channel with more than 2 short SSIDs or more than
918d1e879ecSMiri Korenblit * 6 BSSIDs.
919d1e879ecSMiri Korenblit * 2. Fragmented scan:
920d1e879ecSMiri Korenblit * - PSC channel with more than 1 SSID or 3 BSSIDs.
921d1e879ecSMiri Korenblit * - Non PSC channel with more than 2 SSIDs or 6 BSSIDs.
922d1e879ecSMiri Korenblit * - Non PSC channel with unsolicited probe response and
923d1e879ecSMiri Korenblit * more than 1 SSID or more than 3 BSSIDs.
924d1e879ecSMiri Korenblit */
925d1e879ecSMiri Korenblit if (!iwl_mld_scan_is_fragmented(params->type)) {
926d1e879ecSMiri Korenblit if (!cfg80211_channel_is_psc(params->channels[i]) ||
927d1e879ecSMiri Korenblit psc_no_listen) {
928d1e879ecSMiri Korenblit if (unsolicited_probe_on_chan) {
929d1e879ecSMiri Korenblit max_s_ssids = 2;
930d1e879ecSMiri Korenblit max_bssids = 6;
931d1e879ecSMiri Korenblit } else {
932d1e879ecSMiri Korenblit max_s_ssids = 3;
933d1e879ecSMiri Korenblit max_bssids = 9;
934d1e879ecSMiri Korenblit }
935d1e879ecSMiri Korenblit } else {
936d1e879ecSMiri Korenblit max_s_ssids = 2;
937d1e879ecSMiri Korenblit max_bssids = 6;
938d1e879ecSMiri Korenblit }
939d1e879ecSMiri Korenblit } else if (cfg80211_channel_is_psc(params->channels[i])) {
940d1e879ecSMiri Korenblit max_s_ssids = 1;
941d1e879ecSMiri Korenblit max_bssids = 3;
942d1e879ecSMiri Korenblit } else {
943d1e879ecSMiri Korenblit if (unsolicited_probe_on_chan) {
944d1e879ecSMiri Korenblit max_s_ssids = 1;
945d1e879ecSMiri Korenblit max_bssids = 3;
946d1e879ecSMiri Korenblit } else {
947d1e879ecSMiri Korenblit max_s_ssids = 2;
948d1e879ecSMiri Korenblit max_bssids = 6;
949d1e879ecSMiri Korenblit }
950d1e879ecSMiri Korenblit }
951d1e879ecSMiri Korenblit
952d1e879ecSMiri Korenblit /* To optimize the scan time, i.e., reduce the scan dwell time
953d1e879ecSMiri Korenblit * on each channel, the below logic tries to set 3 direct BSSID
954d1e879ecSMiri Korenblit * probe requests for each broadcast probe request with a short
955d1e879ecSMiri Korenblit * SSID.
956d1e879ecSMiri Korenblit */
957d1e879ecSMiri Korenblit for (u32 j = 0; j < params->n_6ghz_params; j++) {
958d1e879ecSMiri Korenblit if (!(scan_6ghz_params[j].channel_idx == i))
959d1e879ecSMiri Korenblit continue;
960d1e879ecSMiri Korenblit
961d1e879ecSMiri Korenblit found = false;
962d1e879ecSMiri Korenblit
963d1e879ecSMiri Korenblit for (k = 0;
964d1e879ecSMiri Korenblit k < pp->short_ssid_num && n_s_ssids < max_s_ssids;
965d1e879ecSMiri Korenblit k++) {
966d1e879ecSMiri Korenblit if (!scan_6ghz_params[j].unsolicited_probe &&
967d1e879ecSMiri Korenblit le32_to_cpu(pp->short_ssid[k]) ==
968d1e879ecSMiri Korenblit scan_6ghz_params[j].short_ssid) {
969d1e879ecSMiri Korenblit /* Relevant short SSID bit set */
970d1e879ecSMiri Korenblit if (s_ssid_bitmap & BIT(k)) {
971d1e879ecSMiri Korenblit found = true;
972d1e879ecSMiri Korenblit break;
973d1e879ecSMiri Korenblit }
974d1e879ecSMiri Korenblit
975d1e879ecSMiri Korenblit /* Prefer creating BSSID entries unless
976d1e879ecSMiri Korenblit * the short SSID probe can be done in
977d1e879ecSMiri Korenblit * the same channel dwell iteration.
978d1e879ecSMiri Korenblit *
979d1e879ecSMiri Korenblit * We also need to create a short SSID
980d1e879ecSMiri Korenblit * entry for any hidden AP.
981d1e879ecSMiri Korenblit */
982d1e879ecSMiri Korenblit if (3 * n_s_ssids > n_bssids &&
983d1e879ecSMiri Korenblit !pp->direct_scan[k].len)
984d1e879ecSMiri Korenblit break;
985d1e879ecSMiri Korenblit
986d1e879ecSMiri Korenblit /* Hidden AP, cannot do passive scan */
987d1e879ecSMiri Korenblit if (pp->direct_scan[k].len)
988d1e879ecSMiri Korenblit allow_passive = false;
989d1e879ecSMiri Korenblit
990d1e879ecSMiri Korenblit s_ssid_bitmap |= BIT(k);
991d1e879ecSMiri Korenblit n_s_ssids++;
992d1e879ecSMiri Korenblit found = true;
993d1e879ecSMiri Korenblit break;
994d1e879ecSMiri Korenblit }
995d1e879ecSMiri Korenblit }
996d1e879ecSMiri Korenblit
997d1e879ecSMiri Korenblit if (found)
998d1e879ecSMiri Korenblit continue;
999d1e879ecSMiri Korenblit
1000d1e879ecSMiri Korenblit for (k = 0; k < pp->bssid_num; k++) {
1001d1e879ecSMiri Korenblit if (memcmp(&pp->bssid_array[k],
1002d1e879ecSMiri Korenblit scan_6ghz_params[j].bssid,
1003d1e879ecSMiri Korenblit ETH_ALEN))
1004d1e879ecSMiri Korenblit continue;
1005d1e879ecSMiri Korenblit
1006d1e879ecSMiri Korenblit if (bssid_bitmap & BIT(k))
1007d1e879ecSMiri Korenblit break;
1008d1e879ecSMiri Korenblit
1009d1e879ecSMiri Korenblit if (n_bssids < max_bssids) {
1010d1e879ecSMiri Korenblit bssid_bitmap |= BIT(k);
1011d1e879ecSMiri Korenblit n_bssids++;
1012d1e879ecSMiri Korenblit } else {
1013d1e879ecSMiri Korenblit force_passive = TRUE;
1014d1e879ecSMiri Korenblit }
1015d1e879ecSMiri Korenblit
1016d1e879ecSMiri Korenblit break;
1017d1e879ecSMiri Korenblit }
1018d1e879ecSMiri Korenblit }
1019d1e879ecSMiri Korenblit
1020d1e879ecSMiri Korenblit if (cfg80211_channel_is_psc(params->channels[i]) &&
1021d1e879ecSMiri Korenblit psc_no_listen)
1022d1e879ecSMiri Korenblit flags |= IWL_UHB_CHAN_CFG_FLAG_PSC_CHAN_NO_LISTEN;
1023d1e879ecSMiri Korenblit
1024d1e879ecSMiri Korenblit if (unsolicited_probe_on_chan)
1025d1e879ecSMiri Korenblit flags |= IWL_UHB_CHAN_CFG_FLAG_UNSOLICITED_PROBE_RES;
1026d1e879ecSMiri Korenblit
1027d1e879ecSMiri Korenblit if ((allow_passive && force_passive) ||
1028d1e879ecSMiri Korenblit (!(bssid_bitmap | s_ssid_bitmap) &&
1029d1e879ecSMiri Korenblit !cfg80211_channel_is_psc(params->channels[i])))
1030d1e879ecSMiri Korenblit flags |= IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE;
1031d1e879ecSMiri Korenblit else
1032d1e879ecSMiri Korenblit flags |= bssid_bitmap | (s_ssid_bitmap << 16);
1033d1e879ecSMiri Korenblit
1034d1e879ecSMiri Korenblit cfg->flags |= cpu_to_le32(flags);
1035d1e879ecSMiri Korenblit cfg->v5.psd_20 = psd_20;
1036d1e879ecSMiri Korenblit
1037d1e879ecSMiri Korenblit ch_cnt++;
1038d1e879ecSMiri Korenblit }
1039d1e879ecSMiri Korenblit
1040d1e879ecSMiri Korenblit if (params->n_channels > ch_cnt)
1041d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1042d1e879ecSMiri Korenblit "6GHz: reducing number channels: (%u->%u)\n",
1043d1e879ecSMiri Korenblit params->n_channels, ch_cnt);
1044d1e879ecSMiri Korenblit
1045d1e879ecSMiri Korenblit return ch_cnt;
1046d1e879ecSMiri Korenblit }
1047d1e879ecSMiri Korenblit
1048d1e879ecSMiri Korenblit static int
iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif,struct iwl_scan_req_params_v17 * scan_p,enum iwl_mld_scan_status scan_status)1049d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld,
1050d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
1051d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
1052d1e879ecSMiri Korenblit struct iwl_scan_req_params_v17 *scan_p,
1053d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status)
1054d1e879ecSMiri Korenblit {
1055d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params;
1056d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params;
1057d1e879ecSMiri Korenblit
1058d1e879ecSMiri Korenblit chan_p->flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
1059d1e879ecSMiri Korenblit scan_status);
1060d1e879ecSMiri Korenblit chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params,
1061d1e879ecSMiri Korenblit params->n_channels,
1062d1e879ecSMiri Korenblit probe_p, chan_p,
1063d1e879ecSMiri Korenblit vif->type);
1064d1e879ecSMiri Korenblit if (!chan_p->count)
1065d1e879ecSMiri Korenblit return -EINVAL;
1066d1e879ecSMiri Korenblit
1067d1e879ecSMiri Korenblit if (!params->n_ssids ||
1068d1e879ecSMiri Korenblit (params->n_ssids == 1 && !params->ssids[0].ssid_len))
1069d1e879ecSMiri Korenblit chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER;
1070d1e879ecSMiri Korenblit
1071d1e879ecSMiri Korenblit return 0;
1072d1e879ecSMiri Korenblit }
1073d1e879ecSMiri Korenblit
1074d1e879ecSMiri Korenblit static int
iwl_mld_scan_cmd_set_chan_params(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif,struct iwl_scan_req_params_v17 * scan_p,bool low_latency,enum iwl_mld_scan_status scan_status,u32 channel_cfg_flags)1075d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
1076d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
1077d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
1078d1e879ecSMiri Korenblit struct iwl_scan_req_params_v17 *scan_p,
1079d1e879ecSMiri Korenblit bool low_latency,
1080d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status,
1081d1e879ecSMiri Korenblit u32 channel_cfg_flags)
1082d1e879ecSMiri Korenblit {
1083d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params;
1084d1e879ecSMiri Korenblit struct ieee80211_supported_band *sband =
1085d1e879ecSMiri Korenblit &mld->nvm_data->bands[NL80211_BAND_6GHZ];
1086d1e879ecSMiri Korenblit
1087d1e879ecSMiri Korenblit cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY;
1088d1e879ecSMiri Korenblit cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS;
1089d1e879ecSMiri Korenblit
1090d1e879ecSMiri Korenblit if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE)
1091d1e879ecSMiri Korenblit cp->n_aps_override[0] = IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE;
1092d1e879ecSMiri Korenblit
1093d1e879ecSMiri Korenblit if (params->scan_6ghz)
1094d1e879ecSMiri Korenblit return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params,
1095d1e879ecSMiri Korenblit vif, scan_p,
1096d1e879ecSMiri Korenblit scan_status);
1097d1e879ecSMiri Korenblit
1098d1e879ecSMiri Korenblit /* relevant only for 2.4 GHz/5 GHz scan */
1099d1e879ecSMiri Korenblit cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif,
1100d1e879ecSMiri Korenblit low_latency);
1101d1e879ecSMiri Korenblit cp->count = params->n_channels;
1102d1e879ecSMiri Korenblit
1103d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_channels(mld, params->channels, cp,
1104d1e879ecSMiri Korenblit params->n_channels, channel_cfg_flags,
1105d1e879ecSMiri Korenblit vif->type);
1106d1e879ecSMiri Korenblit
1107d1e879ecSMiri Korenblit if (!params->enable_6ghz_passive)
1108d1e879ecSMiri Korenblit return 0;
1109d1e879ecSMiri Korenblit
1110d1e879ecSMiri Korenblit /* fill 6 GHz passive scan cfg */
1111d1e879ecSMiri Korenblit for (int i = 0; i < sband->n_channels; i++) {
1112d1e879ecSMiri Korenblit struct ieee80211_channel *channel =
1113d1e879ecSMiri Korenblit &sband->channels[i];
1114d1e879ecSMiri Korenblit struct iwl_scan_channel_cfg_umac *cfg =
1115d1e879ecSMiri Korenblit &cp->channel_config[cp->count];
1116d1e879ecSMiri Korenblit
1117d1e879ecSMiri Korenblit if (!cfg80211_channel_is_psc(channel))
1118d1e879ecSMiri Korenblit continue;
1119d1e879ecSMiri Korenblit
1120d1e879ecSMiri Korenblit cfg->channel_num = channel->hw_value;
1121d1e879ecSMiri Korenblit cfg->v5.iter_count = 1;
1122d1e879ecSMiri Korenblit cfg->v5.iter_interval = 0;
1123d1e879ecSMiri Korenblit cfg->v5.psd_20 =
1124d1e879ecSMiri Korenblit IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED;
1125d1e879ecSMiri Korenblit cfg->flags = cpu_to_le32(PHY_BAND_6 <<
1126d1e879ecSMiri Korenblit IWL_CHAN_CFG_FLAGS_BAND_POS);
1127d1e879ecSMiri Korenblit cp->count++;
1128d1e879ecSMiri Korenblit }
1129d1e879ecSMiri Korenblit
1130d1e879ecSMiri Korenblit return 0;
1131d1e879ecSMiri Korenblit }
1132d1e879ecSMiri Korenblit
1133d1e879ecSMiri Korenblit static int
iwl_mld_scan_build_cmd(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_scan_params * params,enum iwl_mld_scan_status scan_status,bool low_latency)1134d1e879ecSMiri Korenblit iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
1135d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
1136d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status,
1137d1e879ecSMiri Korenblit bool low_latency)
1138d1e879ecSMiri Korenblit {
1139d1e879ecSMiri Korenblit struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
1140d1e879ecSMiri Korenblit struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params;
1141d1e879ecSMiri Korenblit u32 bitmap_ssid = 0;
1142d1e879ecSMiri Korenblit int uid, ret;
1143d1e879ecSMiri Korenblit
1144d1e879ecSMiri Korenblit memset(mld->scan.cmd, 0, mld->scan.cmd_size);
1145d1e879ecSMiri Korenblit
1146d1e879ecSMiri Korenblit /* find a free UID entry */
1147d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
1148d1e879ecSMiri Korenblit if (uid < 0)
1149d1e879ecSMiri Korenblit return uid;
1150d1e879ecSMiri Korenblit
1151d1e879ecSMiri Korenblit cmd->uid = cpu_to_le32(uid);
1152d1e879ecSMiri Korenblit cmd->ooc_priority =
1153d1e879ecSMiri Korenblit cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status));
1154d1e879ecSMiri Korenblit
1155d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_gen_params(mld, params, vif,
1156d1e879ecSMiri Korenblit &scan_p->general_params, scan_status);
1157d1e879ecSMiri Korenblit
1158d1e879ecSMiri Korenblit ret = iwl_mld_scan_cmd_set_sched_params(params,
1159d1e879ecSMiri Korenblit scan_p->periodic_params.schedule,
1160d1e879ecSMiri Korenblit &scan_p->periodic_params.delay);
1161d1e879ecSMiri Korenblit if (ret)
1162d1e879ecSMiri Korenblit return ret;
1163d1e879ecSMiri Korenblit
1164d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params,
1165d1e879ecSMiri Korenblit &bitmap_ssid);
1166d1e879ecSMiri Korenblit
1167d1e879ecSMiri Korenblit ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p,
1168d1e879ecSMiri Korenblit low_latency, scan_status,
1169d1e879ecSMiri Korenblit bitmap_ssid);
1170d1e879ecSMiri Korenblit if (ret)
1171d1e879ecSMiri Korenblit return ret;
1172d1e879ecSMiri Korenblit
1173d1e879ecSMiri Korenblit return uid;
1174d1e879ecSMiri Korenblit }
1175d1e879ecSMiri Korenblit
1176d1e879ecSMiri Korenblit static bool
iwl_mld_scan_pass_all(struct iwl_mld * mld,struct cfg80211_sched_scan_request * req)1177d1e879ecSMiri Korenblit iwl_mld_scan_pass_all(struct iwl_mld *mld,
1178d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *req)
1179d1e879ecSMiri Korenblit {
1180d1e879ecSMiri Korenblit if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
1181d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1182d1e879ecSMiri Korenblit "Sending scheduled scan with filtering, n_match_sets %d\n",
1183d1e879ecSMiri Korenblit req->n_match_sets);
1184d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
1185d1e879ecSMiri Korenblit return false;
1186d1e879ecSMiri Korenblit }
1187d1e879ecSMiri Korenblit
1188d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Sending Scheduled scan without filtering\n");
1189d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED;
1190d1e879ecSMiri Korenblit
1191d1e879ecSMiri Korenblit return true;
1192d1e879ecSMiri Korenblit }
1193d1e879ecSMiri Korenblit
1194d1e879ecSMiri Korenblit static int
iwl_mld_config_sched_scan_profiles(struct iwl_mld * mld,struct cfg80211_sched_scan_request * req)1195d1e879ecSMiri Korenblit iwl_mld_config_sched_scan_profiles(struct iwl_mld *mld,
1196d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *req)
1197d1e879ecSMiri Korenblit {
1198d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = {
1199d1e879ecSMiri Korenblit .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
1200d1e879ecSMiri Korenblit .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
1201d1e879ecSMiri Korenblit };
1202d1e879ecSMiri Korenblit struct iwl_scan_offload_profile *profile;
1203d1e879ecSMiri Korenblit struct iwl_scan_offload_profile_cfg_data *cfg_data;
1204d1e879ecSMiri Korenblit struct iwl_scan_offload_profile_cfg *profile_cfg;
1205d1e879ecSMiri Korenblit struct iwl_scan_offload_blocklist *blocklist;
1206d1e879ecSMiri Korenblit u32 blocklist_size = IWL_SCAN_MAX_BLACKLIST_LEN * sizeof(*blocklist);
1207d1e879ecSMiri Korenblit u32 cmd_size = blocklist_size + sizeof(*profile_cfg);
1208d1e879ecSMiri Korenblit u8 *cmd;
1209d1e879ecSMiri Korenblit int ret;
1210d1e879ecSMiri Korenblit
1211d1e879ecSMiri Korenblit if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES_V2))
1212d1e879ecSMiri Korenblit return -EIO;
1213d1e879ecSMiri Korenblit
1214d1e879ecSMiri Korenblit cmd = kzalloc(cmd_size, GFP_KERNEL);
1215d1e879ecSMiri Korenblit if (!cmd)
1216d1e879ecSMiri Korenblit return -ENOMEM;
1217d1e879ecSMiri Korenblit
1218d1e879ecSMiri Korenblit hcmd.data[0] = cmd;
1219d1e879ecSMiri Korenblit hcmd.len[0] = cmd_size;
1220d1e879ecSMiri Korenblit
1221d1e879ecSMiri Korenblit blocklist = (struct iwl_scan_offload_blocklist *)cmd;
1222d1e879ecSMiri Korenblit profile_cfg = (struct iwl_scan_offload_profile_cfg *)(cmd + blocklist_size);
1223d1e879ecSMiri Korenblit
1224d1e879ecSMiri Korenblit /* No blocklist configuration */
1225d1e879ecSMiri Korenblit cfg_data = &profile_cfg->data;
1226d1e879ecSMiri Korenblit cfg_data->num_profiles = req->n_match_sets;
1227d1e879ecSMiri Korenblit cfg_data->active_clients = SCAN_CLIENT_SCHED_SCAN;
1228d1e879ecSMiri Korenblit cfg_data->pass_match = SCAN_CLIENT_SCHED_SCAN;
1229d1e879ecSMiri Korenblit cfg_data->match_notify = SCAN_CLIENT_SCHED_SCAN;
1230d1e879ecSMiri Korenblit
1231d1e879ecSMiri Korenblit if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len)
1232d1e879ecSMiri Korenblit cfg_data->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN;
1233d1e879ecSMiri Korenblit
1234d1e879ecSMiri Korenblit for (int i = 0; i < req->n_match_sets; i++) {
1235d1e879ecSMiri Korenblit profile = &profile_cfg->profiles[i];
1236d1e879ecSMiri Korenblit
1237d1e879ecSMiri Korenblit /* Support any cipher and auth algorithm */
1238d1e879ecSMiri Korenblit profile->unicast_cipher = 0xff;
1239d1e879ecSMiri Korenblit profile->auth_alg = IWL_AUTH_ALGO_UNSUPPORTED |
1240d1e879ecSMiri Korenblit IWL_AUTH_ALGO_NONE | IWL_AUTH_ALGO_PSK |
1241d1e879ecSMiri Korenblit IWL_AUTH_ALGO_8021X | IWL_AUTH_ALGO_SAE |
1242d1e879ecSMiri Korenblit IWL_AUTH_ALGO_8021X_SHA384 | IWL_AUTH_ALGO_OWE;
1243d1e879ecSMiri Korenblit profile->network_type = IWL_NETWORK_TYPE_ANY;
1244d1e879ecSMiri Korenblit profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY;
1245d1e879ecSMiri Korenblit profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN;
1246d1e879ecSMiri Korenblit profile->ssid_index = i;
1247d1e879ecSMiri Korenblit }
1248d1e879ecSMiri Korenblit
1249d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1250d1e879ecSMiri Korenblit "Sending scheduled scan profile config (n_match_sets=%u)\n",
1251d1e879ecSMiri Korenblit req->n_match_sets);
1252d1e879ecSMiri Korenblit
1253d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd);
1254d1e879ecSMiri Korenblit
1255d1e879ecSMiri Korenblit kfree(cmd);
1256d1e879ecSMiri Korenblit
1257d1e879ecSMiri Korenblit return ret;
1258d1e879ecSMiri Korenblit }
1259d1e879ecSMiri Korenblit
1260d1e879ecSMiri Korenblit static int
iwl_mld_sched_scan_handle_non_psc_channels(struct iwl_mld_scan_params * params,bool * non_psc_included)1261d1e879ecSMiri Korenblit iwl_mld_sched_scan_handle_non_psc_channels(struct iwl_mld_scan_params *params,
1262d1e879ecSMiri Korenblit bool *non_psc_included)
1263d1e879ecSMiri Korenblit {
1264d1e879ecSMiri Korenblit int i, j;
1265d1e879ecSMiri Korenblit
1266d1e879ecSMiri Korenblit *non_psc_included = false;
1267d1e879ecSMiri Korenblit /* for 6 GHZ band only PSC channels need to be added */
1268d1e879ecSMiri Korenblit for (i = 0; i < params->n_channels; i++) {
1269d1e879ecSMiri Korenblit struct ieee80211_channel *channel = params->channels[i];
1270d1e879ecSMiri Korenblit
1271d1e879ecSMiri Korenblit if (channel->band == NL80211_BAND_6GHZ &&
1272d1e879ecSMiri Korenblit !cfg80211_channel_is_psc(channel)) {
1273d1e879ecSMiri Korenblit *non_psc_included = true;
1274d1e879ecSMiri Korenblit break;
1275d1e879ecSMiri Korenblit }
1276d1e879ecSMiri Korenblit }
1277d1e879ecSMiri Korenblit
1278d1e879ecSMiri Korenblit if (!*non_psc_included)
1279d1e879ecSMiri Korenblit return 0;
1280d1e879ecSMiri Korenblit
1281d1e879ecSMiri Korenblit params->channels =
1282d1e879ecSMiri Korenblit kmemdup(params->channels,
1283d1e879ecSMiri Korenblit sizeof(params->channels[0]) * params->n_channels,
1284d1e879ecSMiri Korenblit GFP_KERNEL);
1285d1e879ecSMiri Korenblit if (!params->channels)
1286d1e879ecSMiri Korenblit return -ENOMEM;
1287d1e879ecSMiri Korenblit
1288d1e879ecSMiri Korenblit for (i = j = 0; i < params->n_channels; i++) {
1289d1e879ecSMiri Korenblit if (params->channels[i]->band == NL80211_BAND_6GHZ &&
1290d1e879ecSMiri Korenblit !cfg80211_channel_is_psc(params->channels[i]))
1291d1e879ecSMiri Korenblit continue;
1292d1e879ecSMiri Korenblit params->channels[j++] = params->channels[i];
1293d1e879ecSMiri Korenblit }
1294d1e879ecSMiri Korenblit
1295d1e879ecSMiri Korenblit params->n_channels = j;
1296d1e879ecSMiri Korenblit
1297d1e879ecSMiri Korenblit return 0;
1298d1e879ecSMiri Korenblit }
1299d1e879ecSMiri Korenblit
1300d1e879ecSMiri Korenblit static void
iwl_mld_scan_6ghz_passive_scan(struct iwl_mld * mld,struct iwl_mld_scan_params * params,struct ieee80211_vif * vif)1301d1e879ecSMiri Korenblit iwl_mld_scan_6ghz_passive_scan(struct iwl_mld *mld,
1302d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
1303d1e879ecSMiri Korenblit struct ieee80211_vif *vif)
1304d1e879ecSMiri Korenblit {
1305d1e879ecSMiri Korenblit struct ieee80211_supported_band *sband =
1306d1e879ecSMiri Korenblit &mld->nvm_data->bands[NL80211_BAND_6GHZ];
1307d1e879ecSMiri Korenblit u32 n_disabled, i;
1308d1e879ecSMiri Korenblit
1309d1e879ecSMiri Korenblit params->enable_6ghz_passive = false;
1310d1e879ecSMiri Korenblit
1311d1e879ecSMiri Korenblit /* 6 GHz passive scan may be enabled in the first 2.4 GHz/5 GHz scan
1312d1e879ecSMiri Korenblit * phase to discover geo location if no AP's are found. Skip it when
1313d1e879ecSMiri Korenblit * we're in the 6 GHz scan phase.
1314d1e879ecSMiri Korenblit */
1315d1e879ecSMiri Korenblit if (params->scan_6ghz)
1316d1e879ecSMiri Korenblit return;
1317d1e879ecSMiri Korenblit
1318d1e879ecSMiri Korenblit /* 6 GHz passive scan allowed only on station interface */
1319d1e879ecSMiri Korenblit if (vif->type != NL80211_IFTYPE_STATION) {
1320d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1321d1e879ecSMiri Korenblit "6GHz passive scan: not station interface\n");
1322d1e879ecSMiri Korenblit return;
1323d1e879ecSMiri Korenblit }
1324d1e879ecSMiri Korenblit
1325d1e879ecSMiri Korenblit /* 6 GHz passive scan is allowed in a defined time interval following
1326d1e879ecSMiri Korenblit * HW reset or resume flow, or while not associated and a large
1327d1e879ecSMiri Korenblit * interval has passed since the last 6 GHz passive scan.
1328d1e879ecSMiri Korenblit */
1329d1e879ecSMiri Korenblit if ((vif->cfg.assoc ||
1330d1e879ecSMiri Korenblit time_after(mld->scan.last_6ghz_passive_jiffies +
1331d1e879ecSMiri Korenblit (IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT * HZ), jiffies)) &&
1332d1e879ecSMiri Korenblit (time_before(mld->scan.last_start_time_jiffies +
1333d1e879ecSMiri Korenblit (IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT * HZ),
1334d1e879ecSMiri Korenblit jiffies))) {
1335d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "6GHz passive scan: %s\n",
1336d1e879ecSMiri Korenblit vif->cfg.assoc ? "associated" :
1337d1e879ecSMiri Korenblit "timeout did not expire");
1338d1e879ecSMiri Korenblit return;
1339d1e879ecSMiri Korenblit }
1340d1e879ecSMiri Korenblit
1341d1e879ecSMiri Korenblit /* not enough channels in the regular scan request */
1342d1e879ecSMiri Korenblit if (params->n_channels < IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS) {
1343d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1344d1e879ecSMiri Korenblit "6GHz passive scan: not enough channels %d\n",
1345d1e879ecSMiri Korenblit params->n_channels);
1346d1e879ecSMiri Korenblit return;
1347d1e879ecSMiri Korenblit }
1348d1e879ecSMiri Korenblit
1349d1e879ecSMiri Korenblit for (i = 0; i < params->n_ssids; i++) {
1350d1e879ecSMiri Korenblit if (!params->ssids[i].ssid_len)
1351d1e879ecSMiri Korenblit break;
1352d1e879ecSMiri Korenblit }
1353d1e879ecSMiri Korenblit
1354d1e879ecSMiri Korenblit /* not a wildcard scan, so cannot enable passive 6 GHz scan */
1355d1e879ecSMiri Korenblit if (i == params->n_ssids) {
1356d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1357d1e879ecSMiri Korenblit "6GHz passive scan: no wildcard SSID\n");
1358d1e879ecSMiri Korenblit return;
1359d1e879ecSMiri Korenblit }
1360d1e879ecSMiri Korenblit
1361d1e879ecSMiri Korenblit if (!sband || !sband->n_channels) {
1362d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1363d1e879ecSMiri Korenblit "6GHz passive scan: no 6GHz channels\n");
1364d1e879ecSMiri Korenblit return;
1365d1e879ecSMiri Korenblit }
1366d1e879ecSMiri Korenblit
1367d1e879ecSMiri Korenblit for (i = 0, n_disabled = 0; i < sband->n_channels; i++) {
1368d1e879ecSMiri Korenblit if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED))
1369d1e879ecSMiri Korenblit n_disabled++;
1370d1e879ecSMiri Korenblit }
1371d1e879ecSMiri Korenblit
1372d1e879ecSMiri Korenblit /* Not all the 6 GHz channels are disabled, so no need for 6 GHz
1373d1e879ecSMiri Korenblit * passive scan
1374d1e879ecSMiri Korenblit */
1375d1e879ecSMiri Korenblit if (n_disabled != sband->n_channels) {
1376d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1377d1e879ecSMiri Korenblit "6GHz passive scan: 6GHz channels enabled\n");
1378d1e879ecSMiri Korenblit return;
1379d1e879ecSMiri Korenblit }
1380d1e879ecSMiri Korenblit
1381d1e879ecSMiri Korenblit /* all conditions to enable 6 GHz passive scan are satisfied */
1382d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "6GHz passive scan: can be enabled\n");
1383d1e879ecSMiri Korenblit params->enable_6ghz_passive = true;
1384d1e879ecSMiri Korenblit }
1385d1e879ecSMiri Korenblit
1386d1e879ecSMiri Korenblit static void
iwl_mld_scan_set_link_id(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_scan_params * params,s8 tsf_report_link_id,enum iwl_mld_scan_status scan_status)1387d1e879ecSMiri Korenblit iwl_mld_scan_set_link_id(struct iwl_mld *mld, struct ieee80211_vif *vif,
1388d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params,
1389d1e879ecSMiri Korenblit s8 tsf_report_link_id,
1390d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status)
1391d1e879ecSMiri Korenblit {
1392d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
1393d1e879ecSMiri Korenblit struct iwl_mld_link *link;
1394d1e879ecSMiri Korenblit
1395d1e879ecSMiri Korenblit if (tsf_report_link_id < 0) {
1396d1e879ecSMiri Korenblit if (vif->active_links)
1397d1e879ecSMiri Korenblit tsf_report_link_id = __ffs(vif->active_links);
1398d1e879ecSMiri Korenblit else
1399d1e879ecSMiri Korenblit tsf_report_link_id = 0;
1400d1e879ecSMiri Korenblit }
1401d1e879ecSMiri Korenblit
1402d1e879ecSMiri Korenblit link = iwl_mld_link_dereference_check(mld_vif, tsf_report_link_id);
1403d1e879ecSMiri Korenblit if (!WARN_ON(!link)) {
1404d1e879ecSMiri Korenblit params->fw_link_id = link->fw_id;
1405d1e879ecSMiri Korenblit /* we to store fw_link_id only for regular scan,
1406d1e879ecSMiri Korenblit * and use it in scan complete notif
1407d1e879ecSMiri Korenblit */
1408d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_REGULAR)
1409d1e879ecSMiri Korenblit mld->scan.fw_link_id = link->fw_id;
1410d1e879ecSMiri Korenblit } else {
1411d1e879ecSMiri Korenblit mld->scan.fw_link_id = IWL_MLD_INVALID_FW_ID;
1412d1e879ecSMiri Korenblit params->fw_link_id = IWL_MLD_INVALID_FW_ID;
1413d1e879ecSMiri Korenblit }
1414d1e879ecSMiri Korenblit }
1415d1e879ecSMiri Korenblit
1416d1e879ecSMiri Korenblit static int
_iwl_mld_single_scan_start(struct iwl_mld * mld,struct ieee80211_vif * vif,struct cfg80211_scan_request * req,struct ieee80211_scan_ies * ies,enum iwl_mld_scan_status scan_status)1417d1e879ecSMiri Korenblit _iwl_mld_single_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
1418d1e879ecSMiri Korenblit struct cfg80211_scan_request *req,
1419d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies,
1420d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status)
1421d1e879ecSMiri Korenblit {
1422d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = {
1423d1e879ecSMiri Korenblit .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC),
1424d1e879ecSMiri Korenblit .len = { mld->scan.cmd_size, },
1425d1e879ecSMiri Korenblit .data = { mld->scan.cmd, },
1426d1e879ecSMiri Korenblit .dataflags = { IWL_HCMD_DFL_NOCOPY, },
1427d1e879ecSMiri Korenblit };
1428d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data scan_iter_data = {
1429d1e879ecSMiri Korenblit .current_vif = vif,
1430d1e879ecSMiri Korenblit };
1431d1e879ecSMiri Korenblit struct cfg80211_sched_scan_plan scan_plan = {.iterations = 1};
1432d1e879ecSMiri Korenblit struct iwl_mld_scan_params params = {};
1433d1e879ecSMiri Korenblit int ret, uid;
1434d1e879ecSMiri Korenblit
1435d1e879ecSMiri Korenblit /* we should have failed registration if scan_cmd was NULL */
1436d1e879ecSMiri Korenblit if (WARN_ON(!mld->scan.cmd))
1437d1e879ecSMiri Korenblit return -ENOMEM;
1438d1e879ecSMiri Korenblit
1439d1e879ecSMiri Korenblit if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, req->n_channels))
1440d1e879ecSMiri Korenblit return -ENOBUFS;
1441d1e879ecSMiri Korenblit
1442d1e879ecSMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw,
1443d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL,
1444d1e879ecSMiri Korenblit iwl_mld_scan_iterator,
1445d1e879ecSMiri Korenblit &scan_iter_data);
1446d1e879ecSMiri Korenblit
1447d1e879ecSMiri Korenblit params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data);
1448d1e879ecSMiri Korenblit params.n_ssids = req->n_ssids;
1449d1e879ecSMiri Korenblit params.flags = req->flags;
1450d1e879ecSMiri Korenblit params.n_channels = req->n_channels;
1451d1e879ecSMiri Korenblit params.delay = 0;
1452d1e879ecSMiri Korenblit params.ssids = req->ssids;
1453d1e879ecSMiri Korenblit params.channels = req->channels;
1454d1e879ecSMiri Korenblit params.mac_addr = req->mac_addr;
1455d1e879ecSMiri Korenblit params.mac_addr_mask = req->mac_addr_mask;
1456d1e879ecSMiri Korenblit params.no_cck = req->no_cck;
1457d1e879ecSMiri Korenblit params.pass_all = true;
1458d1e879ecSMiri Korenblit params.n_match_sets = 0;
1459d1e879ecSMiri Korenblit params.match_sets = NULL;
1460d1e879ecSMiri Korenblit params.scan_plans = &scan_plan;
1461d1e879ecSMiri Korenblit params.n_scan_plans = 1;
1462d1e879ecSMiri Korenblit
1463d1e879ecSMiri Korenblit params.n_6ghz_params = req->n_6ghz_params;
1464d1e879ecSMiri Korenblit params.scan_6ghz_params = req->scan_6ghz_params;
1465d1e879ecSMiri Korenblit params.scan_6ghz = req->scan_6ghz;
1466d1e879ecSMiri Korenblit
1467d1e879ecSMiri Korenblit ether_addr_copy(params.bssid, req->bssid);
1468d1e879ecSMiri Korenblit /* TODO: CDB - per-band flag */
1469d1e879ecSMiri Korenblit params.respect_p2p_go =
1470d1e879ecSMiri Korenblit iwl_mld_get_respect_p2p_go(mld, vif,
1471d1e879ecSMiri Korenblit scan_iter_data.global_low_latency);
1472d1e879ecSMiri Korenblit
1473d1e879ecSMiri Korenblit if (req->duration)
1474d1e879ecSMiri Korenblit params.iter_notif = true;
1475d1e879ecSMiri Korenblit
1476d1e879ecSMiri Korenblit iwl_mld_scan_set_link_id(mld, vif, ¶ms, req->tsf_report_link_id,
1477d1e879ecSMiri Korenblit scan_status);
1478d1e879ecSMiri Korenblit
1479d1e879ecSMiri Korenblit iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms);
1480d1e879ecSMiri Korenblit
1481d1e879ecSMiri Korenblit iwl_mld_scan_6ghz_passive_scan(mld, ¶ms, vif);
1482d1e879ecSMiri Korenblit
1483d1e879ecSMiri Korenblit uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, scan_status,
1484d1e879ecSMiri Korenblit scan_iter_data.global_low_latency);
1485d1e879ecSMiri Korenblit if (uid < 0)
1486d1e879ecSMiri Korenblit return uid;
1487d1e879ecSMiri Korenblit
1488d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd);
1489d1e879ecSMiri Korenblit if (ret) {
1490d1e879ecSMiri Korenblit IWL_ERR(mld, "Scan failed! ret %d\n", ret);
1491d1e879ecSMiri Korenblit return ret;
1492d1e879ecSMiri Korenblit }
1493d1e879ecSMiri Korenblit
1494d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan request send success: status=%u, uid=%u\n",
1495d1e879ecSMiri Korenblit scan_status, uid);
1496d1e879ecSMiri Korenblit
1497d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = scan_status;
1498d1e879ecSMiri Korenblit mld->scan.status |= scan_status;
1499d1e879ecSMiri Korenblit
1500d1e879ecSMiri Korenblit if (params.enable_6ghz_passive)
1501d1e879ecSMiri Korenblit mld->scan.last_6ghz_passive_jiffies = jiffies;
1502d1e879ecSMiri Korenblit
1503d1e879ecSMiri Korenblit return 0;
1504d1e879ecSMiri Korenblit }
1505d1e879ecSMiri Korenblit
1506d1e879ecSMiri Korenblit static int
iwl_mld_scan_send_abort_cmd_status(struct iwl_mld * mld,int uid,u32 * status)1507d1e879ecSMiri Korenblit iwl_mld_scan_send_abort_cmd_status(struct iwl_mld *mld, int uid, u32 *status)
1508d1e879ecSMiri Korenblit {
1509d1e879ecSMiri Korenblit struct iwl_umac_scan_abort abort_cmd = {
1510d1e879ecSMiri Korenblit .uid = cpu_to_le32(uid),
1511d1e879ecSMiri Korenblit };
1512d1e879ecSMiri Korenblit struct iwl_host_cmd cmd = {
1513d1e879ecSMiri Korenblit .id = WIDE_ID(LONG_GROUP, SCAN_ABORT_UMAC),
1514d1e879ecSMiri Korenblit .flags = CMD_WANT_SKB,
1515d1e879ecSMiri Korenblit .data = { &abort_cmd },
1516d1e879ecSMiri Korenblit .len[0] = sizeof(abort_cmd),
1517d1e879ecSMiri Korenblit };
1518d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt;
1519d1e879ecSMiri Korenblit struct iwl_cmd_response *resp;
1520d1e879ecSMiri Korenblit u32 resp_len;
1521d1e879ecSMiri Korenblit int ret;
1522d1e879ecSMiri Korenblit
1523d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &cmd);
1524d1e879ecSMiri Korenblit if (ret)
1525d1e879ecSMiri Korenblit return ret;
1526d1e879ecSMiri Korenblit
1527d1e879ecSMiri Korenblit pkt = cmd.resp_pkt;
1528d1e879ecSMiri Korenblit
1529d1e879ecSMiri Korenblit resp_len = iwl_rx_packet_payload_len(pkt);
1530d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp),
1531d1e879ecSMiri Korenblit "Scan Abort: unexpected response length %d\n",
1532d1e879ecSMiri Korenblit resp_len)) {
1533d1e879ecSMiri Korenblit ret = -EIO;
1534d1e879ecSMiri Korenblit goto out;
1535d1e879ecSMiri Korenblit }
1536d1e879ecSMiri Korenblit
1537d1e879ecSMiri Korenblit resp = (void *)pkt->data;
1538d1e879ecSMiri Korenblit *status = le32_to_cpu(resp->status);
1539d1e879ecSMiri Korenblit
1540d1e879ecSMiri Korenblit out:
1541d1e879ecSMiri Korenblit iwl_free_resp(&cmd);
1542d1e879ecSMiri Korenblit return ret;
1543d1e879ecSMiri Korenblit }
1544d1e879ecSMiri Korenblit
1545d1e879ecSMiri Korenblit static int
iwl_mld_scan_abort(struct iwl_mld * mld,int type,int uid,bool * wait)1546d1e879ecSMiri Korenblit iwl_mld_scan_abort(struct iwl_mld *mld, int type, int uid, bool *wait)
1547d1e879ecSMiri Korenblit {
1548d1e879ecSMiri Korenblit enum iwl_umac_scan_abort_status status;
1549d1e879ecSMiri Korenblit int ret;
1550d1e879ecSMiri Korenblit
1551d1e879ecSMiri Korenblit *wait = true;
1552d1e879ecSMiri Korenblit
1553d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Sending scan abort, uid %u\n", uid);
1554d1e879ecSMiri Korenblit
1555d1e879ecSMiri Korenblit ret = iwl_mld_scan_send_abort_cmd_status(mld, uid, &status);
1556d1e879ecSMiri Korenblit
1557d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan abort: ret=%d status=%u\n", ret, status);
1558d1e879ecSMiri Korenblit
1559d1e879ecSMiri Korenblit /* We don't need to wait to scan complete in the following cases:
1560d1e879ecSMiri Korenblit * 1. Driver failed to send the scan abort cmd.
1561d1e879ecSMiri Korenblit * 2. The FW is no longer familiar with the scan that needs to be
1562d1e879ecSMiri Korenblit * stopped. It is expected that the scan complete notification was
1563d1e879ecSMiri Korenblit * already received but not yet processed.
1564d1e879ecSMiri Korenblit *
1565d1e879ecSMiri Korenblit * In both cases the flow should continue similar to the case that the
1566d1e879ecSMiri Korenblit * scan was really aborted.
1567d1e879ecSMiri Korenblit */
1568d1e879ecSMiri Korenblit if (ret || status == IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND)
1569d1e879ecSMiri Korenblit *wait = false;
1570d1e879ecSMiri Korenblit
1571d1e879ecSMiri Korenblit return ret;
1572d1e879ecSMiri Korenblit }
1573d1e879ecSMiri Korenblit
1574d1e879ecSMiri Korenblit static int
iwl_mld_scan_stop_wait(struct iwl_mld * mld,int type,int uid)1575d1e879ecSMiri Korenblit iwl_mld_scan_stop_wait(struct iwl_mld *mld, int type, int uid)
1576d1e879ecSMiri Korenblit {
1577d1e879ecSMiri Korenblit struct iwl_notification_wait wait_scan_done;
1578d1e879ecSMiri Korenblit static const u16 scan_comp_notif[] = { SCAN_COMPLETE_UMAC };
1579d1e879ecSMiri Korenblit bool wait = true;
1580d1e879ecSMiri Korenblit int ret;
1581d1e879ecSMiri Korenblit
1582d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait, &wait_scan_done,
1583d1e879ecSMiri Korenblit scan_comp_notif,
1584d1e879ecSMiri Korenblit ARRAY_SIZE(scan_comp_notif),
1585d1e879ecSMiri Korenblit NULL, NULL);
1586d1e879ecSMiri Korenblit
1587d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Preparing to stop scan, type=%x\n", type);
1588d1e879ecSMiri Korenblit
1589d1e879ecSMiri Korenblit ret = iwl_mld_scan_abort(mld, type, uid, &wait);
1590d1e879ecSMiri Korenblit if (ret) {
1591d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "couldn't stop scan type=%d\n", type);
1592d1e879ecSMiri Korenblit goto return_no_wait;
1593d1e879ecSMiri Korenblit }
1594d1e879ecSMiri Korenblit
1595d1e879ecSMiri Korenblit if (!wait) {
1596d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "no need to wait for scan type=%d\n", type);
1597d1e879ecSMiri Korenblit goto return_no_wait;
1598d1e879ecSMiri Korenblit }
1599d1e879ecSMiri Korenblit
1600d1e879ecSMiri Korenblit return iwl_wait_notification(&mld->notif_wait, &wait_scan_done, HZ);
1601d1e879ecSMiri Korenblit
1602d1e879ecSMiri Korenblit return_no_wait:
1603d1e879ecSMiri Korenblit iwl_remove_notification(&mld->notif_wait, &wait_scan_done);
1604d1e879ecSMiri Korenblit return ret;
1605d1e879ecSMiri Korenblit }
1606d1e879ecSMiri Korenblit
iwl_mld_sched_scan_start(struct iwl_mld * mld,struct ieee80211_vif * vif,struct cfg80211_sched_scan_request * req,struct ieee80211_scan_ies * ies,int type)1607d1e879ecSMiri Korenblit int iwl_mld_sched_scan_start(struct iwl_mld *mld,
1608d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
1609d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *req,
1610d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies,
1611d1e879ecSMiri Korenblit int type)
1612d1e879ecSMiri Korenblit {
1613d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = {
1614d1e879ecSMiri Korenblit .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC),
1615d1e879ecSMiri Korenblit .len = { mld->scan.cmd_size, },
1616d1e879ecSMiri Korenblit .data = { mld->scan.cmd, },
1617d1e879ecSMiri Korenblit .dataflags = { IWL_HCMD_DFL_NOCOPY, },
1618d1e879ecSMiri Korenblit };
1619d1e879ecSMiri Korenblit struct iwl_mld_scan_params params = {};
1620d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data scan_iter_data = {
1621d1e879ecSMiri Korenblit .current_vif = vif,
1622d1e879ecSMiri Korenblit };
1623d1e879ecSMiri Korenblit bool non_psc_included = false;
1624d1e879ecSMiri Korenblit int ret, uid;
1625d1e879ecSMiri Korenblit
1626d1e879ecSMiri Korenblit /* we should have failed registration if scan_cmd was NULL */
1627d1e879ecSMiri Korenblit if (WARN_ON(!mld->scan.cmd))
1628d1e879ecSMiri Korenblit return -ENOMEM;
1629d1e879ecSMiri Korenblit
1630d1e879ecSMiri Korenblit /* FW supports only a single periodic scan */
1631d1e879ecSMiri Korenblit if (mld->scan.status & (IWL_MLD_SCAN_SCHED | IWL_MLD_SCAN_NETDETECT))
1632d1e879ecSMiri Korenblit return -EBUSY;
1633d1e879ecSMiri Korenblit
1634d1e879ecSMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw,
1635d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL,
1636d1e879ecSMiri Korenblit iwl_mld_scan_iterator,
1637d1e879ecSMiri Korenblit &scan_iter_data);
1638d1e879ecSMiri Korenblit
1639d1e879ecSMiri Korenblit params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data);
1640d1e879ecSMiri Korenblit params.flags = req->flags;
1641d1e879ecSMiri Korenblit params.n_ssids = req->n_ssids;
1642d1e879ecSMiri Korenblit params.ssids = req->ssids;
1643d1e879ecSMiri Korenblit params.n_channels = req->n_channels;
1644d1e879ecSMiri Korenblit params.channels = req->channels;
1645d1e879ecSMiri Korenblit params.mac_addr = req->mac_addr;
1646d1e879ecSMiri Korenblit params.mac_addr_mask = req->mac_addr_mask;
1647d1e879ecSMiri Korenblit params.no_cck = false;
1648d1e879ecSMiri Korenblit params.pass_all = iwl_mld_scan_pass_all(mld, req);
1649d1e879ecSMiri Korenblit params.n_match_sets = req->n_match_sets;
1650d1e879ecSMiri Korenblit params.match_sets = req->match_sets;
1651d1e879ecSMiri Korenblit params.n_scan_plans = req->n_scan_plans;
1652d1e879ecSMiri Korenblit params.scan_plans = req->scan_plans;
1653d1e879ecSMiri Korenblit /* TODO: CDB - per-band flag */
1654d1e879ecSMiri Korenblit params.respect_p2p_go =
1655d1e879ecSMiri Korenblit iwl_mld_get_respect_p2p_go(mld, vif,
1656d1e879ecSMiri Korenblit scan_iter_data.global_low_latency);
1657d1e879ecSMiri Korenblit
1658d1e879ecSMiri Korenblit /* UMAC scan supports up to 16-bit delays, trim it down to 16-bits */
1659d1e879ecSMiri Korenblit params.delay = req->delay > U16_MAX ? U16_MAX : req->delay;
1660d1e879ecSMiri Korenblit
1661d1e879ecSMiri Korenblit eth_broadcast_addr(params.bssid);
1662d1e879ecSMiri Korenblit
1663d1e879ecSMiri Korenblit ret = iwl_mld_config_sched_scan_profiles(mld, req);
1664d1e879ecSMiri Korenblit if (ret)
1665d1e879ecSMiri Korenblit return ret;
1666d1e879ecSMiri Korenblit
1667d1e879ecSMiri Korenblit iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms);
1668d1e879ecSMiri Korenblit
1669d1e879ecSMiri Korenblit ret = iwl_mld_sched_scan_handle_non_psc_channels(¶ms,
1670d1e879ecSMiri Korenblit &non_psc_included);
1671d1e879ecSMiri Korenblit if (ret)
1672d1e879ecSMiri Korenblit goto out;
1673d1e879ecSMiri Korenblit
1674d1e879ecSMiri Korenblit if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, params.n_channels)) {
1675d1e879ecSMiri Korenblit ret = -ENOBUFS;
1676d1e879ecSMiri Korenblit goto out;
1677d1e879ecSMiri Korenblit }
1678d1e879ecSMiri Korenblit
1679d1e879ecSMiri Korenblit uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, type,
1680d1e879ecSMiri Korenblit scan_iter_data.global_low_latency);
1681d1e879ecSMiri Korenblit if (uid < 0) {
1682d1e879ecSMiri Korenblit ret = uid;
1683d1e879ecSMiri Korenblit goto out;
1684d1e879ecSMiri Korenblit }
1685d1e879ecSMiri Korenblit
1686d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd);
1687d1e879ecSMiri Korenblit if (!ret) {
1688d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1689d1e879ecSMiri Korenblit "Sched scan request send success: type=%u, uid=%u\n",
1690d1e879ecSMiri Korenblit type, uid);
1691d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = type;
1692d1e879ecSMiri Korenblit mld->scan.status |= type;
1693d1e879ecSMiri Korenblit } else {
1694d1e879ecSMiri Korenblit IWL_ERR(mld, "Sched scan failed! ret %d\n", ret);
1695d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
1696d1e879ecSMiri Korenblit }
1697d1e879ecSMiri Korenblit
1698d1e879ecSMiri Korenblit out:
1699d1e879ecSMiri Korenblit if (non_psc_included)
1700d1e879ecSMiri Korenblit kfree(params.channels);
1701d1e879ecSMiri Korenblit return ret;
1702d1e879ecSMiri Korenblit }
1703d1e879ecSMiri Korenblit
iwl_mld_scan_stop(struct iwl_mld * mld,int type,bool notify)1704d1e879ecSMiri Korenblit int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify)
1705d1e879ecSMiri Korenblit {
1706d1e879ecSMiri Korenblit int uid, ret;
1707d1e879ecSMiri Korenblit
1708d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1709d1e879ecSMiri Korenblit "Request to stop scan: type=0x%x, status=0x%x\n",
1710d1e879ecSMiri Korenblit type, mld->scan.status);
1711d1e879ecSMiri Korenblit
1712d1e879ecSMiri Korenblit if (!(mld->scan.status & type))
1713d1e879ecSMiri Korenblit return 0;
1714d1e879ecSMiri Korenblit
1715d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, type);
1716d1e879ecSMiri Korenblit /* must be valid, we just checked it's running */
1717d1e879ecSMiri Korenblit if (WARN_ON_ONCE(uid < 0))
1718d1e879ecSMiri Korenblit return uid;
1719d1e879ecSMiri Korenblit
1720d1e879ecSMiri Korenblit ret = iwl_mld_scan_stop_wait(mld, type, uid);
1721d1e879ecSMiri Korenblit if (ret)
1722d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Failed to stop scan\n");
1723d1e879ecSMiri Korenblit
1724d1e879ecSMiri Korenblit /* Clear the scan status so the next scan requests will
1725d1e879ecSMiri Korenblit * succeed and mark the scan as stopping, so that the Rx
1726d1e879ecSMiri Korenblit * handler doesn't do anything, as the scan was stopped from
1727d1e879ecSMiri Korenblit * above. Also remove the handler to not notify mac80211
1728d1e879ecSMiri Korenblit * erroneously after a new scan starts, for example.
1729d1e879ecSMiri Korenblit */
1730d1e879ecSMiri Korenblit mld->scan.status &= ~type;
1731d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
1732d1e879ecSMiri Korenblit iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_SCAN,
1733d1e879ecSMiri Korenblit uid);
1734d1e879ecSMiri Korenblit
1735d1e879ecSMiri Korenblit if (type == IWL_MLD_SCAN_REGULAR) {
1736d1e879ecSMiri Korenblit if (notify) {
1737d1e879ecSMiri Korenblit struct cfg80211_scan_info info = {
1738d1e879ecSMiri Korenblit .aborted = true,
1739d1e879ecSMiri Korenblit };
1740d1e879ecSMiri Korenblit
1741d1e879ecSMiri Korenblit ieee80211_scan_completed(mld->hw, &info);
1742d1e879ecSMiri Korenblit }
1743d1e879ecSMiri Korenblit } else if (notify) {
1744d1e879ecSMiri Korenblit ieee80211_sched_scan_stopped(mld->hw);
1745d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
1746d1e879ecSMiri Korenblit }
1747d1e879ecSMiri Korenblit
1748d1e879ecSMiri Korenblit return ret;
1749d1e879ecSMiri Korenblit }
1750d1e879ecSMiri Korenblit
iwl_mld_regular_scan_start(struct iwl_mld * mld,struct ieee80211_vif * vif,struct cfg80211_scan_request * req,struct ieee80211_scan_ies * ies)1751d1e879ecSMiri Korenblit int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
1752d1e879ecSMiri Korenblit struct cfg80211_scan_request *req,
1753d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies)
1754d1e879ecSMiri Korenblit {
1755d1e879ecSMiri Korenblit return _iwl_mld_single_scan_start(mld, vif, req, ies,
1756d1e879ecSMiri Korenblit IWL_MLD_SCAN_REGULAR);
1757d1e879ecSMiri Korenblit }
1758d1e879ecSMiri Korenblit
iwl_mld_int_mlo_scan_start(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_channel ** channels,size_t n_channels)1759d1e879ecSMiri Korenblit static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
1760d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
1761d1e879ecSMiri Korenblit struct ieee80211_channel **channels,
1762d1e879ecSMiri Korenblit size_t n_channels)
1763d1e879ecSMiri Korenblit {
1764d1e879ecSMiri Korenblit struct cfg80211_scan_request *req __free(kfree) = NULL;
1765d1e879ecSMiri Korenblit struct ieee80211_scan_ies ies = {};
1766d1e879ecSMiri Korenblit size_t size;
1767d1e879ecSMiri Korenblit int ret;
1768d1e879ecSMiri Korenblit
1769d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Starting Internal MLO scan: n_channels=%zu\n",
1770d1e879ecSMiri Korenblit n_channels);
1771d1e879ecSMiri Korenblit
1772d1e879ecSMiri Korenblit size = struct_size(req, channels, n_channels);
1773d1e879ecSMiri Korenblit req = kzalloc(size, GFP_KERNEL);
1774d1e879ecSMiri Korenblit if (!req)
1775d1e879ecSMiri Korenblit return;
1776d1e879ecSMiri Korenblit
1777d1e879ecSMiri Korenblit /* set the requested channels */
1778d1e879ecSMiri Korenblit for (int i = 0; i < n_channels; i++)
1779d1e879ecSMiri Korenblit req->channels[i] = channels[i];
1780d1e879ecSMiri Korenblit
1781d1e879ecSMiri Korenblit req->n_channels = n_channels;
1782d1e879ecSMiri Korenblit
1783d1e879ecSMiri Korenblit /* set the rates */
1784d1e879ecSMiri Korenblit for (int i = 0; i < NUM_NL80211_BANDS; i++)
1785d1e879ecSMiri Korenblit if (mld->wiphy->bands[i])
1786d1e879ecSMiri Korenblit req->rates[i] =
1787d1e879ecSMiri Korenblit (1 << mld->wiphy->bands[i]->n_bitrates) - 1;
1788d1e879ecSMiri Korenblit
1789d1e879ecSMiri Korenblit req->wdev = ieee80211_vif_to_wdev(vif);
1790d1e879ecSMiri Korenblit req->wiphy = mld->wiphy;
1791d1e879ecSMiri Korenblit req->scan_start = jiffies;
1792d1e879ecSMiri Korenblit req->tsf_report_link_id = -1;
1793d1e879ecSMiri Korenblit
1794d1e879ecSMiri Korenblit ret = _iwl_mld_single_scan_start(mld, vif, req, &ies,
1795d1e879ecSMiri Korenblit IWL_MLD_SCAN_INT_MLO);
1796d1e879ecSMiri Korenblit
1797*9324731bSMiri Korenblit if (!ret)
1798*9324731bSMiri Korenblit mld->scan.last_mlo_scan_time = ktime_get_boottime_ns();
1799*9324731bSMiri Korenblit
1800d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
1801d1e879ecSMiri Korenblit }
1802d1e879ecSMiri Korenblit
iwl_mld_int_mlo_scan(struct iwl_mld * mld,struct ieee80211_vif * vif)1803d1e879ecSMiri Korenblit void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif)
1804d1e879ecSMiri Korenblit {
1805d1e879ecSMiri Korenblit struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS];
1806d1e879ecSMiri Korenblit unsigned long usable_links = ieee80211_vif_usable_links(vif);
1807d1e879ecSMiri Korenblit size_t n_channels = 0;
1808d1e879ecSMiri Korenblit u8 link_id;
1809d1e879ecSMiri Korenblit
1810d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy);
1811d1e879ecSMiri Korenblit
1812d1e879ecSMiri Korenblit if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) ||
1813d1e879ecSMiri Korenblit hweight16(vif->valid_links) == 1)
1814d1e879ecSMiri Korenblit return;
1815d1e879ecSMiri Korenblit
1816d1e879ecSMiri Korenblit if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) {
1817d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan is already running\n");
1818d1e879ecSMiri Korenblit return;
1819d1e879ecSMiri Korenblit }
1820d1e879ecSMiri Korenblit
1821d1e879ecSMiri Korenblit for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
1822d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf =
1823d1e879ecSMiri Korenblit link_conf_dereference_check(vif, link_id);
1824d1e879ecSMiri Korenblit
1825d1e879ecSMiri Korenblit if (WARN_ON_ONCE(!link_conf))
1826d1e879ecSMiri Korenblit continue;
1827d1e879ecSMiri Korenblit
1828d1e879ecSMiri Korenblit channels[n_channels++] = link_conf->chanreq.oper.chan;
1829d1e879ecSMiri Korenblit }
1830d1e879ecSMiri Korenblit
1831d1e879ecSMiri Korenblit if (!n_channels)
1832d1e879ecSMiri Korenblit return;
1833d1e879ecSMiri Korenblit
1834d1e879ecSMiri Korenblit iwl_mld_int_mlo_scan_start(mld, vif, channels, n_channels);
1835d1e879ecSMiri Korenblit }
1836d1e879ecSMiri Korenblit
iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)1837d1e879ecSMiri Korenblit void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld,
1838d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt)
1839d1e879ecSMiri Korenblit {
1840d1e879ecSMiri Korenblit struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data;
1841d1e879ecSMiri Korenblit u32 uid = __le32_to_cpu(notif->uid);
1842d1e879ecSMiri Korenblit
1843d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
1844d1e879ecSMiri Korenblit "FW reports out-of-range scan UID %d\n", uid))
1845d1e879ecSMiri Korenblit return;
1846d1e879ecSMiri Korenblit
1847d1e879ecSMiri Korenblit if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR)
1848d1e879ecSMiri Korenblit mld->scan.start_tsf = le64_to_cpu(notif->start_tsf);
1849d1e879ecSMiri Korenblit
1850d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1851d1e879ecSMiri Korenblit "UMAC Scan iteration complete: status=0x%x scanned_channels=%d\n",
1852d1e879ecSMiri Korenblit notif->status, notif->scanned_channels);
1853d1e879ecSMiri Korenblit
1854d1e879ecSMiri Korenblit if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_FOUND) {
1855d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Pass all scheduled scan results found\n");
1856d1e879ecSMiri Korenblit ieee80211_sched_scan_results(mld->hw);
1857d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED;
1858d1e879ecSMiri Korenblit }
1859d1e879ecSMiri Korenblit
1860d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1861d1e879ecSMiri Korenblit "UMAC Scan iteration complete: scan started at %llu (TSF)\n",
1862d1e879ecSMiri Korenblit le64_to_cpu(notif->start_tsf));
1863d1e879ecSMiri Korenblit }
1864d1e879ecSMiri Korenblit
iwl_mld_handle_match_found_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)1865d1e879ecSMiri Korenblit void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
1866d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt)
1867d1e879ecSMiri Korenblit {
1868d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scheduled scan results\n");
1869d1e879ecSMiri Korenblit ieee80211_sched_scan_results(mld->hw);
1870d1e879ecSMiri Korenblit }
1871d1e879ecSMiri Korenblit
iwl_mld_handle_scan_complete_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)1872d1e879ecSMiri Korenblit void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
1873d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt)
1874d1e879ecSMiri Korenblit {
1875d1e879ecSMiri Korenblit struct iwl_umac_scan_complete *notif = (void *)pkt->data;
1876d1e879ecSMiri Korenblit bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED);
1877d1e879ecSMiri Korenblit u32 uid = __le32_to_cpu(notif->uid);
1878d1e879ecSMiri Korenblit
1879d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
1880d1e879ecSMiri Korenblit "FW reports out-of-range scan UID %d\n", uid))
1881d1e879ecSMiri Korenblit return;
1882d1e879ecSMiri Korenblit
1883d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1884d1e879ecSMiri Korenblit "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n",
1885d1e879ecSMiri Korenblit uid, mld->scan.uid_status[uid],
1886d1e879ecSMiri Korenblit notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
1887d1e879ecSMiri Korenblit "completed" : "aborted",
1888d1e879ecSMiri Korenblit iwl_mld_scan_ebs_status_str(notif->ebs_status));
1889d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan completed: scan_status=0x%x\n",
1890d1e879ecSMiri Korenblit mld->scan.status);
1891d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld,
1892d1e879ecSMiri Korenblit "Scan completed: line=%u, iter=%u, elapsed time=%u\n",
1893d1e879ecSMiri Korenblit notif->last_schedule, notif->last_iter,
1894d1e879ecSMiri Korenblit __le32_to_cpu(notif->time_from_last_iter));
1895d1e879ecSMiri Korenblit
1896d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status),
1897d1e879ecSMiri Korenblit "FW reports scan UID %d we didn't trigger\n", uid))
1898d1e879ecSMiri Korenblit return;
1899d1e879ecSMiri Korenblit
1900d1e879ecSMiri Korenblit /* if the scan is already stopping, we don't need to notify mac80211 */
1901d1e879ecSMiri Korenblit if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) {
1902d1e879ecSMiri Korenblit struct cfg80211_scan_info info = {
1903d1e879ecSMiri Korenblit .aborted = aborted,
1904d1e879ecSMiri Korenblit .scan_start_tsf = mld->scan.start_tsf,
1905d1e879ecSMiri Korenblit };
1906d1e879ecSMiri Korenblit int fw_link_id = mld->scan.fw_link_id;
1907d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf = NULL;
1908d1e879ecSMiri Korenblit
1909d1e879ecSMiri Korenblit if (fw_link_id != IWL_MLD_INVALID_FW_ID)
1910d1e879ecSMiri Korenblit link_conf =
1911d1e879ecSMiri Korenblit wiphy_dereference(mld->wiphy,
1912d1e879ecSMiri Korenblit mld->fw_id_to_bss_conf[fw_link_id]);
1913d1e879ecSMiri Korenblit
1914d1e879ecSMiri Korenblit /* It is possible that by the time the scan is complete the
1915d1e879ecSMiri Korenblit * link was already removed and is not valid.
1916d1e879ecSMiri Korenblit */
1917d1e879ecSMiri Korenblit if (link_conf)
1918d1e879ecSMiri Korenblit ether_addr_copy(info.tsf_bssid, link_conf->bssid);
1919d1e879ecSMiri Korenblit else
1920d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n");
1921d1e879ecSMiri Korenblit
1922d1e879ecSMiri Korenblit ieee80211_scan_completed(mld->hw, &info);
1923d1e879ecSMiri Korenblit } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) {
1924d1e879ecSMiri Korenblit ieee80211_sched_scan_stopped(mld->hw);
1925d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
1926d1e879ecSMiri Korenblit } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_INT_MLO) {
1927d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan completed\n");
1928d1e879ecSMiri Korenblit
1929d1e879ecSMiri Korenblit /*
1930d1e879ecSMiri Korenblit * We limit link selection to internal MLO scans as otherwise
1931d1e879ecSMiri Korenblit * we do not know whether all channels were covered.
1932d1e879ecSMiri Korenblit */
1933d1e879ecSMiri Korenblit iwl_mld_select_links(mld);
1934d1e879ecSMiri Korenblit }
1935d1e879ecSMiri Korenblit
1936d1e879ecSMiri Korenblit mld->scan.status &= ~mld->scan.uid_status[uid];
1937d1e879ecSMiri Korenblit
1938d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan completed: after update: scan_status=0x%x\n",
1939d1e879ecSMiri Korenblit mld->scan.status);
1940d1e879ecSMiri Korenblit
1941d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
1942d1e879ecSMiri Korenblit
1943d1e879ecSMiri Korenblit if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS &&
1944d1e879ecSMiri Korenblit notif->ebs_status != IWL_SCAN_EBS_INACTIVE)
1945d1e879ecSMiri Korenblit mld->scan.last_ebs_failed = true;
1946d1e879ecSMiri Korenblit }
1947d1e879ecSMiri Korenblit
1948d1e879ecSMiri Korenblit /* This function is used in nic restart flow, to inform mac80211 about scans
1949d1e879ecSMiri Korenblit * that were aborted by restart flow or by an assert.
1950d1e879ecSMiri Korenblit */
iwl_mld_report_scan_aborted(struct iwl_mld * mld)1951d1e879ecSMiri Korenblit void iwl_mld_report_scan_aborted(struct iwl_mld *mld)
1952d1e879ecSMiri Korenblit {
1953d1e879ecSMiri Korenblit int uid;
1954d1e879ecSMiri Korenblit
1955d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_REGULAR);
1956d1e879ecSMiri Korenblit if (uid >= 0) {
1957d1e879ecSMiri Korenblit struct cfg80211_scan_info info = {
1958d1e879ecSMiri Korenblit .aborted = true,
1959d1e879ecSMiri Korenblit };
1960d1e879ecSMiri Korenblit
1961d1e879ecSMiri Korenblit ieee80211_scan_completed(mld->hw, &info);
1962d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
1963d1e879ecSMiri Korenblit }
1964d1e879ecSMiri Korenblit
1965d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_SCHED);
1966d1e879ecSMiri Korenblit if (uid >= 0) {
1967d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED;
1968d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
1969d1e879ecSMiri Korenblit
1970d1e879ecSMiri Korenblit /* sched scan will be restarted by mac80211 in reconfig.
1971d1e879ecSMiri Korenblit * report to mac80211 that sched scan stopped only if we won't
1972d1e879ecSMiri Korenblit * restart the firmware.
1973d1e879ecSMiri Korenblit */
1974d1e879ecSMiri Korenblit if (!iwlwifi_mod_params.fw_restart)
1975d1e879ecSMiri Korenblit ieee80211_sched_scan_stopped(mld->hw);
1976d1e879ecSMiri Korenblit }
1977d1e879ecSMiri Korenblit
1978d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_INT_MLO);
1979d1e879ecSMiri Korenblit if (uid >= 0) {
1980d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan aborted\n");
1981d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE;
1982d1e879ecSMiri Korenblit }
1983d1e879ecSMiri Korenblit
1984d1e879ecSMiri Korenblit BUILD_BUG_ON(IWL_MLD_SCAN_NONE != 0);
1985d1e879ecSMiri Korenblit memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status));
1986d1e879ecSMiri Korenblit }
1987d1e879ecSMiri Korenblit
iwl_mld_alloc_scan_cmd(struct iwl_mld * mld)1988d1e879ecSMiri Korenblit int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
1989d1e879ecSMiri Korenblit {
1990d1e879ecSMiri Korenblit u8 scan_cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, SCAN_REQ_UMAC,
1991d1e879ecSMiri Korenblit IWL_FW_CMD_VER_UNKNOWN);
1992d1e879ecSMiri Korenblit size_t scan_cmd_size;
1993d1e879ecSMiri Korenblit
1994d1e879ecSMiri Korenblit if (scan_cmd_ver == 17) {
1995d1e879ecSMiri Korenblit scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17);
1996d1e879ecSMiri Korenblit } else {
1997d1e879ecSMiri Korenblit IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver);
1998d1e879ecSMiri Korenblit return -EINVAL;
1999d1e879ecSMiri Korenblit }
2000d1e879ecSMiri Korenblit
2001d1e879ecSMiri Korenblit mld->scan.cmd = kmalloc(scan_cmd_size, GFP_KERNEL);
2002d1e879ecSMiri Korenblit if (!mld->scan.cmd)
2003d1e879ecSMiri Korenblit return -ENOMEM;
2004d1e879ecSMiri Korenblit
2005d1e879ecSMiri Korenblit mld->scan.cmd_size = scan_cmd_size;
2006d1e879ecSMiri Korenblit
2007d1e879ecSMiri Korenblit return 0;
2008d1e879ecSMiri Korenblit }
2009