xref: /freebsd/contrib/wpa/wpa_supplicant/scan.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * WPA Supplicant - Scanning
34bc52338SCy Schubert  * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
9e28a4053SRui Paulo #include "utils/includes.h"
1039beb93cSSam Leffler 
11e28a4053SRui Paulo #include "utils/common.h"
12e28a4053SRui Paulo #include "utils/eloop.h"
13e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
145b9c547cSRui Paulo #include "common/wpa_ctrl.h"
1539beb93cSSam Leffler #include "config.h"
1639beb93cSSam Leffler #include "wpa_supplicant_i.h"
17e28a4053SRui Paulo #include "driver_i.h"
1839beb93cSSam Leffler #include "wps_supplicant.h"
19f05cddf9SRui Paulo #include "p2p_supplicant.h"
20f05cddf9SRui Paulo #include "p2p/p2p.h"
21f05cddf9SRui Paulo #include "hs20_supplicant.h"
22e28a4053SRui Paulo #include "notify.h"
23e28a4053SRui Paulo #include "bss.h"
24e28a4053SRui Paulo #include "scan.h"
255b9c547cSRui Paulo #include "mesh.h"
2639beb93cSSam Leffler 
27*a90b9d01SCy Schubert static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s);
28*a90b9d01SCy Schubert 
2939beb93cSSam Leffler 
wpa_supplicant_gen_assoc_event(struct wpa_supplicant * wpa_s)3039beb93cSSam Leffler static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
3139beb93cSSam Leffler {
3239beb93cSSam Leffler 	struct wpa_ssid *ssid;
3339beb93cSSam Leffler 	union wpa_event_data data;
3439beb93cSSam Leffler 
3539beb93cSSam Leffler 	ssid = wpa_supplicant_get_ssid(wpa_s);
3639beb93cSSam Leffler 	if (ssid == NULL)
3739beb93cSSam Leffler 		return;
3839beb93cSSam Leffler 
39e28a4053SRui Paulo 	if (wpa_s->current_ssid == NULL) {
4039beb93cSSam Leffler 		wpa_s->current_ssid = ssid;
41e28a4053SRui Paulo 		wpas_notify_network_changed(wpa_s);
42e28a4053SRui Paulo 	}
4339beb93cSSam Leffler 	wpa_supplicant_initiate_eapol(wpa_s);
44f05cddf9SRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
45f05cddf9SRui Paulo 		"network - generating associated event");
4639beb93cSSam Leffler 	os_memset(&data, 0, sizeof(data));
4739beb93cSSam Leffler 	wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
4839beb93cSSam Leffler }
4939beb93cSSam Leffler 
5039beb93cSSam Leffler 
5139beb93cSSam Leffler #ifdef CONFIG_WPS
wpas_wps_in_use(struct wpa_supplicant * wpa_s,enum wps_request_type * req_type)52f05cddf9SRui Paulo static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
5339beb93cSSam Leffler 			   enum wps_request_type *req_type)
5439beb93cSSam Leffler {
5539beb93cSSam Leffler 	struct wpa_ssid *ssid;
5639beb93cSSam Leffler 	int wps = 0;
5739beb93cSSam Leffler 
58f05cddf9SRui Paulo 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
5939beb93cSSam Leffler 		if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
6039beb93cSSam Leffler 			continue;
6139beb93cSSam Leffler 
6239beb93cSSam Leffler 		wps = 1;
6339beb93cSSam Leffler 		*req_type = wpas_wps_get_req_type(ssid);
64780fb4a2SCy Schubert 		if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1"))
6539beb93cSSam Leffler 			return 2;
6639beb93cSSam Leffler 	}
6739beb93cSSam Leffler 
68f05cddf9SRui Paulo #ifdef CONFIG_P2P
69f05cddf9SRui Paulo 	if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
70f05cddf9SRui Paulo 	    !wpa_s->conf->p2p_disabled) {
71f05cddf9SRui Paulo 		wpa_s->wps->dev.p2p = 1;
72f05cddf9SRui Paulo 		if (!wps) {
73f05cddf9SRui Paulo 			wps = 1;
74f05cddf9SRui Paulo 			*req_type = WPS_REQ_ENROLLEE_INFO;
75f05cddf9SRui Paulo 		}
76f05cddf9SRui Paulo 	}
77f05cddf9SRui Paulo #endif /* CONFIG_P2P */
78f05cddf9SRui Paulo 
7939beb93cSSam Leffler 	return wps;
8039beb93cSSam Leffler }
8139beb93cSSam Leffler #endif /* CONFIG_WPS */
8239beb93cSSam Leffler 
833157ba21SRui Paulo 
wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params * params,const u8 * mac_addr)84c1d255d3SCy Schubert static int wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params *params,
85c1d255d3SCy Schubert 					  const u8 *mac_addr)
86c1d255d3SCy Schubert {
87c1d255d3SCy Schubert 	u8 *tmp;
88c1d255d3SCy Schubert 
89c1d255d3SCy Schubert 	if (params->mac_addr) {
90c1d255d3SCy Schubert 		params->mac_addr_mask = NULL;
91c1d255d3SCy Schubert 		os_free(params->mac_addr);
92c1d255d3SCy Schubert 		params->mac_addr = NULL;
93c1d255d3SCy Schubert 	}
94c1d255d3SCy Schubert 
95c1d255d3SCy Schubert 	params->mac_addr_rand = 1;
96c1d255d3SCy Schubert 
97c1d255d3SCy Schubert 	if (!mac_addr)
98c1d255d3SCy Schubert 		return 0;
99c1d255d3SCy Schubert 
100c1d255d3SCy Schubert 	tmp = os_malloc(2 * ETH_ALEN);
101c1d255d3SCy Schubert 	if (!tmp)
102c1d255d3SCy Schubert 		return -1;
103c1d255d3SCy Schubert 
104c1d255d3SCy Schubert 	os_memcpy(tmp, mac_addr, 2 * ETH_ALEN);
105c1d255d3SCy Schubert 	params->mac_addr = tmp;
106c1d255d3SCy Schubert 	params->mac_addr_mask = tmp + ETH_ALEN;
107c1d255d3SCy Schubert 	return 0;
108c1d255d3SCy Schubert }
109c1d255d3SCy Schubert 
110c1d255d3SCy Schubert 
111f05cddf9SRui Paulo /**
112f05cddf9SRui Paulo  * wpa_supplicant_enabled_networks - Check whether there are enabled networks
113f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
114f05cddf9SRui Paulo  * Returns: 0 if no networks are enabled, >0 if networks are enabled
115f05cddf9SRui Paulo  *
116f05cddf9SRui Paulo  * This function is used to figure out whether any networks (or Interworking
117f05cddf9SRui Paulo  * with enabled credentials and auto_interworking) are present in the current
118f05cddf9SRui Paulo  * configuration.
119f05cddf9SRui Paulo  */
wpa_supplicant_enabled_networks(struct wpa_supplicant * wpa_s)120f05cddf9SRui Paulo int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
1213157ba21SRui Paulo {
122f05cddf9SRui Paulo 	struct wpa_ssid *ssid = wpa_s->conf->ssid;
123f05cddf9SRui Paulo 	int count = 0, disabled = 0;
1245b9c547cSRui Paulo 
1255b9c547cSRui Paulo 	if (wpa_s->p2p_mgmt)
1265b9c547cSRui Paulo 		return 0; /* no normal network profiles on p2p_mgmt interface */
1275b9c547cSRui Paulo 
1283157ba21SRui Paulo 	while (ssid) {
129f05cddf9SRui Paulo 		if (!wpas_network_disabled(wpa_s, ssid))
130f05cddf9SRui Paulo 			count++;
131f05cddf9SRui Paulo 		else
132f05cddf9SRui Paulo 			disabled++;
1333157ba21SRui Paulo 		ssid = ssid->next;
1343157ba21SRui Paulo 	}
135f05cddf9SRui Paulo 	if (wpa_s->conf->cred && wpa_s->conf->interworking &&
136f05cddf9SRui Paulo 	    wpa_s->conf->auto_interworking)
137f05cddf9SRui Paulo 		count++;
138f05cddf9SRui Paulo 	if (count == 0 && disabled > 0) {
139f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
140f05cddf9SRui Paulo 			"networks)", disabled);
141f05cddf9SRui Paulo 	}
142f05cddf9SRui Paulo 	return count;
1433157ba21SRui Paulo }
1443157ba21SRui Paulo 
1453157ba21SRui Paulo 
wpa_supplicant_assoc_try(struct wpa_supplicant * wpa_s,struct wpa_ssid * ssid)146e28a4053SRui Paulo static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
147e28a4053SRui Paulo 				     struct wpa_ssid *ssid)
148e28a4053SRui Paulo {
14985732ac8SCy Schubert 	int min_temp_disabled = 0;
15085732ac8SCy Schubert 
151e28a4053SRui Paulo 	while (ssid) {
15285732ac8SCy Schubert 		if (!wpas_network_disabled(wpa_s, ssid)) {
15385732ac8SCy Schubert 			int temp_disabled = wpas_temp_disabled(wpa_s, ssid);
15485732ac8SCy Schubert 
15585732ac8SCy Schubert 			if (temp_disabled <= 0)
156e28a4053SRui Paulo 				break;
15785732ac8SCy Schubert 
15885732ac8SCy Schubert 			if (!min_temp_disabled ||
15985732ac8SCy Schubert 			    temp_disabled < min_temp_disabled)
16085732ac8SCy Schubert 				min_temp_disabled = temp_disabled;
16185732ac8SCy Schubert 		}
162e28a4053SRui Paulo 		ssid = ssid->next;
163e28a4053SRui Paulo 	}
164e28a4053SRui Paulo 
165e28a4053SRui Paulo 	/* ap_scan=2 mode - try to associate with each SSID. */
166e28a4053SRui Paulo 	if (ssid == NULL) {
167f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
168e28a4053SRui Paulo 			"end of scan list - go back to beginning");
169e28a4053SRui Paulo 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
17085732ac8SCy Schubert 		wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0);
171e28a4053SRui Paulo 		return;
172e28a4053SRui Paulo 	}
173e28a4053SRui Paulo 	if (ssid->next) {
174e28a4053SRui Paulo 		/* Continue from the next SSID on the next attempt. */
175e28a4053SRui Paulo 		wpa_s->prev_scan_ssid = ssid;
176e28a4053SRui Paulo 	} else {
177e28a4053SRui Paulo 		/* Start from the beginning of the SSID list. */
178e28a4053SRui Paulo 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
179e28a4053SRui Paulo 	}
180e28a4053SRui Paulo 	wpa_supplicant_associate(wpa_s, NULL, ssid);
181e28a4053SRui Paulo }
182e28a4053SRui Paulo 
183e28a4053SRui Paulo 
wpas_trigger_scan_cb(struct wpa_radio_work * work,int deinit)1845b9c547cSRui Paulo static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
185e28a4053SRui Paulo {
1865b9c547cSRui Paulo 	struct wpa_supplicant *wpa_s = work->wpa_s;
1875b9c547cSRui Paulo 	struct wpa_driver_scan_params *params = work->ctx;
1885b9c547cSRui Paulo 	int ret;
189e28a4053SRui Paulo 
1905b9c547cSRui Paulo 	if (deinit) {
1915b9c547cSRui Paulo 		if (!work->started) {
1925b9c547cSRui Paulo 			wpa_scan_free_params(params);
193e28a4053SRui Paulo 			return;
194e28a4053SRui Paulo 		}
1955b9c547cSRui Paulo 		wpa_supplicant_notify_scanning(wpa_s, 0);
1965b9c547cSRui Paulo 		wpas_notify_scan_done(wpa_s, 0);
1975b9c547cSRui Paulo 		wpa_s->scan_work = NULL;
198e28a4053SRui Paulo 		return;
199e28a4053SRui Paulo 	}
2005b9c547cSRui Paulo 
201c1d255d3SCy Schubert 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
202c1d255d3SCy Schubert 	    wpa_s->wpa_state <= WPA_SCANNING)
203c1d255d3SCy Schubert 		wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan);
204c1d255d3SCy Schubert 
2055b9c547cSRui Paulo 	if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
2065b9c547cSRui Paulo 		wpa_msg(wpa_s, MSG_INFO,
2075b9c547cSRui Paulo 			"Failed to assign random MAC address for a scan");
208780fb4a2SCy Schubert 		wpa_scan_free_params(params);
209780fb4a2SCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
2105b9c547cSRui Paulo 		radio_work_done(work);
2115b9c547cSRui Paulo 		return;
212e28a4053SRui Paulo 	}
2135b9c547cSRui Paulo 
2145b9c547cSRui Paulo 	wpa_supplicant_notify_scanning(wpa_s, 1);
2155b9c547cSRui Paulo 
2165b9c547cSRui Paulo 	if (wpa_s->clear_driver_scan_cache) {
2175b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
2185b9c547cSRui Paulo 			   "Request driver to clear scan cache due to local BSS flush");
2195b9c547cSRui Paulo 		params->only_new_results = 1;
2205b9c547cSRui Paulo 	}
2215b9c547cSRui Paulo 	ret = wpa_drv_scan(wpa_s, params);
22285732ac8SCy Schubert 	/*
22385732ac8SCy Schubert 	 * Store the obtained vendor scan cookie (if any) in wpa_s context.
22485732ac8SCy Schubert 	 * The current design is to allow only one scan request on each
22585732ac8SCy Schubert 	 * interface, hence having this scan cookie stored in wpa_s context is
22685732ac8SCy Schubert 	 * fine for now.
22785732ac8SCy Schubert 	 *
22885732ac8SCy Schubert 	 * Revisit this logic if concurrent scan operations per interface
22985732ac8SCy Schubert 	 * is supported.
23085732ac8SCy Schubert 	 */
23185732ac8SCy Schubert 	if (ret == 0)
23285732ac8SCy Schubert 		wpa_s->curr_scan_cookie = params->scan_cookie;
2335b9c547cSRui Paulo 	wpa_scan_free_params(params);
2345b9c547cSRui Paulo 	work->ctx = NULL;
2355b9c547cSRui Paulo 	if (ret) {
23685732ac8SCy Schubert 		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
23785732ac8SCy Schubert 			!wpa_s->beacon_rep_data.token;
2385b9c547cSRui Paulo 
2395b9c547cSRui Paulo 		if (wpa_s->disconnected)
2405b9c547cSRui Paulo 			retry = 0;
2415b9c547cSRui Paulo 
242c1d255d3SCy Schubert 		/* do not retry if operation is not supported */
243c1d255d3SCy Schubert 		if (ret == -EOPNOTSUPP)
244c1d255d3SCy Schubert 			retry = 0;
245c1d255d3SCy Schubert 
2465b9c547cSRui Paulo 		wpa_supplicant_notify_scanning(wpa_s, 0);
2475b9c547cSRui Paulo 		wpas_notify_scan_done(wpa_s, 0);
2485b9c547cSRui Paulo 		if (wpa_s->wpa_state == WPA_SCANNING)
2495b9c547cSRui Paulo 			wpa_supplicant_set_state(wpa_s,
2505b9c547cSRui Paulo 						 wpa_s->scan_prev_wpa_state);
2515b9c547cSRui Paulo 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
2525b9c547cSRui Paulo 			ret, retry ? " retry=1" : "");
2535b9c547cSRui Paulo 		radio_work_done(work);
2545b9c547cSRui Paulo 
2555b9c547cSRui Paulo 		if (retry) {
2565b9c547cSRui Paulo 			/* Restore scan_req since we will try to scan again */
2575b9c547cSRui Paulo 			wpa_s->scan_req = wpa_s->last_scan_req;
2585b9c547cSRui Paulo 			wpa_supplicant_req_scan(wpa_s, 1, 0);
25985732ac8SCy Schubert 		} else if (wpa_s->scan_res_handler) {
26085732ac8SCy Schubert 			/* Clear the scan_res_handler */
26185732ac8SCy Schubert 			wpa_s->scan_res_handler = NULL;
2625b9c547cSRui Paulo 		}
26385732ac8SCy Schubert 
264*a90b9d01SCy Schubert #ifndef CONFIG_NO_RRM
26585732ac8SCy Schubert 		if (wpa_s->beacon_rep_data.token)
26685732ac8SCy Schubert 			wpas_rrm_refuse_request(wpa_s);
267*a90b9d01SCy Schubert #endif /* CONFIG_NO_RRM */
26885732ac8SCy Schubert 
2695b9c547cSRui Paulo 		return;
2705b9c547cSRui Paulo 	}
2715b9c547cSRui Paulo 
2725b9c547cSRui Paulo 	os_get_reltime(&wpa_s->scan_trigger_time);
2735b9c547cSRui Paulo 	wpa_s->scan_runs++;
2745b9c547cSRui Paulo 	wpa_s->normal_scans++;
2755b9c547cSRui Paulo 	wpa_s->own_scan_requested = 1;
2765b9c547cSRui Paulo 	wpa_s->clear_driver_scan_cache = 0;
2775b9c547cSRui Paulo 	wpa_s->scan_work = work;
278e28a4053SRui Paulo }
279e28a4053SRui Paulo 
280e28a4053SRui Paulo 
281f05cddf9SRui Paulo /**
282f05cddf9SRui Paulo  * wpa_supplicant_trigger_scan - Request driver to start a scan
283f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
284f05cddf9SRui Paulo  * @params: Scan parameters
285*a90b9d01SCy Schubert  * @default_ies: Whether or not to use the default IEs in the Probe Request
286*a90b9d01SCy Schubert  * frames. Note that this will free any existing IEs set in @params, so this
287*a90b9d01SCy Schubert  * shouldn't be set if the IEs have already been set with
288*a90b9d01SCy Schubert  * wpa_supplicant_extra_ies(). Otherwise, wpabuf_free() will lead to a
289*a90b9d01SCy Schubert  * double-free.
290*a90b9d01SCy Schubert  * @next: Whether or not to perform this scan as the next radio work
291f05cddf9SRui Paulo  * Returns: 0 on success, -1 on failure
292f05cddf9SRui Paulo  */
wpa_supplicant_trigger_scan(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,bool default_ies,bool next)293e28a4053SRui Paulo int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
294*a90b9d01SCy Schubert 				struct wpa_driver_scan_params *params,
295*a90b9d01SCy Schubert 				bool default_ies, bool next)
296e28a4053SRui Paulo {
2975b9c547cSRui Paulo 	struct wpa_driver_scan_params *ctx;
298*a90b9d01SCy Schubert 	struct wpabuf *ies = NULL;
299e28a4053SRui Paulo 
3005b9c547cSRui Paulo 	if (wpa_s->scan_work) {
3015b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
3025b9c547cSRui Paulo 		return -1;
303f05cddf9SRui Paulo 	}
304f05cddf9SRui Paulo 
305*a90b9d01SCy Schubert 	if (default_ies) {
306*a90b9d01SCy Schubert 		if (params->extra_ies_len) {
307*a90b9d01SCy Schubert 			os_free((u8 *) params->extra_ies);
308*a90b9d01SCy Schubert 			params->extra_ies = NULL;
309*a90b9d01SCy Schubert 			params->extra_ies_len = 0;
310*a90b9d01SCy Schubert 		}
311*a90b9d01SCy Schubert 		ies = wpa_supplicant_extra_ies(wpa_s);
312*a90b9d01SCy Schubert 		if (ies) {
313*a90b9d01SCy Schubert 			params->extra_ies = wpabuf_head(ies);
314*a90b9d01SCy Schubert 			params->extra_ies_len = wpabuf_len(ies);
315*a90b9d01SCy Schubert 		}
316*a90b9d01SCy Schubert 	}
3175b9c547cSRui Paulo 	ctx = wpa_scan_clone_params(params);
318*a90b9d01SCy Schubert 	if (ies) {
319*a90b9d01SCy Schubert 		wpabuf_free(ies);
320*a90b9d01SCy Schubert 		params->extra_ies = NULL;
321*a90b9d01SCy Schubert 		params->extra_ies_len = 0;
322*a90b9d01SCy Schubert 	}
323*a90b9d01SCy Schubert 	wpa_s->last_scan_all_chan = !params->freqs;
324*a90b9d01SCy Schubert 	wpa_s->last_scan_non_coloc_6ghz = params->non_coloc_6ghz;
325*a90b9d01SCy Schubert 
326*a90b9d01SCy Schubert 	if (wpa_s->crossed_6ghz_dom) {
327*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "First scan after crossing 6 GHz domain");
328*a90b9d01SCy Schubert 		wpa_s->crossed_6ghz_dom = false;
329*a90b9d01SCy Schubert 	}
330*a90b9d01SCy Schubert 
331780fb4a2SCy Schubert 	if (!ctx ||
332*a90b9d01SCy Schubert 	    radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb,
333*a90b9d01SCy Schubert 			   ctx) < 0) {
3345b9c547cSRui Paulo 		wpa_scan_free_params(ctx);
335780fb4a2SCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
3365b9c547cSRui Paulo 		return -1;
3375b9c547cSRui Paulo 	}
3385b9c547cSRui Paulo 
339*a90b9d01SCy Schubert 	wpa_s->wps_scan_done = false;
340*a90b9d01SCy Schubert 
3415b9c547cSRui Paulo 	return 0;
342f05cddf9SRui Paulo }
343f05cddf9SRui Paulo 
344f05cddf9SRui Paulo 
345f05cddf9SRui Paulo static void
wpa_supplicant_delayed_sched_scan_timeout(void * eloop_ctx,void * timeout_ctx)346f05cddf9SRui Paulo wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
347f05cddf9SRui Paulo {
348f05cddf9SRui Paulo 	struct wpa_supplicant *wpa_s = eloop_ctx;
349f05cddf9SRui Paulo 
350f05cddf9SRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
351f05cddf9SRui Paulo 
352f05cddf9SRui Paulo 	if (wpa_supplicant_req_sched_scan(wpa_s))
353f05cddf9SRui Paulo 		wpa_supplicant_req_scan(wpa_s, 0, 0);
354f05cddf9SRui Paulo }
355f05cddf9SRui Paulo 
356f05cddf9SRui Paulo 
357f05cddf9SRui Paulo static void
wpa_supplicant_sched_scan_timeout(void * eloop_ctx,void * timeout_ctx)358f05cddf9SRui Paulo wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
359f05cddf9SRui Paulo {
360f05cddf9SRui Paulo 	struct wpa_supplicant *wpa_s = eloop_ctx;
361f05cddf9SRui Paulo 
362f05cddf9SRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
363f05cddf9SRui Paulo 
364f05cddf9SRui Paulo 	wpa_s->sched_scan_timed_out = 1;
365f05cddf9SRui Paulo 	wpa_supplicant_cancel_sched_scan(wpa_s);
366f05cddf9SRui Paulo }
367f05cddf9SRui Paulo 
368f05cddf9SRui Paulo 
369780fb4a2SCy Schubert static int
wpa_supplicant_start_sched_scan(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)370780fb4a2SCy Schubert wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
371780fb4a2SCy Schubert 				struct wpa_driver_scan_params *params)
372f05cddf9SRui Paulo {
373f05cddf9SRui Paulo 	int ret;
374f05cddf9SRui Paulo 
375f05cddf9SRui Paulo 	wpa_supplicant_notify_scanning(wpa_s, 1);
376780fb4a2SCy Schubert 	ret = wpa_drv_sched_scan(wpa_s, params);
377f05cddf9SRui Paulo 	if (ret)
378f05cddf9SRui Paulo 		wpa_supplicant_notify_scanning(wpa_s, 0);
379f05cddf9SRui Paulo 	else
380f05cddf9SRui Paulo 		wpa_s->sched_scanning = 1;
381f05cddf9SRui Paulo 
382f05cddf9SRui Paulo 	return ret;
383f05cddf9SRui Paulo }
384f05cddf9SRui Paulo 
385f05cddf9SRui Paulo 
wpa_supplicant_stop_sched_scan(struct wpa_supplicant * wpa_s)386780fb4a2SCy Schubert static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
387f05cddf9SRui Paulo {
388f05cddf9SRui Paulo 	int ret;
389f05cddf9SRui Paulo 
390f05cddf9SRui Paulo 	ret = wpa_drv_stop_sched_scan(wpa_s);
391f05cddf9SRui Paulo 	if (ret) {
392f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
393f05cddf9SRui Paulo 		/* TODO: what to do if stopping fails? */
394f05cddf9SRui Paulo 		return -1;
395f05cddf9SRui Paulo 	}
396e28a4053SRui Paulo 
397e28a4053SRui Paulo 	return ret;
398e28a4053SRui Paulo }
399e28a4053SRui Paulo 
400e28a4053SRui Paulo 
401e28a4053SRui Paulo static struct wpa_driver_scan_filter *
wpa_supplicant_build_filter_ssids(struct wpa_config * conf,size_t * num_ssids)402e28a4053SRui Paulo wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
403e28a4053SRui Paulo {
404e28a4053SRui Paulo 	struct wpa_driver_scan_filter *ssids;
405e28a4053SRui Paulo 	struct wpa_ssid *ssid;
406e28a4053SRui Paulo 	size_t count;
407e28a4053SRui Paulo 
408e28a4053SRui Paulo 	*num_ssids = 0;
409e28a4053SRui Paulo 	if (!conf->filter_ssids)
410e28a4053SRui Paulo 		return NULL;
411e28a4053SRui Paulo 
412e28a4053SRui Paulo 	for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
413e28a4053SRui Paulo 		if (ssid->ssid && ssid->ssid_len)
414e28a4053SRui Paulo 			count++;
415e28a4053SRui Paulo 	}
416e28a4053SRui Paulo 	if (count == 0)
417e28a4053SRui Paulo 		return NULL;
4185b9c547cSRui Paulo 	ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
419e28a4053SRui Paulo 	if (ssids == NULL)
420e28a4053SRui Paulo 		return NULL;
421e28a4053SRui Paulo 
422e28a4053SRui Paulo 	for (ssid = conf->ssid; ssid; ssid = ssid->next) {
423e28a4053SRui Paulo 		if (!ssid->ssid || !ssid->ssid_len)
424e28a4053SRui Paulo 			continue;
425e28a4053SRui Paulo 		os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
426e28a4053SRui Paulo 		ssids[*num_ssids].ssid_len = ssid->ssid_len;
427e28a4053SRui Paulo 		(*num_ssids)++;
428e28a4053SRui Paulo 	}
429e28a4053SRui Paulo 
430e28a4053SRui Paulo 	return ssids;
431e28a4053SRui Paulo }
432e28a4053SRui Paulo 
433e28a4053SRui Paulo 
wpa_supplicant_optimize_freqs(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)434f05cddf9SRui Paulo static void wpa_supplicant_optimize_freqs(
435f05cddf9SRui Paulo 	struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
43639beb93cSSam Leffler {
437f05cddf9SRui Paulo #ifdef CONFIG_P2P
438f05cddf9SRui Paulo 	if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
439f05cddf9SRui Paulo 	    wpa_s->go_params) {
440f05cddf9SRui Paulo 		/* Optimize provisioning state scan based on GO information */
441f05cddf9SRui Paulo 		if (wpa_s->p2p_in_provisioning < 5 &&
442f05cddf9SRui Paulo 		    wpa_s->go_params->freq > 0) {
443f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
444f05cddf9SRui Paulo 				"preferred frequency %d MHz",
445f05cddf9SRui Paulo 				wpa_s->go_params->freq);
4465b9c547cSRui Paulo 			params->freqs = os_calloc(2, sizeof(int));
447f05cddf9SRui Paulo 			if (params->freqs)
448f05cddf9SRui Paulo 				params->freqs[0] = wpa_s->go_params->freq;
449f05cddf9SRui Paulo 		} else if (wpa_s->p2p_in_provisioning < 8 &&
450f05cddf9SRui Paulo 			   wpa_s->go_params->freq_list[0]) {
451f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
452f05cddf9SRui Paulo 				"channels");
453f05cddf9SRui Paulo 			int_array_concat(&params->freqs,
454f05cddf9SRui Paulo 					 wpa_s->go_params->freq_list);
455f05cddf9SRui Paulo 			if (params->freqs)
456f05cddf9SRui Paulo 				int_array_sort_unique(params->freqs);
457f05cddf9SRui Paulo 		}
458f05cddf9SRui Paulo 		wpa_s->p2p_in_provisioning++;
459f05cddf9SRui Paulo 	}
4605b9c547cSRui Paulo 
4615b9c547cSRui Paulo 	if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
462*a90b9d01SCy Schubert 		struct wpa_ssid *ssid = wpa_s->current_ssid;
463*a90b9d01SCy Schubert 
464*a90b9d01SCy Schubert 		/*
465*a90b9d01SCy Schubert 		 * Perform a single-channel scan if the GO has already been
466*a90b9d01SCy Schubert 		 * discovered on another non-P2P interface. Note that a scan
467*a90b9d01SCy Schubert 		 * initiated by a P2P interface (e.g., the device interface)
468*a90b9d01SCy Schubert 		 * should already have sufficient IEs and scan results will be
469*a90b9d01SCy Schubert 		 * fetched on interface creation in that case.
470*a90b9d01SCy Schubert 		 */
471*a90b9d01SCy Schubert 		if (wpa_s->p2p_in_invitation == 1 && ssid) {
472*a90b9d01SCy Schubert 			struct wpa_supplicant *ifs;
473*a90b9d01SCy Schubert 			struct wpa_bss *bss = NULL;
474*a90b9d01SCy Schubert 			const u8 *bssid = ssid->bssid_set ? ssid->bssid : NULL;
475*a90b9d01SCy Schubert 
476*a90b9d01SCy Schubert 			dl_list_for_each(ifs, &wpa_s->radio->ifaces,
477*a90b9d01SCy Schubert 					 struct wpa_supplicant, radio_list) {
478*a90b9d01SCy Schubert 				bss = wpa_bss_get(ifs, bssid, ssid->ssid,
479*a90b9d01SCy Schubert 						  ssid->ssid_len);
480*a90b9d01SCy Schubert 				if (bss)
481*a90b9d01SCy Schubert 					break;
482*a90b9d01SCy Schubert 			}
483*a90b9d01SCy Schubert 			if (bss && !disabled_freq(wpa_s, bss->freq)) {
484*a90b9d01SCy Schubert 				params->freqs = os_calloc(2, sizeof(int));
485*a90b9d01SCy Schubert 				if (params->freqs) {
486*a90b9d01SCy Schubert 					wpa_dbg(wpa_s, MSG_DEBUG,
487*a90b9d01SCy Schubert 						"P2P: Scan only the known GO frequency %d MHz during invitation",
488*a90b9d01SCy Schubert 						bss->freq);
489*a90b9d01SCy Schubert 					params->freqs[0] = bss->freq;
490*a90b9d01SCy Schubert 				}
491*a90b9d01SCy Schubert 			}
492*a90b9d01SCy Schubert 		}
493*a90b9d01SCy Schubert 
4945b9c547cSRui Paulo 		/*
4955b9c547cSRui Paulo 		 * Optimize scan based on GO information during persistent
4965b9c547cSRui Paulo 		 * group reinvocation
4975b9c547cSRui Paulo 		 */
498*a90b9d01SCy Schubert 		if (!params->freqs && wpa_s->p2p_in_invitation < 5 &&
4995b9c547cSRui Paulo 		    wpa_s->p2p_invite_go_freq > 0) {
500*a90b9d01SCy Schubert 			if (wpa_s->p2p_invite_go_freq == 2 ||
501*a90b9d01SCy Schubert 			    wpa_s->p2p_invite_go_freq == 5) {
502*a90b9d01SCy Schubert 				enum hostapd_hw_mode mode;
503*a90b9d01SCy Schubert 
504*a90b9d01SCy Schubert 				wpa_dbg(wpa_s, MSG_DEBUG,
505*a90b9d01SCy Schubert 					"P2P: Scan only GO preferred band %d GHz during invitation",
506*a90b9d01SCy Schubert 					wpa_s->p2p_invite_go_freq);
507*a90b9d01SCy Schubert 
508*a90b9d01SCy Schubert 				if (!wpa_s->hw.modes)
509*a90b9d01SCy Schubert 					return;
510*a90b9d01SCy Schubert 				mode = wpa_s->p2p_invite_go_freq == 5 ?
511*a90b9d01SCy Schubert 					HOSTAPD_MODE_IEEE80211A :
512*a90b9d01SCy Schubert 					HOSTAPD_MODE_IEEE80211G;
513*a90b9d01SCy Schubert 				if (wpa_s->p2p_in_invitation <= 2)
514*a90b9d01SCy Schubert 					wpa_add_scan_freqs_list(wpa_s, mode,
515*a90b9d01SCy Schubert 								params, false,
516*a90b9d01SCy Schubert 								false, true);
517*a90b9d01SCy Schubert 				if (!params->freqs || params->freqs[0] == 0)
518*a90b9d01SCy Schubert 					wpa_add_scan_freqs_list(wpa_s, mode,
519*a90b9d01SCy Schubert 								params, false,
520*a90b9d01SCy Schubert 								false, false);
521*a90b9d01SCy Schubert 			} else {
522*a90b9d01SCy Schubert 				wpa_dbg(wpa_s, MSG_DEBUG,
523*a90b9d01SCy Schubert 					"P2P: Scan only GO preferred frequency %d MHz during invitation",
5245b9c547cSRui Paulo 					wpa_s->p2p_invite_go_freq);
5255b9c547cSRui Paulo 				params->freqs = os_calloc(2, sizeof(int));
5265b9c547cSRui Paulo 				if (params->freqs)
527*a90b9d01SCy Schubert 					params->freqs[0] =
528*a90b9d01SCy Schubert 					    wpa_s->p2p_invite_go_freq;
529*a90b9d01SCy Schubert 			}
5305b9c547cSRui Paulo 		}
5315b9c547cSRui Paulo 		wpa_s->p2p_in_invitation++;
5325b9c547cSRui Paulo 		if (wpa_s->p2p_in_invitation > 20) {
5335b9c547cSRui Paulo 			/*
5345b9c547cSRui Paulo 			 * This should not really happen since the variable is
5355b9c547cSRui Paulo 			 * cleared on group removal, but if it does happen, make
5365b9c547cSRui Paulo 			 * sure we do not get stuck in special invitation scan
5375b9c547cSRui Paulo 			 * mode.
5385b9c547cSRui Paulo 			 */
5395b9c547cSRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
5405b9c547cSRui Paulo 			wpa_s->p2p_in_invitation = 0;
541*a90b9d01SCy Schubert 			wpa_s->p2p_retry_limit = 0;
5425b9c547cSRui Paulo 		}
5435b9c547cSRui Paulo 	}
544f05cddf9SRui Paulo #endif /* CONFIG_P2P */
545f05cddf9SRui Paulo 
546f05cddf9SRui Paulo #ifdef CONFIG_WPS
547f05cddf9SRui Paulo 	if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
548f05cddf9SRui Paulo 		/*
549f05cddf9SRui Paulo 		 * Optimize post-provisioning scan based on channel used
550f05cddf9SRui Paulo 		 * during provisioning.
551f05cddf9SRui Paulo 		 */
552f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
553f05cddf9SRui Paulo 			"that was used during provisioning", wpa_s->wps_freq);
5545b9c547cSRui Paulo 		params->freqs = os_calloc(2, sizeof(int));
555f05cddf9SRui Paulo 		if (params->freqs)
556f05cddf9SRui Paulo 			params->freqs[0] = wpa_s->wps_freq;
557f05cddf9SRui Paulo 		wpa_s->after_wps--;
5585b9c547cSRui Paulo 	} else if (wpa_s->after_wps)
5595b9c547cSRui Paulo 		wpa_s->after_wps--;
560f05cddf9SRui Paulo 
561f05cddf9SRui Paulo 	if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
562f05cddf9SRui Paulo 	{
563f05cddf9SRui Paulo 		/* Optimize provisioning scan based on already known channel */
564f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
565f05cddf9SRui Paulo 			wpa_s->wps_freq);
5665b9c547cSRui Paulo 		params->freqs = os_calloc(2, sizeof(int));
567f05cddf9SRui Paulo 		if (params->freqs)
568f05cddf9SRui Paulo 			params->freqs[0] = wpa_s->wps_freq;
569f05cddf9SRui Paulo 		wpa_s->known_wps_freq = 0; /* only do this once */
570f05cddf9SRui Paulo 	}
571f05cddf9SRui Paulo #endif /* CONFIG_WPS */
572f05cddf9SRui Paulo }
573f05cddf9SRui Paulo 
574f05cddf9SRui Paulo 
575f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING
wpas_add_interworking_elements(struct wpa_supplicant * wpa_s,struct wpabuf * buf)576f05cddf9SRui Paulo static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
577f05cddf9SRui Paulo 					   struct wpabuf *buf)
578f05cddf9SRui Paulo {
579f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
580f05cddf9SRui Paulo 	wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
581f05cddf9SRui Paulo 		      1 + ETH_ALEN);
582f05cddf9SRui Paulo 	wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
583f05cddf9SRui Paulo 	/* No Venue Info */
584f05cddf9SRui Paulo 	if (!is_zero_ether_addr(wpa_s->conf->hessid))
585f05cddf9SRui Paulo 		wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
586f05cddf9SRui Paulo }
587f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */
588f05cddf9SRui Paulo 
589f05cddf9SRui Paulo 
59085732ac8SCy Schubert #ifdef CONFIG_MBO
wpas_fils_req_param_add_max_channel(struct wpa_supplicant * wpa_s,struct wpabuf ** ie)59185732ac8SCy Schubert static void wpas_fils_req_param_add_max_channel(struct wpa_supplicant *wpa_s,
59285732ac8SCy Schubert 						struct wpabuf **ie)
59385732ac8SCy Schubert {
59485732ac8SCy Schubert 	if (wpabuf_resize(ie, 5)) {
59585732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
59685732ac8SCy Schubert 			   "Failed to allocate space for FILS Request Parameters element");
59785732ac8SCy Schubert 		return;
59885732ac8SCy Schubert 	}
59985732ac8SCy Schubert 
60085732ac8SCy Schubert 	/* FILS Request Parameters element */
60185732ac8SCy Schubert 	wpabuf_put_u8(*ie, WLAN_EID_EXTENSION);
60285732ac8SCy Schubert 	wpabuf_put_u8(*ie, 3); /* FILS Request attribute length */
60385732ac8SCy Schubert 	wpabuf_put_u8(*ie, WLAN_EID_EXT_FILS_REQ_PARAMS);
60485732ac8SCy Schubert 	/* Parameter control bitmap */
60585732ac8SCy Schubert 	wpabuf_put_u8(*ie, 0);
60685732ac8SCy Schubert 	/* Max Channel Time field - contains the value of MaxChannelTime
60785732ac8SCy Schubert 	 * parameter of the MLME-SCAN.request primitive represented in units of
60885732ac8SCy Schubert 	 * TUs, as an unsigned integer. A Max Channel Time field value of 255
60985732ac8SCy Schubert 	 * is used to indicate any duration of more than 254 TUs, or an
61085732ac8SCy Schubert 	 * unspecified or unknown duration. (IEEE Std 802.11ai-2016, 9.4.2.178)
61185732ac8SCy Schubert 	 */
61285732ac8SCy Schubert 	wpabuf_put_u8(*ie, 255);
61385732ac8SCy Schubert }
61485732ac8SCy Schubert #endif /* CONFIG_MBO */
61585732ac8SCy Schubert 
61685732ac8SCy Schubert 
wpa_supplicant_set_default_scan_ies(struct wpa_supplicant * wpa_s)617780fb4a2SCy Schubert void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
618780fb4a2SCy Schubert {
619780fb4a2SCy Schubert 	struct wpabuf *default_ies = NULL;
620780fb4a2SCy Schubert 	u8 ext_capab[18];
621c1d255d3SCy Schubert 	int ext_capab_len, frame_id;
622780fb4a2SCy Schubert 	enum wpa_driver_if_type type = WPA_IF_STATION;
623780fb4a2SCy Schubert 
624780fb4a2SCy Schubert #ifdef CONFIG_P2P
625780fb4a2SCy Schubert 	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
626780fb4a2SCy Schubert 		type = WPA_IF_P2P_CLIENT;
627780fb4a2SCy Schubert #endif /* CONFIG_P2P */
628780fb4a2SCy Schubert 
629780fb4a2SCy Schubert 	wpa_drv_get_ext_capa(wpa_s, type);
630780fb4a2SCy Schubert 
631780fb4a2SCy Schubert 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
632*a90b9d01SCy Schubert 					     sizeof(ext_capab), NULL);
633780fb4a2SCy Schubert 	if (ext_capab_len > 0 &&
634780fb4a2SCy Schubert 	    wpabuf_resize(&default_ies, ext_capab_len) == 0)
635780fb4a2SCy Schubert 		wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
636780fb4a2SCy Schubert 
637780fb4a2SCy Schubert #ifdef CONFIG_MBO
63885732ac8SCy Schubert 	if (wpa_s->enable_oce & OCE_STA)
63985732ac8SCy Schubert 		wpas_fils_req_param_add_max_channel(wpa_s, &default_ies);
64085732ac8SCy Schubert 	/* Send MBO and OCE capabilities */
64185732ac8SCy Schubert 	if (wpabuf_resize(&default_ies, 12) == 0)
642780fb4a2SCy Schubert 		wpas_mbo_scan_ie(wpa_s, default_ies);
643780fb4a2SCy Schubert #endif /* CONFIG_MBO */
644780fb4a2SCy Schubert 
645c1d255d3SCy Schubert 	if (type == WPA_IF_P2P_CLIENT)
646c1d255d3SCy Schubert 		frame_id = VENDOR_ELEM_PROBE_REQ_P2P;
647c1d255d3SCy Schubert 	else
648c1d255d3SCy Schubert 		frame_id = VENDOR_ELEM_PROBE_REQ;
649c1d255d3SCy Schubert 
650c1d255d3SCy Schubert 	if (wpa_s->vendor_elem[frame_id]) {
651c1d255d3SCy Schubert 		size_t len;
652c1d255d3SCy Schubert 
653c1d255d3SCy Schubert 		len = wpabuf_len(wpa_s->vendor_elem[frame_id]);
654c1d255d3SCy Schubert 		if (len > 0 && wpabuf_resize(&default_ies, len) == 0)
655c1d255d3SCy Schubert 			wpabuf_put_buf(default_ies,
656c1d255d3SCy Schubert 				       wpa_s->vendor_elem[frame_id]);
657c1d255d3SCy Schubert 	}
658c1d255d3SCy Schubert 
659780fb4a2SCy Schubert 	if (default_ies)
660780fb4a2SCy Schubert 		wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies),
661780fb4a2SCy Schubert 					     wpabuf_len(default_ies));
662780fb4a2SCy Schubert 	wpabuf_free(default_ies);
663780fb4a2SCy Schubert }
664780fb4a2SCy Schubert 
665780fb4a2SCy Schubert 
wpa_supplicant_ml_probe_ie(int mld_id,u16 links)666*a90b9d01SCy Schubert static struct wpabuf * wpa_supplicant_ml_probe_ie(int mld_id, u16 links)
667*a90b9d01SCy Schubert {
668*a90b9d01SCy Schubert 	struct wpabuf *extra_ie;
669*a90b9d01SCy Schubert 	u16 control = MULTI_LINK_CONTROL_TYPE_PROBE_REQ;
670*a90b9d01SCy Schubert 	size_t len = 3 + 4 + 4 * MAX_NUM_MLD_LINKS;
671*a90b9d01SCy Schubert 	u8 link_id;
672*a90b9d01SCy Schubert 	u8 *len_pos;
673*a90b9d01SCy Schubert 
674*a90b9d01SCy Schubert 	if (mld_id >= 0) {
675*a90b9d01SCy Schubert 		control |= EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID;
676*a90b9d01SCy Schubert 		len++;
677*a90b9d01SCy Schubert 	}
678*a90b9d01SCy Schubert 
679*a90b9d01SCy Schubert 	extra_ie = wpabuf_alloc(len);
680*a90b9d01SCy Schubert 	if (!extra_ie)
681*a90b9d01SCy Schubert 		return NULL;
682*a90b9d01SCy Schubert 
683*a90b9d01SCy Schubert 	wpabuf_put_u8(extra_ie, WLAN_EID_EXTENSION);
684*a90b9d01SCy Schubert 	len_pos = wpabuf_put(extra_ie, 1);
685*a90b9d01SCy Schubert 	wpabuf_put_u8(extra_ie, WLAN_EID_EXT_MULTI_LINK);
686*a90b9d01SCy Schubert 
687*a90b9d01SCy Schubert 	wpabuf_put_le16(extra_ie, control);
688*a90b9d01SCy Schubert 
689*a90b9d01SCy Schubert 	/* common info length and MLD ID (if requested) */
690*a90b9d01SCy Schubert 	if (mld_id >= 0) {
691*a90b9d01SCy Schubert 		wpabuf_put_u8(extra_ie, 2);
692*a90b9d01SCy Schubert 		wpabuf_put_u8(extra_ie, mld_id);
693*a90b9d01SCy Schubert 
694*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: ML probe targeted at MLD ID %d",
695*a90b9d01SCy Schubert 			   mld_id);
696*a90b9d01SCy Schubert 	} else {
697*a90b9d01SCy Schubert 		wpabuf_put_u8(extra_ie, 1);
698*a90b9d01SCy Schubert 
699*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: ML probe targeted at receiving AP");
700*a90b9d01SCy Schubert 	}
701*a90b9d01SCy Schubert 
702*a90b9d01SCy Schubert 	if (!links)
703*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: Probing all links");
704*a90b9d01SCy Schubert 	else
705*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links);
706*a90b9d01SCy Schubert 
707*a90b9d01SCy Schubert 	for_each_link(links, link_id) {
708*a90b9d01SCy Schubert 		wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
709*a90b9d01SCy Schubert 
710*a90b9d01SCy Schubert 		/* Subelement length includes only the control */
711*a90b9d01SCy Schubert 		wpabuf_put_u8(extra_ie, 2);
712*a90b9d01SCy Schubert 
713*a90b9d01SCy Schubert 		control = link_id | EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK;
714*a90b9d01SCy Schubert 
715*a90b9d01SCy Schubert 		wpabuf_put_le16(extra_ie, control);
716*a90b9d01SCy Schubert 	}
717*a90b9d01SCy Schubert 
718*a90b9d01SCy Schubert 	*len_pos = (u8 *) wpabuf_put(extra_ie, 0) - len_pos - 1;
719*a90b9d01SCy Schubert 
720*a90b9d01SCy Schubert 	return extra_ie;
721*a90b9d01SCy Schubert }
722*a90b9d01SCy Schubert 
723*a90b9d01SCy Schubert 
wpa_supplicant_extra_ies(struct wpa_supplicant * wpa_s)724f05cddf9SRui Paulo static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
725f05cddf9SRui Paulo {
726f05cddf9SRui Paulo 	struct wpabuf *extra_ie = NULL;
727325151a3SRui Paulo 	u8 ext_capab[18];
728325151a3SRui Paulo 	int ext_capab_len;
72939beb93cSSam Leffler #ifdef CONFIG_WPS
730e28a4053SRui Paulo 	int wps = 0;
73139beb93cSSam Leffler 	enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
73239beb93cSSam Leffler #endif /* CONFIG_WPS */
733f05cddf9SRui Paulo 
734*a90b9d01SCy Schubert 	if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) {
735*a90b9d01SCy Schubert 		extra_ie = wpa_supplicant_ml_probe_ie(wpa_s->ml_probe_mld_id,
736*a90b9d01SCy Schubert 						      wpa_s->ml_probe_links);
737*a90b9d01SCy Schubert 
738*a90b9d01SCy Schubert 		/* No other elements should be included in the probe request */
739*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "MLD: Scan including only ML element");
740*a90b9d01SCy Schubert 		return extra_ie;
741*a90b9d01SCy Schubert 	}
742*a90b9d01SCy Schubert 
743780fb4a2SCy Schubert #ifdef CONFIG_P2P
744780fb4a2SCy Schubert 	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
745780fb4a2SCy Schubert 		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
746780fb4a2SCy Schubert 	else
747780fb4a2SCy Schubert #endif /* CONFIG_P2P */
748780fb4a2SCy Schubert 		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
749780fb4a2SCy Schubert 
750325151a3SRui Paulo 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
751*a90b9d01SCy Schubert 					     sizeof(ext_capab), NULL);
752325151a3SRui Paulo 	if (ext_capab_len > 0 &&
753325151a3SRui Paulo 	    wpabuf_resize(&extra_ie, ext_capab_len) == 0)
754325151a3SRui Paulo 		wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
755325151a3SRui Paulo 
756f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING
757f05cddf9SRui Paulo 	if (wpa_s->conf->interworking &&
758f05cddf9SRui Paulo 	    wpabuf_resize(&extra_ie, 100) == 0)
759f05cddf9SRui Paulo 		wpas_add_interworking_elements(wpa_s, extra_ie);
760f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */
761f05cddf9SRui Paulo 
76285732ac8SCy Schubert #ifdef CONFIG_MBO
76385732ac8SCy Schubert 	if (wpa_s->enable_oce & OCE_STA)
76485732ac8SCy Schubert 		wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie);
76585732ac8SCy Schubert #endif /* CONFIG_MBO */
76685732ac8SCy Schubert 
767f05cddf9SRui Paulo #ifdef CONFIG_WPS
768f05cddf9SRui Paulo 	wps = wpas_wps_in_use(wpa_s, &req_type);
769f05cddf9SRui Paulo 
770f05cddf9SRui Paulo 	if (wps) {
771f05cddf9SRui Paulo 		struct wpabuf *wps_ie;
772f05cddf9SRui Paulo 		wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
773f05cddf9SRui Paulo 						DEV_PW_DEFAULT,
774f05cddf9SRui Paulo 						&wpa_s->wps->dev,
775f05cddf9SRui Paulo 						wpa_s->wps->uuid, req_type,
776f05cddf9SRui Paulo 						0, NULL);
777f05cddf9SRui Paulo 		if (wps_ie) {
778f05cddf9SRui Paulo 			if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
779f05cddf9SRui Paulo 				wpabuf_put_buf(extra_ie, wps_ie);
780f05cddf9SRui Paulo 			wpabuf_free(wps_ie);
781f05cddf9SRui Paulo 		}
782f05cddf9SRui Paulo 	}
783f05cddf9SRui Paulo 
784f05cddf9SRui Paulo #ifdef CONFIG_P2P
785f05cddf9SRui Paulo 	if (wps) {
786f05cddf9SRui Paulo 		size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
787f05cddf9SRui Paulo 		if (wpabuf_resize(&extra_ie, ielen) == 0)
788f05cddf9SRui Paulo 			wpas_p2p_scan_ie(wpa_s, extra_ie);
789f05cddf9SRui Paulo 	}
790f05cddf9SRui Paulo #endif /* CONFIG_P2P */
791f05cddf9SRui Paulo 
7925b9c547cSRui Paulo 	wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
7935b9c547cSRui Paulo 
794f05cddf9SRui Paulo #endif /* CONFIG_WPS */
795f05cddf9SRui Paulo 
7965b9c547cSRui Paulo #ifdef CONFIG_HS20
7974bc52338SCy Schubert 	if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0)
7984bc52338SCy Schubert 		wpas_hs20_add_indication(extra_ie, -1, 0);
7995b9c547cSRui Paulo #endif /* CONFIG_HS20 */
8005b9c547cSRui Paulo 
801325151a3SRui Paulo #ifdef CONFIG_FST
802325151a3SRui Paulo 	if (wpa_s->fst_ies &&
803325151a3SRui Paulo 	    wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
804325151a3SRui Paulo 		wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
805325151a3SRui Paulo #endif /* CONFIG_FST */
806325151a3SRui Paulo 
807780fb4a2SCy Schubert #ifdef CONFIG_MBO
80885732ac8SCy Schubert 	/* Send MBO and OCE capabilities */
80985732ac8SCy Schubert 	if (wpabuf_resize(&extra_ie, 12) == 0)
810780fb4a2SCy Schubert 		wpas_mbo_scan_ie(wpa_s, extra_ie);
811780fb4a2SCy Schubert #endif /* CONFIG_MBO */
812780fb4a2SCy Schubert 
813780fb4a2SCy Schubert 	if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
814780fb4a2SCy Schubert 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
815780fb4a2SCy Schubert 
816780fb4a2SCy Schubert 		if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
817780fb4a2SCy Schubert 			wpabuf_put_buf(extra_ie, buf);
818780fb4a2SCy Schubert 	}
819780fb4a2SCy Schubert 
820f05cddf9SRui Paulo 	return extra_ie;
821f05cddf9SRui Paulo }
822f05cddf9SRui Paulo 
823f05cddf9SRui Paulo 
824f05cddf9SRui Paulo #ifdef CONFIG_P2P
825f05cddf9SRui Paulo 
826f05cddf9SRui Paulo /*
827f05cddf9SRui Paulo  * Check whether there are any enabled networks or credentials that could be
828f05cddf9SRui Paulo  * used for a non-P2P connection.
829f05cddf9SRui Paulo  */
non_p2p_network_enabled(struct wpa_supplicant * wpa_s)830f05cddf9SRui Paulo static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
831f05cddf9SRui Paulo {
832f05cddf9SRui Paulo 	struct wpa_ssid *ssid;
833f05cddf9SRui Paulo 
834f05cddf9SRui Paulo 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
835f05cddf9SRui Paulo 		if (wpas_network_disabled(wpa_s, ssid))
836f05cddf9SRui Paulo 			continue;
837f05cddf9SRui Paulo 		if (!ssid->p2p_group)
838f05cddf9SRui Paulo 			return 1;
839f05cddf9SRui Paulo 	}
840f05cddf9SRui Paulo 
841f05cddf9SRui Paulo 	if (wpa_s->conf->cred && wpa_s->conf->interworking &&
842f05cddf9SRui Paulo 	    wpa_s->conf->auto_interworking)
843f05cddf9SRui Paulo 		return 1;
844f05cddf9SRui Paulo 
845f05cddf9SRui Paulo 	return 0;
846f05cddf9SRui Paulo }
847f05cddf9SRui Paulo 
848f05cddf9SRui Paulo #endif /* CONFIG_P2P */
849f05cddf9SRui Paulo 
850f05cddf9SRui Paulo 
wpa_add_scan_freqs_list(struct wpa_supplicant * wpa_s,enum hostapd_hw_mode band,struct wpa_driver_scan_params * params,bool is_6ghz,bool only_6ghz_psc,bool exclude_radar)851c1d255d3SCy Schubert int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
8525b9c547cSRui Paulo 			    enum hostapd_hw_mode band,
853*a90b9d01SCy Schubert 			    struct wpa_driver_scan_params *params,
854*a90b9d01SCy Schubert 			    bool is_6ghz, bool only_6ghz_psc,
855*a90b9d01SCy Schubert 			    bool exclude_radar)
8565b9c547cSRui Paulo {
8575b9c547cSRui Paulo 	/* Include only supported channels for the specified band */
8585b9c547cSRui Paulo 	struct hostapd_hw_modes *mode;
859c1d255d3SCy Schubert 	int num_chans = 0;
860c1d255d3SCy Schubert 	int *freqs, i;
8615b9c547cSRui Paulo 
862c1d255d3SCy Schubert 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz);
863*a90b9d01SCy Schubert 	if (!mode || !mode->num_channels)
864c1d255d3SCy Schubert 		return -1;
865c1d255d3SCy Schubert 
866c1d255d3SCy Schubert 	if (params->freqs) {
867c1d255d3SCy Schubert 		while (params->freqs[num_chans])
868c1d255d3SCy Schubert 			num_chans++;
8695b9c547cSRui Paulo 	}
8705b9c547cSRui Paulo 
871c1d255d3SCy Schubert 	freqs = os_realloc(params->freqs,
872c1d255d3SCy Schubert 			   (num_chans + mode->num_channels + 1) * sizeof(int));
873c1d255d3SCy Schubert 	if (!freqs)
874c1d255d3SCy Schubert 		return -1;
875c1d255d3SCy Schubert 
876c1d255d3SCy Schubert 	params->freqs = freqs;
877c1d255d3SCy Schubert 	for (i = 0; i < mode->num_channels; i++) {
8785b9c547cSRui Paulo 		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
8795b9c547cSRui Paulo 			continue;
880*a90b9d01SCy Schubert 		if (exclude_radar &&
881*a90b9d01SCy Schubert 		    (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
882*a90b9d01SCy Schubert 			continue;
883*a90b9d01SCy Schubert 
884*a90b9d01SCy Schubert 		if (is_6ghz && only_6ghz_psc &&
885*a90b9d01SCy Schubert 		    !is_6ghz_psc_frequency(mode->channels[i].freq))
886*a90b9d01SCy Schubert 			continue;
887*a90b9d01SCy Schubert 
888c1d255d3SCy Schubert 		params->freqs[num_chans++] = mode->channels[i].freq;
8895b9c547cSRui Paulo 	}
890c1d255d3SCy Schubert 	params->freqs[num_chans] = 0;
891c1d255d3SCy Schubert 
892c1d255d3SCy Schubert 	return 0;
8935b9c547cSRui Paulo }
8945b9c547cSRui Paulo 
8955b9c547cSRui Paulo 
wpa_setband_scan_freqs(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)8965b9c547cSRui Paulo static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
8975b9c547cSRui Paulo 				   struct wpa_driver_scan_params *params)
8985b9c547cSRui Paulo {
8995b9c547cSRui Paulo 	if (wpa_s->hw.modes == NULL)
9005b9c547cSRui Paulo 		return; /* unknown what channels the driver supports */
9015b9c547cSRui Paulo 	if (params->freqs)
9025b9c547cSRui Paulo 		return; /* already using a limited channel set */
903c1d255d3SCy Schubert 
904c1d255d3SCy Schubert 	if (wpa_s->setband_mask & WPA_SETBAND_5G)
905c1d255d3SCy Schubert 		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
906*a90b9d01SCy Schubert 					false, false, false);
907c1d255d3SCy Schubert 	if (wpa_s->setband_mask & WPA_SETBAND_2G)
908c1d255d3SCy Schubert 		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
909*a90b9d01SCy Schubert 					false, false, false);
910c1d255d3SCy Schubert 	if (wpa_s->setband_mask & WPA_SETBAND_6G)
911c1d255d3SCy Schubert 		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
912*a90b9d01SCy Schubert 					true, false, false);
9135b9c547cSRui Paulo }
9145b9c547cSRui Paulo 
9155b9c547cSRui Paulo 
wpa_add_scan_ssid(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,size_t max_ssids,const u8 * ssid,size_t ssid_len)91685732ac8SCy Schubert static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s,
91785732ac8SCy Schubert 			      struct wpa_driver_scan_params *params,
91885732ac8SCy Schubert 			      size_t max_ssids, const u8 *ssid, size_t ssid_len)
91985732ac8SCy Schubert {
92085732ac8SCy Schubert 	unsigned int j;
92185732ac8SCy Schubert 
92285732ac8SCy Schubert 	for (j = 0; j < params->num_ssids; j++) {
92385732ac8SCy Schubert 		if (params->ssids[j].ssid_len == ssid_len &&
92485732ac8SCy Schubert 		    params->ssids[j].ssid &&
92585732ac8SCy Schubert 		    os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0)
92685732ac8SCy Schubert 			return; /* already in the list */
92785732ac8SCy Schubert 	}
92885732ac8SCy Schubert 
92985732ac8SCy Schubert 	if (params->num_ssids + 1 > max_ssids) {
93085732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request");
93185732ac8SCy Schubert 		return;
93285732ac8SCy Schubert 	}
93385732ac8SCy Schubert 
93485732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
93585732ac8SCy Schubert 		   wpa_ssid_txt(ssid, ssid_len));
93685732ac8SCy Schubert 
93785732ac8SCy Schubert 	params->ssids[params->num_ssids].ssid = ssid;
93885732ac8SCy Schubert 	params->ssids[params->num_ssids].ssid_len = ssid_len;
93985732ac8SCy Schubert 	params->num_ssids++;
94085732ac8SCy Schubert }
94185732ac8SCy Schubert 
94285732ac8SCy Schubert 
wpa_add_owe_scan_ssid(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,struct wpa_ssid * ssid,size_t max_ssids)94385732ac8SCy Schubert static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s,
94485732ac8SCy Schubert 				  struct wpa_driver_scan_params *params,
94585732ac8SCy Schubert 				  struct wpa_ssid *ssid, size_t max_ssids)
94685732ac8SCy Schubert {
94785732ac8SCy Schubert #ifdef CONFIG_OWE
94885732ac8SCy Schubert 	struct wpa_bss *bss;
94985732ac8SCy Schubert 
95085732ac8SCy Schubert 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE))
95185732ac8SCy Schubert 		return;
95285732ac8SCy Schubert 
95385732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s",
95485732ac8SCy Schubert 		   wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
95585732ac8SCy Schubert 
95685732ac8SCy Schubert 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
95785732ac8SCy Schubert 		const u8 *owe, *pos, *end;
95885732ac8SCy Schubert 		const u8 *owe_ssid;
95985732ac8SCy Schubert 		size_t owe_ssid_len;
96085732ac8SCy Schubert 
96185732ac8SCy Schubert 		if (bss->ssid_len != ssid->ssid_len ||
96285732ac8SCy Schubert 		    os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0)
96385732ac8SCy Schubert 			continue;
96485732ac8SCy Schubert 
96585732ac8SCy Schubert 		owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
96685732ac8SCy Schubert 		if (!owe || owe[1] < 4)
96785732ac8SCy Schubert 			continue;
96885732ac8SCy Schubert 
96985732ac8SCy Schubert 		pos = owe + 6;
97085732ac8SCy Schubert 		end = owe + 2 + owe[1];
97185732ac8SCy Schubert 
97285732ac8SCy Schubert 		/* Must include BSSID and ssid_len */
97385732ac8SCy Schubert 		if (end - pos < ETH_ALEN + 1)
97485732ac8SCy Schubert 			return;
97585732ac8SCy Schubert 
97685732ac8SCy Schubert 		/* Skip BSSID */
97785732ac8SCy Schubert 		pos += ETH_ALEN;
97885732ac8SCy Schubert 		owe_ssid_len = *pos++;
97985732ac8SCy Schubert 		owe_ssid = pos;
98085732ac8SCy Schubert 
98185732ac8SCy Schubert 		if ((size_t) (end - pos) < owe_ssid_len ||
98285732ac8SCy Schubert 		    owe_ssid_len > SSID_MAX_LEN)
98385732ac8SCy Schubert 			return;
98485732ac8SCy Schubert 
98585732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
98685732ac8SCy Schubert 			   "OWE: scan_ssids: transition mode OWE ssid=%s",
98785732ac8SCy Schubert 			   wpa_ssid_txt(owe_ssid, owe_ssid_len));
98885732ac8SCy Schubert 
98985732ac8SCy Schubert 		wpa_add_scan_ssid(wpa_s, params, max_ssids,
99085732ac8SCy Schubert 				  owe_ssid, owe_ssid_len);
99185732ac8SCy Schubert 		return;
99285732ac8SCy Schubert 	}
99385732ac8SCy Schubert #endif /* CONFIG_OWE */
99485732ac8SCy Schubert }
99585732ac8SCy Schubert 
99685732ac8SCy Schubert 
wpa_set_scan_ssids(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,size_t max_ssids)9975b9c547cSRui Paulo static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
9985b9c547cSRui Paulo 			       struct wpa_driver_scan_params *params,
9995b9c547cSRui Paulo 			       size_t max_ssids)
10005b9c547cSRui Paulo {
10015b9c547cSRui Paulo 	unsigned int i;
10025b9c547cSRui Paulo 	struct wpa_ssid *ssid;
10035b9c547cSRui Paulo 
1004780fb4a2SCy Schubert 	/*
1005780fb4a2SCy Schubert 	 * For devices with max_ssids greater than 1, leave the last slot empty
1006780fb4a2SCy Schubert 	 * for adding the wildcard scan entry.
1007780fb4a2SCy Schubert 	 */
1008780fb4a2SCy Schubert 	max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
1009780fb4a2SCy Schubert 
10105b9c547cSRui Paulo 	for (i = 0; i < wpa_s->scan_id_count; i++) {
10115b9c547cSRui Paulo 		ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
101285732ac8SCy Schubert 		if (!ssid)
10135b9c547cSRui Paulo 			continue;
101485732ac8SCy Schubert 		if (ssid->scan_ssid)
101585732ac8SCy Schubert 			wpa_add_scan_ssid(wpa_s, params, max_ssids,
101685732ac8SCy Schubert 					  ssid->ssid, ssid->ssid_len);
101785732ac8SCy Schubert 		/*
101885732ac8SCy Schubert 		 * Also add the SSID of the OWE BSS, to allow discovery of
101985732ac8SCy Schubert 		 * transition mode APs more quickly.
102085732ac8SCy Schubert 		 */
102185732ac8SCy Schubert 		wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids);
10225b9c547cSRui Paulo 	}
10235b9c547cSRui Paulo 
10245b9c547cSRui Paulo 	wpa_s->scan_id_count = 0;
10255b9c547cSRui Paulo }
10265b9c547cSRui Paulo 
10275b9c547cSRui Paulo 
wpa_set_ssids_from_scan_req(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,size_t max_ssids)1028325151a3SRui Paulo static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
1029325151a3SRui Paulo 				       struct wpa_driver_scan_params *params,
1030325151a3SRui Paulo 				       size_t max_ssids)
1031325151a3SRui Paulo {
1032325151a3SRui Paulo 	unsigned int i;
1033325151a3SRui Paulo 
1034325151a3SRui Paulo 	if (wpa_s->ssids_from_scan_req == NULL ||
1035325151a3SRui Paulo 	    wpa_s->num_ssids_from_scan_req == 0)
1036325151a3SRui Paulo 		return 0;
1037325151a3SRui Paulo 
1038325151a3SRui Paulo 	if (wpa_s->num_ssids_from_scan_req > max_ssids) {
1039325151a3SRui Paulo 		wpa_s->num_ssids_from_scan_req = max_ssids;
1040325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
1041325151a3SRui Paulo 			   (unsigned int) max_ssids);
1042325151a3SRui Paulo 	}
1043325151a3SRui Paulo 
1044325151a3SRui Paulo 	for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
1045325151a3SRui Paulo 		params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
1046325151a3SRui Paulo 		params->ssids[i].ssid_len =
1047325151a3SRui Paulo 			wpa_s->ssids_from_scan_req[i].ssid_len;
1048325151a3SRui Paulo 		wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
1049325151a3SRui Paulo 				  params->ssids[i].ssid,
1050325151a3SRui Paulo 				  params->ssids[i].ssid_len);
1051325151a3SRui Paulo 	}
1052325151a3SRui Paulo 
1053325151a3SRui Paulo 	params->num_ssids = wpa_s->num_ssids_from_scan_req;
1054325151a3SRui Paulo 	wpa_s->num_ssids_from_scan_req = 0;
1055325151a3SRui Paulo 	return 1;
1056325151a3SRui Paulo }
1057325151a3SRui Paulo 
1058325151a3SRui Paulo 
wpa_supplicant_scan(void * eloop_ctx,void * timeout_ctx)1059f05cddf9SRui Paulo static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
1060f05cddf9SRui Paulo {
1061f05cddf9SRui Paulo 	struct wpa_supplicant *wpa_s = eloop_ctx;
1062f05cddf9SRui Paulo 	struct wpa_ssid *ssid;
10635b9c547cSRui Paulo 	int ret, p2p_in_prog;
1064f05cddf9SRui Paulo 	struct wpabuf *extra_ie = NULL;
1065e28a4053SRui Paulo 	struct wpa_driver_scan_params params;
1066f05cddf9SRui Paulo 	struct wpa_driver_scan_params *scan_params;
1067e28a4053SRui Paulo 	size_t max_ssids;
10685b9c547cSRui Paulo 	int connect_without_scan = 0;
10695b9c547cSRui Paulo 
107085732ac8SCy Schubert 	wpa_s->ignore_post_flush_scan_res = 0;
107139beb93cSSam Leffler 
1072f05cddf9SRui Paulo 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
1073f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
1074f05cddf9SRui Paulo 		return;
1075f05cddf9SRui Paulo 	}
1076f05cddf9SRui Paulo 
1077f05cddf9SRui Paulo 	if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
1078f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
10793157ba21SRui Paulo 		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
108039beb93cSSam Leffler 		return;
10813157ba21SRui Paulo 	}
108239beb93cSSam Leffler 
10835b9c547cSRui Paulo 	if (wpa_s->scanning) {
10845b9c547cSRui Paulo 		/*
10855b9c547cSRui Paulo 		 * If we are already in scanning state, we shall reschedule the
10865b9c547cSRui Paulo 		 * the incoming scan request.
10875b9c547cSRui Paulo 		 */
10885b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
10895b9c547cSRui Paulo 		wpa_supplicant_req_scan(wpa_s, 1, 0);
10905b9c547cSRui Paulo 		return;
10915b9c547cSRui Paulo 	}
10925b9c547cSRui Paulo 
1093f05cddf9SRui Paulo 	if (!wpa_supplicant_enabled_networks(wpa_s) &&
1094f05cddf9SRui Paulo 	    wpa_s->scan_req == NORMAL_SCAN_REQ) {
1095f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
109639beb93cSSam Leffler 		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
109739beb93cSSam Leffler 		return;
109839beb93cSSam Leffler 	}
109939beb93cSSam Leffler 
110039beb93cSSam Leffler 	if (wpa_s->conf->ap_scan != 0 &&
1101e28a4053SRui Paulo 	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
1102f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
110339beb93cSSam Leffler 			"overriding ap_scan configuration");
110439beb93cSSam Leffler 		wpa_s->conf->ap_scan = 0;
1105e28a4053SRui Paulo 		wpas_notify_ap_scan_changed(wpa_s);
110639beb93cSSam Leffler 	}
110739beb93cSSam Leffler 
110839beb93cSSam Leffler 	if (wpa_s->conf->ap_scan == 0) {
110939beb93cSSam Leffler 		wpa_supplicant_gen_assoc_event(wpa_s);
111039beb93cSSam Leffler 		return;
111139beb93cSSam Leffler 	}
111239beb93cSSam Leffler 
11135b9c547cSRui Paulo 	ssid = NULL;
11145b9c547cSRui Paulo 	if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
11155b9c547cSRui Paulo 	    wpa_s->connect_without_scan) {
11165b9c547cSRui Paulo 		connect_without_scan = 1;
11175b9c547cSRui Paulo 		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
11185b9c547cSRui Paulo 			if (ssid == wpa_s->connect_without_scan)
11195b9c547cSRui Paulo 				break;
11205b9c547cSRui Paulo 		}
11215b9c547cSRui Paulo 	}
11225b9c547cSRui Paulo 
11235b9c547cSRui Paulo 	p2p_in_prog = wpas_p2p_in_progress(wpa_s);
11245b9c547cSRui Paulo 	if (p2p_in_prog && p2p_in_prog != 2 &&
11255b9c547cSRui Paulo 	    (!ssid ||
11265b9c547cSRui Paulo 	     (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
11275b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
1128f05cddf9SRui Paulo 		wpa_supplicant_req_scan(wpa_s, 5, 0);
1129f05cddf9SRui Paulo 		return;
1130f05cddf9SRui Paulo 	}
1131f05cddf9SRui Paulo 
113285732ac8SCy Schubert 	/*
113385732ac8SCy Schubert 	 * Don't cancel the scan based on ongoing PNO; defer it. Some scans are
113485732ac8SCy Schubert 	 * used for changing modes inside wpa_supplicant (roaming,
113585732ac8SCy Schubert 	 * auto-reconnect, etc). Discarding the scan might hurt these processes.
113685732ac8SCy Schubert 	 * The normal use case for PNO is to suspend the host immediately after
113785732ac8SCy Schubert 	 * starting PNO, so the periodic 100 ms attempts to run the scan do not
113885732ac8SCy Schubert 	 * normally happen in practice multiple times, i.e., this is simply
113985732ac8SCy Schubert 	 * restarting scanning once the host is woken up and PNO stopped.
114085732ac8SCy Schubert 	 */
114185732ac8SCy Schubert 	if (wpa_s->pno || wpa_s->pno_sched_pending) {
114285732ac8SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
114385732ac8SCy Schubert 		wpa_supplicant_req_scan(wpa_s, 0, 100000);
114485732ac8SCy Schubert 		return;
114585732ac8SCy Schubert 	}
114685732ac8SCy Schubert 
1147f05cddf9SRui Paulo 	if (wpa_s->conf->ap_scan == 2)
1148e28a4053SRui Paulo 		max_ssids = 1;
1149e28a4053SRui Paulo 	else {
1150e28a4053SRui Paulo 		max_ssids = wpa_s->max_scan_ssids;
1151e28a4053SRui Paulo 		if (max_ssids > WPAS_MAX_SCAN_SSIDS)
1152e28a4053SRui Paulo 			max_ssids = WPAS_MAX_SCAN_SSIDS;
1153e28a4053SRui Paulo 	}
1154e28a4053SRui Paulo 
11555b9c547cSRui Paulo 	wpa_s->last_scan_req = wpa_s->scan_req;
1156f05cddf9SRui Paulo 	wpa_s->scan_req = NORMAL_SCAN_REQ;
1157e28a4053SRui Paulo 
11585b9c547cSRui Paulo 	if (connect_without_scan) {
11595b9c547cSRui Paulo 		wpa_s->connect_without_scan = NULL;
11605b9c547cSRui Paulo 		if (ssid) {
11615b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "Start a pre-selected network "
11625b9c547cSRui Paulo 				   "without scan step");
11635b9c547cSRui Paulo 			wpa_supplicant_associate(wpa_s, NULL, ssid);
11645b9c547cSRui Paulo 			return;
11655b9c547cSRui Paulo 		}
11665b9c547cSRui Paulo 	}
11675b9c547cSRui Paulo 
1168e28a4053SRui Paulo 	os_memset(&params, 0, sizeof(params));
1169e28a4053SRui Paulo 
11705b9c547cSRui Paulo 	wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
117139beb93cSSam Leffler 	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
117239beb93cSSam Leffler 	    wpa_s->wpa_state == WPA_INACTIVE)
117339beb93cSSam Leffler 		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
117439beb93cSSam Leffler 
1175f05cddf9SRui Paulo 	/*
1176f05cddf9SRui Paulo 	 * If autoscan has set its own scanning parameters
1177f05cddf9SRui Paulo 	 */
1178f05cddf9SRui Paulo 	if (wpa_s->autoscan_params != NULL) {
1179f05cddf9SRui Paulo 		scan_params = wpa_s->autoscan_params;
1180f05cddf9SRui Paulo 		goto scan;
1181f05cddf9SRui Paulo 	}
1182f05cddf9SRui Paulo 
1183325151a3SRui Paulo 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
1184325151a3SRui Paulo 	    wpa_set_ssids_from_scan_req(wpa_s, &params, max_ssids)) {
1185325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
1186325151a3SRui Paulo 		goto ssid_list_set;
1187325151a3SRui Paulo 	}
1188325151a3SRui Paulo 
1189f05cddf9SRui Paulo #ifdef CONFIG_P2P
1190f05cddf9SRui Paulo 	if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
11915b9c547cSRui Paulo 	    wpa_s->go_params && !wpa_s->conf->passive_scan) {
11925b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
11935b9c547cSRui Paulo 			   wpa_s->p2p_in_provisioning,
11945b9c547cSRui Paulo 			   wpa_s->show_group_started);
1195f05cddf9SRui Paulo 		params.ssids[0].ssid = wpa_s->go_params->ssid;
1196f05cddf9SRui Paulo 		params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
1197f05cddf9SRui Paulo 		params.num_ssids = 1;
1198*a90b9d01SCy Schubert 		params.bssid = wpa_s->go_params->peer_interface_addr;
1199*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "P2P: Use specific BSSID " MACSTR
1200*a90b9d01SCy Schubert 			   " (peer interface address) for scan",
1201*a90b9d01SCy Schubert 			   MAC2STR(params.bssid));
1202f05cddf9SRui Paulo 		goto ssid_list_set;
1203f05cddf9SRui Paulo 	}
12045b9c547cSRui Paulo 
12055b9c547cSRui Paulo 	if (wpa_s->p2p_in_invitation) {
12065b9c547cSRui Paulo 		if (wpa_s->current_ssid) {
12075b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
12085b9c547cSRui Paulo 			params.ssids[0].ssid = wpa_s->current_ssid->ssid;
12095b9c547cSRui Paulo 			params.ssids[0].ssid_len =
12105b9c547cSRui Paulo 				wpa_s->current_ssid->ssid_len;
12115b9c547cSRui Paulo 			params.num_ssids = 1;
1212*a90b9d01SCy Schubert 			if (wpa_s->current_ssid->bssid_set) {
1213*a90b9d01SCy Schubert 				params.bssid = wpa_s->current_ssid->bssid;
1214*a90b9d01SCy Schubert 				wpa_printf(MSG_DEBUG, "P2P: Use specific BSSID "
1215*a90b9d01SCy Schubert 					   MACSTR " for scan",
1216*a90b9d01SCy Schubert 					   MAC2STR(params.bssid));
1217*a90b9d01SCy Schubert 			}
12185b9c547cSRui Paulo 		} else {
12195b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
12205b9c547cSRui Paulo 		}
12215b9c547cSRui Paulo 		goto ssid_list_set;
12225b9c547cSRui Paulo 	}
1223f05cddf9SRui Paulo #endif /* CONFIG_P2P */
1224f05cddf9SRui Paulo 
1225e28a4053SRui Paulo 	/* Find the starting point from which to continue scanning */
122639beb93cSSam Leffler 	ssid = wpa_s->conf->ssid;
1227e28a4053SRui Paulo 	if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
122839beb93cSSam Leffler 		while (ssid) {
122939beb93cSSam Leffler 			if (ssid == wpa_s->prev_scan_ssid) {
123039beb93cSSam Leffler 				ssid = ssid->next;
123139beb93cSSam Leffler 				break;
123239beb93cSSam Leffler 			}
123339beb93cSSam Leffler 			ssid = ssid->next;
123439beb93cSSam Leffler 		}
123539beb93cSSam Leffler 	}
123639beb93cSSam Leffler 
12375b9c547cSRui Paulo 	if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
1238325151a3SRui Paulo #ifdef CONFIG_AP
1239325151a3SRui Paulo 	    !wpa_s->ap_iface &&
1240325151a3SRui Paulo #endif /* CONFIG_AP */
12415b9c547cSRui Paulo 	    wpa_s->conf->ap_scan == 2) {
1242f05cddf9SRui Paulo 		wpa_s->connect_without_scan = NULL;
1243f05cddf9SRui Paulo 		wpa_s->prev_scan_wildcard = 0;
1244e28a4053SRui Paulo 		wpa_supplicant_assoc_try(wpa_s, ssid);
1245e28a4053SRui Paulo 		return;
1246e28a4053SRui Paulo 	} else if (wpa_s->conf->ap_scan == 2) {
124739beb93cSSam Leffler 		/*
1248e28a4053SRui Paulo 		 * User-initiated scan request in ap_scan == 2; scan with
1249e28a4053SRui Paulo 		 * wildcard SSID.
125039beb93cSSam Leffler 		 */
1251e28a4053SRui Paulo 		ssid = NULL;
12525b9c547cSRui Paulo 	} else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
12535b9c547cSRui Paulo 		/*
12545b9c547cSRui Paulo 		 * Perform single-channel single-SSID scan for
12555b9c547cSRui Paulo 		 * reassociate-to-same-BSS operation.
12565b9c547cSRui Paulo 		 */
12575b9c547cSRui Paulo 		/* Setup SSID */
12585b9c547cSRui Paulo 		ssid = wpa_s->current_ssid;
12595b9c547cSRui Paulo 		wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
12605b9c547cSRui Paulo 				  ssid->ssid, ssid->ssid_len);
12615b9c547cSRui Paulo 		params.ssids[0].ssid = ssid->ssid;
12625b9c547cSRui Paulo 		params.ssids[0].ssid_len = ssid->ssid_len;
12635b9c547cSRui Paulo 		params.num_ssids = 1;
12645b9c547cSRui Paulo 
12655b9c547cSRui Paulo 		/*
12665b9c547cSRui Paulo 		 * Allocate memory for frequency array, allocate one extra
12675b9c547cSRui Paulo 		 * slot for the zero-terminator.
12685b9c547cSRui Paulo 		 */
12695b9c547cSRui Paulo 		params.freqs = os_malloc(sizeof(int) * 2);
1270780fb4a2SCy Schubert 		if (params.freqs) {
12715b9c547cSRui Paulo 			params.freqs[0] = wpa_s->assoc_freq;
12725b9c547cSRui Paulo 			params.freqs[1] = 0;
1273780fb4a2SCy Schubert 		}
12745b9c547cSRui Paulo 
12755b9c547cSRui Paulo 		/*
12765b9c547cSRui Paulo 		 * Reset the reattach flag so that we fall back to full scan if
12775b9c547cSRui Paulo 		 * this scan fails.
12785b9c547cSRui Paulo 		 */
12795b9c547cSRui Paulo 		wpa_s->reattach = 0;
128039beb93cSSam Leffler 	} else {
1281e28a4053SRui Paulo 		struct wpa_ssid *start = ssid, *tssid;
1282e28a4053SRui Paulo 		int freqs_set = 0;
1283e28a4053SRui Paulo 		if (ssid == NULL && max_ssids > 1)
1284e28a4053SRui Paulo 			ssid = wpa_s->conf->ssid;
1285e28a4053SRui Paulo 		while (ssid) {
1286f05cddf9SRui Paulo 			if (!wpas_network_disabled(wpa_s, ssid) &&
1287f05cddf9SRui Paulo 			    ssid->scan_ssid) {
128839beb93cSSam Leffler 				wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
128939beb93cSSam Leffler 						  ssid->ssid, ssid->ssid_len);
1290e28a4053SRui Paulo 				params.ssids[params.num_ssids].ssid =
1291e28a4053SRui Paulo 					ssid->ssid;
1292e28a4053SRui Paulo 				params.ssids[params.num_ssids].ssid_len =
1293e28a4053SRui Paulo 					ssid->ssid_len;
1294e28a4053SRui Paulo 				params.num_ssids++;
1295e28a4053SRui Paulo 				if (params.num_ssids + 1 >= max_ssids)
1296e28a4053SRui Paulo 					break;
1297e28a4053SRui Paulo 			}
129885732ac8SCy Schubert 
129985732ac8SCy Schubert 			if (!wpas_network_disabled(wpa_s, ssid)) {
130085732ac8SCy Schubert 				/*
130185732ac8SCy Schubert 				 * Also add the SSID of the OWE BSS, to allow
130285732ac8SCy Schubert 				 * discovery of transition mode APs more
130385732ac8SCy Schubert 				 * quickly.
130485732ac8SCy Schubert 				 */
130585732ac8SCy Schubert 				wpa_add_owe_scan_ssid(wpa_s, &params, ssid,
130685732ac8SCy Schubert 						      max_ssids);
130785732ac8SCy Schubert 			}
130885732ac8SCy Schubert 
1309e28a4053SRui Paulo 			ssid = ssid->next;
1310e28a4053SRui Paulo 			if (ssid == start)
1311e28a4053SRui Paulo 				break;
1312e28a4053SRui Paulo 			if (ssid == NULL && max_ssids > 1 &&
1313e28a4053SRui Paulo 			    start != wpa_s->conf->ssid)
1314e28a4053SRui Paulo 				ssid = wpa_s->conf->ssid;
1315e28a4053SRui Paulo 		}
1316e28a4053SRui Paulo 
13175b9c547cSRui Paulo 		if (wpa_s->scan_id_count &&
13185b9c547cSRui Paulo 		    wpa_s->last_scan_req == MANUAL_SCAN_REQ)
13195b9c547cSRui Paulo 			wpa_set_scan_ssids(wpa_s, &params, max_ssids);
13205b9c547cSRui Paulo 
13215b9c547cSRui Paulo 		for (tssid = wpa_s->conf->ssid;
13225b9c547cSRui Paulo 		     wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
13235b9c547cSRui Paulo 		     tssid = tssid->next) {
1324f05cddf9SRui Paulo 			if (wpas_network_disabled(wpa_s, tssid))
1325e28a4053SRui Paulo 				continue;
1326c1d255d3SCy Schubert 			if (((params.freqs || !freqs_set) &&
1327c1d255d3SCy Schubert 			     tssid->scan_freq) &&
1328c1d255d3SCy Schubert 			    int_array_len(params.freqs) < 100) {
1329e28a4053SRui Paulo 				int_array_concat(&params.freqs,
1330e28a4053SRui Paulo 						 tssid->scan_freq);
1331e28a4053SRui Paulo 			} else {
1332e28a4053SRui Paulo 				os_free(params.freqs);
1333e28a4053SRui Paulo 				params.freqs = NULL;
1334e28a4053SRui Paulo 			}
1335e28a4053SRui Paulo 			freqs_set = 1;
1336e28a4053SRui Paulo 		}
1337e28a4053SRui Paulo 		int_array_sort_unique(params.freqs);
1338e28a4053SRui Paulo 	}
1339e28a4053SRui Paulo 
1340f05cddf9SRui Paulo 	if (ssid && max_ssids == 1) {
1341f05cddf9SRui Paulo 		/*
1342f05cddf9SRui Paulo 		 * If the driver is limited to 1 SSID at a time interleave
1343f05cddf9SRui Paulo 		 * wildcard SSID scans with specific SSID scans to avoid
1344f05cddf9SRui Paulo 		 * waiting a long time for a wildcard scan.
1345f05cddf9SRui Paulo 		 */
1346f05cddf9SRui Paulo 		if (!wpa_s->prev_scan_wildcard) {
1347f05cddf9SRui Paulo 			params.ssids[0].ssid = NULL;
1348f05cddf9SRui Paulo 			params.ssids[0].ssid_len = 0;
1349f05cddf9SRui Paulo 			wpa_s->prev_scan_wildcard = 1;
1350f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
1351f05cddf9SRui Paulo 				"wildcard SSID (Interleave with specific)");
1352f05cddf9SRui Paulo 		} else {
135339beb93cSSam Leffler 			wpa_s->prev_scan_ssid = ssid;
1354f05cddf9SRui Paulo 			wpa_s->prev_scan_wildcard = 0;
1355f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG,
1356f05cddf9SRui Paulo 				"Starting AP scan for specific SSID: %s",
1357f05cddf9SRui Paulo 				wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
1358e28a4053SRui Paulo 		}
1359f05cddf9SRui Paulo 	} else if (ssid) {
1360f05cddf9SRui Paulo 		/* max_ssids > 1 */
1361f05cddf9SRui Paulo 
1362f05cddf9SRui Paulo 		wpa_s->prev_scan_ssid = ssid;
1363f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
1364f05cddf9SRui Paulo 			"the scan request");
1365f05cddf9SRui Paulo 		params.num_ssids++;
13665b9c547cSRui Paulo 	} else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
13675b9c547cSRui Paulo 		   wpa_s->manual_scan_passive && params.num_ssids == 0) {
13685b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
13695b9c547cSRui Paulo 	} else if (wpa_s->conf->passive_scan) {
13705b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG,
13715b9c547cSRui Paulo 			"Use passive scan based on configuration");
1372e28a4053SRui Paulo 	} else {
1373e28a4053SRui Paulo 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
1374e28a4053SRui Paulo 		params.num_ssids++;
1375f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
1376f05cddf9SRui Paulo 			"SSID");
137739beb93cSSam Leffler 	}
137839beb93cSSam Leffler 
1379325151a3SRui Paulo ssid_list_set:
1380f05cddf9SRui Paulo 	wpa_supplicant_optimize_freqs(wpa_s, &params);
1381f05cddf9SRui Paulo 	extra_ie = wpa_supplicant_extra_ies(wpa_s);
1382e28a4053SRui Paulo 
13835b9c547cSRui Paulo 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
13845b9c547cSRui Paulo 	    wpa_s->manual_scan_only_new) {
13855b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
13865b9c547cSRui Paulo 			   "Request driver to clear scan cache due to manual only_new=1 scan");
13875b9c547cSRui Paulo 		params.only_new_results = 1;
13885b9c547cSRui Paulo 	}
13895b9c547cSRui Paulo 
13905b9c547cSRui Paulo 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
13915b9c547cSRui Paulo 	    wpa_s->manual_scan_freqs) {
13925b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
13935b9c547cSRui Paulo 		params.freqs = wpa_s->manual_scan_freqs;
13945b9c547cSRui Paulo 		wpa_s->manual_scan_freqs = NULL;
13955b9c547cSRui Paulo 	}
1396f05cddf9SRui Paulo 
139785732ac8SCy Schubert 	if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
139885732ac8SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG,
139985732ac8SCy Schubert 			"Limit select_network scan to specified channels");
140085732ac8SCy Schubert 		params.freqs = wpa_s->select_network_scan_freqs;
140185732ac8SCy Schubert 		wpa_s->select_network_scan_freqs = NULL;
140285732ac8SCy Schubert 	}
140385732ac8SCy Schubert 
1404f05cddf9SRui Paulo 	if (params.freqs == NULL && wpa_s->next_scan_freqs) {
1405f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
1406f05cddf9SRui Paulo 			"generated frequency list");
1407f05cddf9SRui Paulo 		params.freqs = wpa_s->next_scan_freqs;
1408f05cddf9SRui Paulo 	} else
1409f05cddf9SRui Paulo 		os_free(wpa_s->next_scan_freqs);
1410f05cddf9SRui Paulo 	wpa_s->next_scan_freqs = NULL;
14115b9c547cSRui Paulo 	wpa_setband_scan_freqs(wpa_s, &params);
14125b9c547cSRui Paulo 
14135b9c547cSRui Paulo 	/* See if user specified frequencies. If so, scan only those. */
1414c1d255d3SCy Schubert 	if (wpa_s->last_scan_req == INITIAL_SCAN_REQ &&
1415c1d255d3SCy Schubert 	    wpa_s->conf->initial_freq_list && !params.freqs) {
1416c1d255d3SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG,
1417c1d255d3SCy Schubert 			"Optimize scan based on conf->initial_freq_list");
1418c1d255d3SCy Schubert 		int_array_concat(&params.freqs, wpa_s->conf->initial_freq_list);
1419c1d255d3SCy Schubert 	} else if (wpa_s->conf->freq_list && !params.freqs) {
14205b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG,
14215b9c547cSRui Paulo 			"Optimize scan based on conf->freq_list");
14225b9c547cSRui Paulo 		int_array_concat(&params.freqs, wpa_s->conf->freq_list);
14235b9c547cSRui Paulo 	}
14245b9c547cSRui Paulo 
14255b9c547cSRui Paulo 	/* Use current associated channel? */
14265b9c547cSRui Paulo 	if (wpa_s->conf->scan_cur_freq && !params.freqs) {
14275b9c547cSRui Paulo 		unsigned int num = wpa_s->num_multichan_concurrent;
14285b9c547cSRui Paulo 
14295b9c547cSRui Paulo 		params.freqs = os_calloc(num + 1, sizeof(int));
14305b9c547cSRui Paulo 		if (params.freqs) {
1431*a90b9d01SCy Schubert 			num = get_shared_radio_freqs(wpa_s, params.freqs, num,
1432*a90b9d01SCy Schubert 						     false);
14335b9c547cSRui Paulo 			if (num > 0) {
14345b9c547cSRui Paulo 				wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
14355b9c547cSRui Paulo 					"current operating channels since "
14365b9c547cSRui Paulo 					"scan_cur_freq is enabled");
14375b9c547cSRui Paulo 			} else {
14385b9c547cSRui Paulo 				os_free(params.freqs);
14395b9c547cSRui Paulo 				params.freqs = NULL;
14405b9c547cSRui Paulo 			}
14415b9c547cSRui Paulo 		}
14425b9c547cSRui Paulo 	}
144339beb93cSSam Leffler 
144485732ac8SCy Schubert #ifdef CONFIG_MBO
144585732ac8SCy Schubert 	if (wpa_s->enable_oce & OCE_STA)
144685732ac8SCy Schubert 		params.oce_scan = 1;
144785732ac8SCy Schubert #endif /* CONFIG_MBO */
144885732ac8SCy Schubert 
1449e28a4053SRui Paulo 	params.filter_ssids = wpa_supplicant_build_filter_ssids(
1450e28a4053SRui Paulo 		wpa_s->conf, &params.num_filter_ssids);
1451f05cddf9SRui Paulo 	if (extra_ie) {
1452f05cddf9SRui Paulo 		params.extra_ies = wpabuf_head(extra_ie);
1453f05cddf9SRui Paulo 		params.extra_ies_len = wpabuf_len(extra_ie);
1454f05cddf9SRui Paulo 	}
14553157ba21SRui Paulo 
1456f05cddf9SRui Paulo #ifdef CONFIG_P2P
14575b9c547cSRui Paulo 	if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
1458f05cddf9SRui Paulo 	    (wpa_s->show_group_started && wpa_s->go_params)) {
1459f05cddf9SRui Paulo 		/*
1460f05cddf9SRui Paulo 		 * The interface may not yet be in P2P mode, so we have to
1461f05cddf9SRui Paulo 		 * explicitly request P2P probe to disable CCK rates.
1462f05cddf9SRui Paulo 		 */
1463f05cddf9SRui Paulo 		params.p2p_probe = 1;
1464f05cddf9SRui Paulo 	}
1465f05cddf9SRui Paulo #endif /* CONFIG_P2P */
146639beb93cSSam Leffler 
146785732ac8SCy Schubert 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
1468c1d255d3SCy Schubert 	    wpa_s->wpa_state <= WPA_SCANNING)
1469c1d255d3SCy Schubert 		wpa_setup_mac_addr_rand_params(&params, wpa_s->mac_addr_scan);
14705b9c547cSRui Paulo 
1471780fb4a2SCy Schubert 	if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
1472780fb4a2SCy Schubert 		struct wpa_bss *bss;
1473780fb4a2SCy Schubert 
1474780fb4a2SCy Schubert 		params.bssid = wpa_s->next_scan_bssid;
1475780fb4a2SCy Schubert 		bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid);
1476c1d255d3SCy Schubert 		if (!wpa_s->next_scan_bssid_wildcard_ssid &&
1477c1d255d3SCy Schubert 		    bss && bss->ssid_len && params.num_ssids == 1 &&
1478780fb4a2SCy Schubert 		    params.ssids[0].ssid_len == 0) {
1479780fb4a2SCy Schubert 			params.ssids[0].ssid = bss->ssid;
1480780fb4a2SCy Schubert 			params.ssids[0].ssid_len = bss->ssid_len;
1481780fb4a2SCy Schubert 			wpa_dbg(wpa_s, MSG_DEBUG,
1482780fb4a2SCy Schubert 				"Scan a previously specified BSSID " MACSTR
1483780fb4a2SCy Schubert 				" and SSID %s",
1484780fb4a2SCy Schubert 				MAC2STR(params.bssid),
1485780fb4a2SCy Schubert 				wpa_ssid_txt(bss->ssid, bss->ssid_len));
1486780fb4a2SCy Schubert 		} else {
1487780fb4a2SCy Schubert 			wpa_dbg(wpa_s, MSG_DEBUG,
1488780fb4a2SCy Schubert 				"Scan a previously specified BSSID " MACSTR,
1489780fb4a2SCy Schubert 				MAC2STR(params.bssid));
1490780fb4a2SCy Schubert 		}
1491*a90b9d01SCy Schubert 	} else if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) {
1492*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "Scanning for ML probe request");
1493*a90b9d01SCy Schubert 		params.bssid = wpa_s->ml_probe_bssid;
1494*a90b9d01SCy Schubert 		params.min_probe_req_content = true;
1495*a90b9d01SCy Schubert 	}
1496*a90b9d01SCy Schubert 
1497*a90b9d01SCy Schubert 
1498*a90b9d01SCy Schubert 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
1499*a90b9d01SCy Schubert 	    wpa_s->manual_non_coloc_6ghz) {
1500*a90b9d01SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG, "Collocated 6 GHz logic is disabled");
1501*a90b9d01SCy Schubert 		params.non_coloc_6ghz = 1;
1502780fb4a2SCy Schubert 	}
1503780fb4a2SCy Schubert 
1504f05cddf9SRui Paulo 	scan_params = &params;
1505f05cddf9SRui Paulo 
1506f05cddf9SRui Paulo scan:
1507f05cddf9SRui Paulo #ifdef CONFIG_P2P
1508f05cddf9SRui Paulo 	/*
1509f05cddf9SRui Paulo 	 * If the driver does not support multi-channel concurrency and a
1510f05cddf9SRui Paulo 	 * virtual interface that shares the same radio with the wpa_s interface
1511f05cddf9SRui Paulo 	 * is operating there may not be need to scan other channels apart from
1512f05cddf9SRui Paulo 	 * the current operating channel on the other virtual interface. Filter
1513f05cddf9SRui Paulo 	 * out other channels in case we are trying to find a connection for a
1514f05cddf9SRui Paulo 	 * station interface when we are not configured to prefer station
1515f05cddf9SRui Paulo 	 * connection and a concurrent operation is already in process.
1516f05cddf9SRui Paulo 	 */
15175b9c547cSRui Paulo 	if (wpa_s->scan_for_connection &&
15185b9c547cSRui Paulo 	    wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
1519f05cddf9SRui Paulo 	    !scan_params->freqs && !params.freqs &&
1520f05cddf9SRui Paulo 	    wpas_is_p2p_prioritized(wpa_s) &&
1521f05cddf9SRui Paulo 	    wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
1522f05cddf9SRui Paulo 	    non_p2p_network_enabled(wpa_s)) {
15235b9c547cSRui Paulo 		unsigned int num = wpa_s->num_multichan_concurrent;
15245b9c547cSRui Paulo 
15255b9c547cSRui Paulo 		params.freqs = os_calloc(num + 1, sizeof(int));
15265b9c547cSRui Paulo 		if (params.freqs) {
1527*a90b9d01SCy Schubert 			/*
1528*a90b9d01SCy Schubert 			 * Exclude the operating frequency of the current
1529*a90b9d01SCy Schubert 			 * interface since we're looking to transition off of
1530*a90b9d01SCy Schubert 			 * it.
1531*a90b9d01SCy Schubert 			 */
1532*a90b9d01SCy Schubert 			num = get_shared_radio_freqs(wpa_s, params.freqs, num,
1533*a90b9d01SCy Schubert 						     true);
15345b9c547cSRui Paulo 			if (num > 0 && num == wpa_s->num_multichan_concurrent) {
15355b9c547cSRui Paulo 				wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
15365b9c547cSRui Paulo 			} else {
15375b9c547cSRui Paulo 				os_free(params.freqs);
15385b9c547cSRui Paulo 				params.freqs = NULL;
15395b9c547cSRui Paulo 			}
1540f05cddf9SRui Paulo 		}
1541f05cddf9SRui Paulo 	}
15424b72b91aSCy Schubert 
1543*a90b9d01SCy Schubert 	if (!params.freqs && wpas_is_6ghz_supported(wpa_s, true) &&
1544*a90b9d01SCy Schubert 	    (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning))
1545*a90b9d01SCy Schubert 		wpas_p2p_scan_freqs(wpa_s, &params, true);
1546f05cddf9SRui Paulo #endif /* CONFIG_P2P */
1547f05cddf9SRui Paulo 
1548*a90b9d01SCy Schubert 	ret = wpa_supplicant_trigger_scan(wpa_s, scan_params, false, false);
1549f05cddf9SRui Paulo 
15505b9c547cSRui Paulo 	if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
15515b9c547cSRui Paulo 	    !wpa_s->manual_scan_freqs) {
15525b9c547cSRui Paulo 		/* Restore manual_scan_freqs for the next attempt */
15535b9c547cSRui Paulo 		wpa_s->manual_scan_freqs = params.freqs;
15545b9c547cSRui Paulo 		params.freqs = NULL;
15555b9c547cSRui Paulo 	}
15565b9c547cSRui Paulo 
1557f05cddf9SRui Paulo 	wpabuf_free(extra_ie);
1558e28a4053SRui Paulo 	os_free(params.freqs);
1559e28a4053SRui Paulo 	os_free(params.filter_ssids);
1560c1d255d3SCy Schubert 	os_free(params.mac_addr);
156139beb93cSSam Leffler 
156239beb93cSSam Leffler 	if (ret) {
1563f05cddf9SRui Paulo 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
15645b9c547cSRui Paulo 		if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
15655b9c547cSRui Paulo 			wpa_supplicant_set_state(wpa_s,
15665b9c547cSRui Paulo 						 wpa_s->scan_prev_wpa_state);
1567f05cddf9SRui Paulo 		/* Restore scan_req since we will try to scan again */
15685b9c547cSRui Paulo 		wpa_s->scan_req = wpa_s->last_scan_req;
1569e28a4053SRui Paulo 		wpa_supplicant_req_scan(wpa_s, 1, 0);
1570f05cddf9SRui Paulo 	} else {
1571f05cddf9SRui Paulo 		wpa_s->scan_for_connection = 0;
15725b9c547cSRui Paulo #ifdef CONFIG_INTERWORKING
15735b9c547cSRui Paulo 		wpa_s->interworking_fast_assoc_tried = 0;
15745b9c547cSRui Paulo #endif /* CONFIG_INTERWORKING */
1575c1d255d3SCy Schubert 		wpa_s->next_scan_bssid_wildcard_ssid = 0;
1576780fb4a2SCy Schubert 		if (params.bssid)
1577780fb4a2SCy Schubert 			os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
1578e28a4053SRui Paulo 	}
1579*a90b9d01SCy Schubert 
1580*a90b9d01SCy Schubert 	wpa_s->ml_probe_mld_id = -1;
1581*a90b9d01SCy Schubert 	wpa_s->ml_probe_links = 0;
1582*a90b9d01SCy Schubert 	os_memset(wpa_s->ml_probe_bssid, 0, sizeof(wpa_s->ml_probe_bssid));
158339beb93cSSam Leffler }
158439beb93cSSam Leffler 
158539beb93cSSam Leffler 
wpa_supplicant_update_scan_int(struct wpa_supplicant * wpa_s,int sec)15865b9c547cSRui Paulo void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
15875b9c547cSRui Paulo {
15885b9c547cSRui Paulo 	struct os_reltime remaining, new_int;
15895b9c547cSRui Paulo 	int cancelled;
15905b9c547cSRui Paulo 
15915b9c547cSRui Paulo 	cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
15925b9c547cSRui Paulo 					     &remaining);
15935b9c547cSRui Paulo 
15945b9c547cSRui Paulo 	new_int.sec = sec;
15955b9c547cSRui Paulo 	new_int.usec = 0;
15965b9c547cSRui Paulo 	if (cancelled && os_reltime_before(&remaining, &new_int)) {
15975b9c547cSRui Paulo 		new_int.sec = remaining.sec;
15985b9c547cSRui Paulo 		new_int.usec = remaining.usec;
15995b9c547cSRui Paulo 	}
16005b9c547cSRui Paulo 
16015b9c547cSRui Paulo 	if (cancelled) {
16025b9c547cSRui Paulo 		eloop_register_timeout(new_int.sec, new_int.usec,
16035b9c547cSRui Paulo 				       wpa_supplicant_scan, wpa_s, NULL);
16045b9c547cSRui Paulo 	}
16055b9c547cSRui Paulo 	wpa_s->scan_interval = sec;
16065b9c547cSRui Paulo }
16075b9c547cSRui Paulo 
16085b9c547cSRui Paulo 
160939beb93cSSam Leffler /**
161039beb93cSSam Leffler  * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
161139beb93cSSam Leffler  * @wpa_s: Pointer to wpa_supplicant data
161239beb93cSSam Leffler  * @sec: Number of seconds after which to scan
161339beb93cSSam Leffler  * @usec: Number of microseconds after which to scan
161439beb93cSSam Leffler  *
161539beb93cSSam Leffler  * This function is used to schedule a scan for neighboring access points after
161639beb93cSSam Leffler  * the specified time.
161739beb93cSSam Leffler  */
wpa_supplicant_req_scan(struct wpa_supplicant * wpa_s,int sec,int usec)161839beb93cSSam Leffler void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
161939beb93cSSam Leffler {
16205b9c547cSRui Paulo 	int res;
162139beb93cSSam Leffler 
16225b9c547cSRui Paulo 	if (wpa_s->p2p_mgmt) {
16235b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG,
16245b9c547cSRui Paulo 			"Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
16255b9c547cSRui Paulo 			sec, usec);
162639beb93cSSam Leffler 		return;
162739beb93cSSam Leffler 	}
162839beb93cSSam Leffler 
16295b9c547cSRui Paulo 	res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
16305b9c547cSRui Paulo 				    NULL);
16315b9c547cSRui Paulo 	if (res == 1) {
16325b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
163339beb93cSSam Leffler 			sec, usec);
16345b9c547cSRui Paulo 	} else if (res == 0) {
16355b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
16365b9c547cSRui Paulo 			sec, usec);
16375b9c547cSRui Paulo 	} else {
16385b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
16395b9c547cSRui Paulo 			sec, usec);
164039beb93cSSam Leffler 		eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
164139beb93cSSam Leffler 	}
16425b9c547cSRui Paulo }
164339beb93cSSam Leffler 
164439beb93cSSam Leffler 
164539beb93cSSam Leffler /**
1646f05cddf9SRui Paulo  * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
1647f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
1648f05cddf9SRui Paulo  * @sec: Number of seconds after which to scan
1649f05cddf9SRui Paulo  * @usec: Number of microseconds after which to scan
1650f05cddf9SRui Paulo  * Returns: 0 on success or -1 otherwise
1651f05cddf9SRui Paulo  *
1652f05cddf9SRui Paulo  * This function is used to schedule periodic scans for neighboring
1653f05cddf9SRui Paulo  * access points after the specified time.
1654f05cddf9SRui Paulo  */
wpa_supplicant_delayed_sched_scan(struct wpa_supplicant * wpa_s,int sec,int usec)1655f05cddf9SRui Paulo int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
1656f05cddf9SRui Paulo 				      int sec, int usec)
1657f05cddf9SRui Paulo {
1658f05cddf9SRui Paulo 	if (!wpa_s->sched_scan_supported)
1659f05cddf9SRui Paulo 		return -1;
1660f05cddf9SRui Paulo 
1661f05cddf9SRui Paulo 	eloop_register_timeout(sec, usec,
1662f05cddf9SRui Paulo 			       wpa_supplicant_delayed_sched_scan_timeout,
1663f05cddf9SRui Paulo 			       wpa_s, NULL);
1664f05cddf9SRui Paulo 
1665f05cddf9SRui Paulo 	return 0;
1666f05cddf9SRui Paulo }
1667f05cddf9SRui Paulo 
1668f05cddf9SRui Paulo 
166985732ac8SCy Schubert static void
wpa_scan_set_relative_rssi_params(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)167085732ac8SCy Schubert wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s,
167185732ac8SCy Schubert 				  struct wpa_driver_scan_params *params)
167285732ac8SCy Schubert {
167385732ac8SCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED ||
167485732ac8SCy Schubert 	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) ||
167585732ac8SCy Schubert 	    wpa_s->srp.relative_rssi_set == 0)
167685732ac8SCy Schubert 		return;
167785732ac8SCy Schubert 
167885732ac8SCy Schubert 	params->relative_rssi_set = 1;
167985732ac8SCy Schubert 	params->relative_rssi = wpa_s->srp.relative_rssi;
168085732ac8SCy Schubert 
168185732ac8SCy Schubert 	if (wpa_s->srp.relative_adjust_rssi == 0)
168285732ac8SCy Schubert 		return;
168385732ac8SCy Schubert 
168485732ac8SCy Schubert 	params->relative_adjust_band = wpa_s->srp.relative_adjust_band;
168585732ac8SCy Schubert 	params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi;
168685732ac8SCy Schubert }
168785732ac8SCy Schubert 
168885732ac8SCy Schubert 
1689f05cddf9SRui Paulo /**
1690f05cddf9SRui Paulo  * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
1691f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
1692f05cddf9SRui Paulo  * Returns: 0 is sched_scan was started or -1 otherwise
1693f05cddf9SRui Paulo  *
1694f05cddf9SRui Paulo  * This function is used to schedule periodic scans for neighboring
1695f05cddf9SRui Paulo  * access points repeating the scan continuously.
1696f05cddf9SRui Paulo  */
wpa_supplicant_req_sched_scan(struct wpa_supplicant * wpa_s)1697f05cddf9SRui Paulo int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
1698f05cddf9SRui Paulo {
1699f05cddf9SRui Paulo 	struct wpa_driver_scan_params params;
1700f05cddf9SRui Paulo 	struct wpa_driver_scan_params *scan_params;
1701f05cddf9SRui Paulo 	enum wpa_states prev_state;
1702f05cddf9SRui Paulo 	struct wpa_ssid *ssid = NULL;
1703f05cddf9SRui Paulo 	struct wpabuf *extra_ie = NULL;
1704f05cddf9SRui Paulo 	int ret;
1705f05cddf9SRui Paulo 	unsigned int max_sched_scan_ssids;
1706f05cddf9SRui Paulo 	int wildcard = 0;
1707f05cddf9SRui Paulo 	int need_ssids;
1708780fb4a2SCy Schubert 	struct sched_scan_plan scan_plan;
1709f05cddf9SRui Paulo 
1710f05cddf9SRui Paulo 	if (!wpa_s->sched_scan_supported)
1711f05cddf9SRui Paulo 		return -1;
1712f05cddf9SRui Paulo 
1713f05cddf9SRui Paulo 	if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
1714f05cddf9SRui Paulo 		max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
1715f05cddf9SRui Paulo 	else
1716f05cddf9SRui Paulo 		max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
1717f05cddf9SRui Paulo 	if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
1718f05cddf9SRui Paulo 		return -1;
1719f05cddf9SRui Paulo 
1720780fb4a2SCy Schubert 	wpa_s->sched_scan_stop_req = 0;
1721780fb4a2SCy Schubert 
1722f05cddf9SRui Paulo 	if (wpa_s->sched_scanning) {
1723f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
1724f05cddf9SRui Paulo 		return 0;
1725f05cddf9SRui Paulo 	}
1726f05cddf9SRui Paulo 
1727f05cddf9SRui Paulo 	need_ssids = 0;
1728f05cddf9SRui Paulo 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
1729f05cddf9SRui Paulo 		if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
1730f05cddf9SRui Paulo 			/* Use wildcard SSID to find this network */
1731f05cddf9SRui Paulo 			wildcard = 1;
1732f05cddf9SRui Paulo 		} else if (!wpas_network_disabled(wpa_s, ssid) &&
1733f05cddf9SRui Paulo 			   ssid->ssid_len)
1734f05cddf9SRui Paulo 			need_ssids++;
1735f05cddf9SRui Paulo 
1736f05cddf9SRui Paulo #ifdef CONFIG_WPS
1737f05cddf9SRui Paulo 		if (!wpas_network_disabled(wpa_s, ssid) &&
1738f05cddf9SRui Paulo 		    ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
1739f05cddf9SRui Paulo 			/*
1740f05cddf9SRui Paulo 			 * Normal scan is more reliable and faster for WPS
1741f05cddf9SRui Paulo 			 * operations and since these are for short periods of
1742f05cddf9SRui Paulo 			 * time, the benefit of trying to use sched_scan would
1743f05cddf9SRui Paulo 			 * be limited.
1744f05cddf9SRui Paulo 			 */
1745f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
1746f05cddf9SRui Paulo 				"sched_scan for WPS");
1747f05cddf9SRui Paulo 			return -1;
1748f05cddf9SRui Paulo 		}
1749f05cddf9SRui Paulo #endif /* CONFIG_WPS */
1750f05cddf9SRui Paulo 	}
1751f05cddf9SRui Paulo 	if (wildcard)
1752f05cddf9SRui Paulo 		need_ssids++;
1753f05cddf9SRui Paulo 
1754f05cddf9SRui Paulo 	if (wpa_s->normal_scans < 3 &&
1755f05cddf9SRui Paulo 	    (need_ssids <= wpa_s->max_scan_ssids ||
1756f05cddf9SRui Paulo 	     wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
1757f05cddf9SRui Paulo 		/*
1758f05cddf9SRui Paulo 		 * When normal scan can speed up operations, use that for the
1759f05cddf9SRui Paulo 		 * first operations before starting the sched_scan to allow
1760f05cddf9SRui Paulo 		 * user space sleep more. We do this only if the normal scan
1761f05cddf9SRui Paulo 		 * has functionality that is suitable for this or if the
1762f05cddf9SRui Paulo 		 * sched_scan does not have better support for multiple SSIDs.
1763f05cddf9SRui Paulo 		 */
1764f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
1765f05cddf9SRui Paulo 			"sched_scan for initial scans (normal_scans=%d)",
1766f05cddf9SRui Paulo 			wpa_s->normal_scans);
1767f05cddf9SRui Paulo 		return -1;
1768f05cddf9SRui Paulo 	}
1769f05cddf9SRui Paulo 
1770f05cddf9SRui Paulo 	os_memset(&params, 0, sizeof(params));
1771f05cddf9SRui Paulo 
1772f05cddf9SRui Paulo 	/* If we can't allocate space for the filters, we just don't filter */
17735b9c547cSRui Paulo 	params.filter_ssids = os_calloc(wpa_s->max_match_sets,
1774f05cddf9SRui Paulo 					sizeof(struct wpa_driver_scan_filter));
1775f05cddf9SRui Paulo 
1776f05cddf9SRui Paulo 	prev_state = wpa_s->wpa_state;
1777f05cddf9SRui Paulo 	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
1778f05cddf9SRui Paulo 	    wpa_s->wpa_state == WPA_INACTIVE)
1779f05cddf9SRui Paulo 		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
1780f05cddf9SRui Paulo 
1781f05cddf9SRui Paulo 	if (wpa_s->autoscan_params != NULL) {
1782f05cddf9SRui Paulo 		scan_params = wpa_s->autoscan_params;
1783f05cddf9SRui Paulo 		goto scan;
1784f05cddf9SRui Paulo 	}
1785f05cddf9SRui Paulo 
1786f05cddf9SRui Paulo 	/* Find the starting point from which to continue scanning */
1787f05cddf9SRui Paulo 	ssid = wpa_s->conf->ssid;
1788f05cddf9SRui Paulo 	if (wpa_s->prev_sched_ssid) {
1789f05cddf9SRui Paulo 		while (ssid) {
1790f05cddf9SRui Paulo 			if (ssid == wpa_s->prev_sched_ssid) {
1791f05cddf9SRui Paulo 				ssid = ssid->next;
1792f05cddf9SRui Paulo 				break;
1793f05cddf9SRui Paulo 			}
1794f05cddf9SRui Paulo 			ssid = ssid->next;
1795f05cddf9SRui Paulo 		}
1796f05cddf9SRui Paulo 	}
1797f05cddf9SRui Paulo 
1798f05cddf9SRui Paulo 	if (!ssid || !wpa_s->prev_sched_ssid) {
1799f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
1800f05cddf9SRui Paulo 		wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
1801f05cddf9SRui Paulo 		wpa_s->first_sched_scan = 1;
1802f05cddf9SRui Paulo 		ssid = wpa_s->conf->ssid;
1803f05cddf9SRui Paulo 		wpa_s->prev_sched_ssid = ssid;
1804f05cddf9SRui Paulo 	}
1805f05cddf9SRui Paulo 
1806f05cddf9SRui Paulo 	if (wildcard) {
1807f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
1808f05cddf9SRui Paulo 		params.num_ssids++;
1809f05cddf9SRui Paulo 	}
1810f05cddf9SRui Paulo 
1811f05cddf9SRui Paulo 	while (ssid) {
1812f05cddf9SRui Paulo 		if (wpas_network_disabled(wpa_s, ssid))
1813f05cddf9SRui Paulo 			goto next;
1814f05cddf9SRui Paulo 
1815f05cddf9SRui Paulo 		if (params.num_filter_ssids < wpa_s->max_match_sets &&
1816f05cddf9SRui Paulo 		    params.filter_ssids && ssid->ssid && ssid->ssid_len) {
1817f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
1818f05cddf9SRui Paulo 				wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
1819f05cddf9SRui Paulo 			os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
1820f05cddf9SRui Paulo 				  ssid->ssid, ssid->ssid_len);
1821f05cddf9SRui Paulo 			params.filter_ssids[params.num_filter_ssids].ssid_len =
1822f05cddf9SRui Paulo 				ssid->ssid_len;
1823f05cddf9SRui Paulo 			params.num_filter_ssids++;
1824f05cddf9SRui Paulo 		} else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
1825f05cddf9SRui Paulo 		{
1826f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
1827f05cddf9SRui Paulo 				"filter for sched_scan - drop filter");
1828f05cddf9SRui Paulo 			os_free(params.filter_ssids);
1829f05cddf9SRui Paulo 			params.filter_ssids = NULL;
1830f05cddf9SRui Paulo 			params.num_filter_ssids = 0;
1831f05cddf9SRui Paulo 		}
1832f05cddf9SRui Paulo 
1833f05cddf9SRui Paulo 		if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
1834f05cddf9SRui Paulo 			if (params.num_ssids == max_sched_scan_ssids)
1835f05cddf9SRui Paulo 				break; /* only room for broadcast SSID */
1836f05cddf9SRui Paulo 			wpa_dbg(wpa_s, MSG_DEBUG,
1837f05cddf9SRui Paulo 				"add to active scan ssid: %s",
1838f05cddf9SRui Paulo 				wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
1839f05cddf9SRui Paulo 			params.ssids[params.num_ssids].ssid =
1840f05cddf9SRui Paulo 				ssid->ssid;
1841f05cddf9SRui Paulo 			params.ssids[params.num_ssids].ssid_len =
1842f05cddf9SRui Paulo 				ssid->ssid_len;
1843f05cddf9SRui Paulo 			params.num_ssids++;
1844f05cddf9SRui Paulo 			if (params.num_ssids >= max_sched_scan_ssids) {
1845f05cddf9SRui Paulo 				wpa_s->prev_sched_ssid = ssid;
1846f05cddf9SRui Paulo 				do {
1847f05cddf9SRui Paulo 					ssid = ssid->next;
1848f05cddf9SRui Paulo 				} while (ssid &&
1849f05cddf9SRui Paulo 					 (wpas_network_disabled(wpa_s, ssid) ||
1850f05cddf9SRui Paulo 					  !ssid->scan_ssid));
1851f05cddf9SRui Paulo 				break;
1852f05cddf9SRui Paulo 			}
1853f05cddf9SRui Paulo 		}
1854f05cddf9SRui Paulo 
1855f05cddf9SRui Paulo 	next:
1856f05cddf9SRui Paulo 		wpa_s->prev_sched_ssid = ssid;
1857f05cddf9SRui Paulo 		ssid = ssid->next;
1858f05cddf9SRui Paulo 	}
1859f05cddf9SRui Paulo 
1860f05cddf9SRui Paulo 	if (params.num_filter_ssids == 0) {
1861f05cddf9SRui Paulo 		os_free(params.filter_ssids);
1862f05cddf9SRui Paulo 		params.filter_ssids = NULL;
1863f05cddf9SRui Paulo 	}
1864f05cddf9SRui Paulo 
1865f05cddf9SRui Paulo 	extra_ie = wpa_supplicant_extra_ies(wpa_s);
1866f05cddf9SRui Paulo 	if (extra_ie) {
1867f05cddf9SRui Paulo 		params.extra_ies = wpabuf_head(extra_ie);
1868f05cddf9SRui Paulo 		params.extra_ies_len = wpabuf_len(extra_ie);
1869f05cddf9SRui Paulo 	}
1870f05cddf9SRui Paulo 
18715b9c547cSRui Paulo 	if (wpa_s->conf->filter_rssi)
18725b9c547cSRui Paulo 		params.filter_rssi = wpa_s->conf->filter_rssi;
18735b9c547cSRui Paulo 
18745b9c547cSRui Paulo 	/* See if user specified frequencies. If so, scan only those. */
18755b9c547cSRui Paulo 	if (wpa_s->conf->freq_list && !params.freqs) {
18765b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG,
18775b9c547cSRui Paulo 			"Optimize scan based on conf->freq_list");
18785b9c547cSRui Paulo 		int_array_concat(&params.freqs, wpa_s->conf->freq_list);
18795b9c547cSRui Paulo 	}
18805b9c547cSRui Paulo 
188185732ac8SCy Schubert #ifdef CONFIG_MBO
188285732ac8SCy Schubert 	if (wpa_s->enable_oce & OCE_STA)
188385732ac8SCy Schubert 		params.oce_scan = 1;
188485732ac8SCy Schubert #endif /* CONFIG_MBO */
188585732ac8SCy Schubert 
1886f05cddf9SRui Paulo 	scan_params = &params;
1887f05cddf9SRui Paulo 
1888f05cddf9SRui Paulo scan:
1889780fb4a2SCy Schubert 	wpa_s->sched_scan_timed_out = 0;
1890780fb4a2SCy Schubert 
1891780fb4a2SCy Schubert 	/*
1892780fb4a2SCy Schubert 	 * We cannot support multiple scan plans if the scan request includes
1893780fb4a2SCy Schubert 	 * too many SSID's, so in this case use only the last scan plan and make
1894780fb4a2SCy Schubert 	 * it run infinitely. It will be stopped by the timeout.
1895780fb4a2SCy Schubert 	 */
1896780fb4a2SCy Schubert 	if (wpa_s->sched_scan_plans_num == 1 ||
1897780fb4a2SCy Schubert 	    (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
1898780fb4a2SCy Schubert 		params.sched_scan_plans = wpa_s->sched_scan_plans;
1899780fb4a2SCy Schubert 		params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
1900780fb4a2SCy Schubert 	} else if (wpa_s->sched_scan_plans_num > 1) {
1901780fb4a2SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG,
1902780fb4a2SCy Schubert 			"Too many SSIDs. Default to using single scheduled_scan plan");
1903780fb4a2SCy Schubert 		params.sched_scan_plans =
1904780fb4a2SCy Schubert 			&wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
1905780fb4a2SCy Schubert 						 1];
1906780fb4a2SCy Schubert 		params.sched_scan_plans_num = 1;
1907780fb4a2SCy Schubert 	} else {
1908780fb4a2SCy Schubert 		if (wpa_s->conf->sched_scan_interval)
1909780fb4a2SCy Schubert 			scan_plan.interval = wpa_s->conf->sched_scan_interval;
1910780fb4a2SCy Schubert 		else
1911780fb4a2SCy Schubert 			scan_plan.interval = 10;
1912780fb4a2SCy Schubert 
1913780fb4a2SCy Schubert 		if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
1914780fb4a2SCy Schubert 			wpa_printf(MSG_WARNING,
1915780fb4a2SCy Schubert 				   "Scan interval too long(%u), use the maximum allowed(%u)",
1916780fb4a2SCy Schubert 				   scan_plan.interval,
1917780fb4a2SCy Schubert 				   wpa_s->max_sched_scan_plan_interval);
1918780fb4a2SCy Schubert 			scan_plan.interval =
1919780fb4a2SCy Schubert 				wpa_s->max_sched_scan_plan_interval;
1920780fb4a2SCy Schubert 		}
1921780fb4a2SCy Schubert 
1922780fb4a2SCy Schubert 		scan_plan.iterations = 0;
1923780fb4a2SCy Schubert 		params.sched_scan_plans = &scan_plan;
1924780fb4a2SCy Schubert 		params.sched_scan_plans_num = 1;
1925780fb4a2SCy Schubert 	}
1926780fb4a2SCy Schubert 
192785732ac8SCy Schubert 	params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
192885732ac8SCy Schubert 
1929f05cddf9SRui Paulo 	if (ssid || !wpa_s->first_sched_scan) {
1930f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG,
193185732ac8SCy Schubert 			"Starting sched scan after %u seconds: interval %u timeout %d",
193285732ac8SCy Schubert 			params.sched_scan_start_delay,
1933780fb4a2SCy Schubert 			params.sched_scan_plans[0].interval,
1934780fb4a2SCy Schubert 			wpa_s->sched_scan_timeout);
1935f05cddf9SRui Paulo 	} else {
193685732ac8SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG,
193785732ac8SCy Schubert 			"Starting sched scan after %u seconds (no timeout)",
193885732ac8SCy Schubert 			params.sched_scan_start_delay);
1939f05cddf9SRui Paulo 	}
1940f05cddf9SRui Paulo 
19415b9c547cSRui Paulo 	wpa_setband_scan_freqs(wpa_s, scan_params);
19425b9c547cSRui Paulo 
194385732ac8SCy Schubert 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
1944c1d255d3SCy Schubert 	    wpa_s->wpa_state <= WPA_SCANNING)
1945c1d255d3SCy Schubert 		wpa_setup_mac_addr_rand_params(&params,
1946c1d255d3SCy Schubert 					       wpa_s->mac_addr_sched_scan);
19475b9c547cSRui Paulo 
194885732ac8SCy Schubert 	wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
194985732ac8SCy Schubert 
1950780fb4a2SCy Schubert 	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
1951f05cddf9SRui Paulo 	wpabuf_free(extra_ie);
1952f05cddf9SRui Paulo 	os_free(params.filter_ssids);
1953c1d255d3SCy Schubert 	os_free(params.mac_addr);
1954f05cddf9SRui Paulo 	if (ret) {
1955f05cddf9SRui Paulo 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
1956f05cddf9SRui Paulo 		if (prev_state != wpa_s->wpa_state)
1957f05cddf9SRui Paulo 			wpa_supplicant_set_state(wpa_s, prev_state);
1958f05cddf9SRui Paulo 		return ret;
1959f05cddf9SRui Paulo 	}
1960f05cddf9SRui Paulo 
1961f05cddf9SRui Paulo 	/* If we have more SSIDs to scan, add a timeout so we scan them too */
1962f05cddf9SRui Paulo 	if (ssid || !wpa_s->first_sched_scan) {
1963f05cddf9SRui Paulo 		wpa_s->sched_scan_timed_out = 0;
1964f05cddf9SRui Paulo 		eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
1965f05cddf9SRui Paulo 				       wpa_supplicant_sched_scan_timeout,
1966f05cddf9SRui Paulo 				       wpa_s, NULL);
1967f05cddf9SRui Paulo 		wpa_s->first_sched_scan = 0;
1968f05cddf9SRui Paulo 		wpa_s->sched_scan_timeout /= 2;
1969780fb4a2SCy Schubert 		params.sched_scan_plans[0].interval *= 2;
1970780fb4a2SCy Schubert 		if ((unsigned int) wpa_s->sched_scan_timeout <
1971780fb4a2SCy Schubert 		    params.sched_scan_plans[0].interval ||
1972780fb4a2SCy Schubert 		    params.sched_scan_plans[0].interval >
1973780fb4a2SCy Schubert 		    wpa_s->max_sched_scan_plan_interval) {
1974780fb4a2SCy Schubert 			params.sched_scan_plans[0].interval = 10;
19755b9c547cSRui Paulo 			wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
1976f05cddf9SRui Paulo 		}
19775b9c547cSRui Paulo 	}
19785b9c547cSRui Paulo 
19795b9c547cSRui Paulo 	/* If there is no more ssids, start next time from the beginning */
19805b9c547cSRui Paulo 	if (!ssid)
19815b9c547cSRui Paulo 		wpa_s->prev_sched_ssid = NULL;
1982f05cddf9SRui Paulo 
1983f05cddf9SRui Paulo 	return 0;
1984f05cddf9SRui Paulo }
1985f05cddf9SRui Paulo 
1986f05cddf9SRui Paulo 
1987f05cddf9SRui Paulo /**
198839beb93cSSam Leffler  * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
198939beb93cSSam Leffler  * @wpa_s: Pointer to wpa_supplicant data
199039beb93cSSam Leffler  *
199139beb93cSSam Leffler  * This function is used to cancel a scan request scheduled with
199239beb93cSSam Leffler  * wpa_supplicant_req_scan().
199339beb93cSSam Leffler  */
wpa_supplicant_cancel_scan(struct wpa_supplicant * wpa_s)199439beb93cSSam Leffler void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
199539beb93cSSam Leffler {
1996f05cddf9SRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
199739beb93cSSam Leffler 	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
199839beb93cSSam Leffler }
19993157ba21SRui Paulo 
20003157ba21SRui Paulo 
2001f05cddf9SRui Paulo /**
20025b9c547cSRui Paulo  * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan
20035b9c547cSRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
20045b9c547cSRui Paulo  *
20055b9c547cSRui Paulo  * This function is used to stop a delayed scheduled scan.
20065b9c547cSRui Paulo  */
wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant * wpa_s)20075b9c547cSRui Paulo void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s)
20085b9c547cSRui Paulo {
20095b9c547cSRui Paulo 	if (!wpa_s->sched_scan_supported)
20105b9c547cSRui Paulo 		return;
20115b9c547cSRui Paulo 
20125b9c547cSRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan");
20135b9c547cSRui Paulo 	eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout,
20145b9c547cSRui Paulo 			     wpa_s, NULL);
20155b9c547cSRui Paulo }
20165b9c547cSRui Paulo 
20175b9c547cSRui Paulo 
20185b9c547cSRui Paulo /**
2019f05cddf9SRui Paulo  * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
2020f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
2021f05cddf9SRui Paulo  *
2022f05cddf9SRui Paulo  * This function is used to stop a periodic scheduled scan.
2023f05cddf9SRui Paulo  */
wpa_supplicant_cancel_sched_scan(struct wpa_supplicant * wpa_s)2024f05cddf9SRui Paulo void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
2025f05cddf9SRui Paulo {
2026f05cddf9SRui Paulo 	if (!wpa_s->sched_scanning)
2027f05cddf9SRui Paulo 		return;
2028f05cddf9SRui Paulo 
2029780fb4a2SCy Schubert 	if (wpa_s->sched_scanning)
2030780fb4a2SCy Schubert 		wpa_s->sched_scan_stop_req = 1;
2031780fb4a2SCy Schubert 
2032f05cddf9SRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
2033f05cddf9SRui Paulo 	eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
2034f05cddf9SRui Paulo 	wpa_supplicant_stop_sched_scan(wpa_s);
2035f05cddf9SRui Paulo }
2036f05cddf9SRui Paulo 
2037f05cddf9SRui Paulo 
2038f05cddf9SRui Paulo /**
2039f05cddf9SRui Paulo  * wpa_supplicant_notify_scanning - Indicate possible scan state change
2040f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
2041f05cddf9SRui Paulo  * @scanning: Whether scanning is currently in progress
2042f05cddf9SRui Paulo  *
2043f05cddf9SRui Paulo  * This function is to generate scanning notifycations. It is called whenever
2044f05cddf9SRui Paulo  * there may have been a change in scanning (scan started, completed, stopped).
2045f05cddf9SRui Paulo  * wpas_notify_scanning() is called whenever the scanning state changed from the
2046f05cddf9SRui Paulo  * previously notified state.
2047f05cddf9SRui Paulo  */
wpa_supplicant_notify_scanning(struct wpa_supplicant * wpa_s,int scanning)20483157ba21SRui Paulo void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
20493157ba21SRui Paulo 				    int scanning)
20503157ba21SRui Paulo {
20513157ba21SRui Paulo 	if (wpa_s->scanning != scanning) {
20523157ba21SRui Paulo 		wpa_s->scanning = scanning;
2053e28a4053SRui Paulo 		wpas_notify_scanning(wpa_s);
20543157ba21SRui Paulo 	}
20553157ba21SRui Paulo }
20563157ba21SRui Paulo 
2057e28a4053SRui Paulo 
wpa_scan_get_max_rate(const struct wpa_scan_res * res)2058e28a4053SRui Paulo static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
2059e28a4053SRui Paulo {
2060e28a4053SRui Paulo 	int rate = 0;
2061e28a4053SRui Paulo 	const u8 *ie;
2062e28a4053SRui Paulo 	int i;
2063e28a4053SRui Paulo 
2064e28a4053SRui Paulo 	ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
2065e28a4053SRui Paulo 	for (i = 0; ie && i < ie[1]; i++) {
2066e28a4053SRui Paulo 		if ((ie[i + 2] & 0x7f) > rate)
2067e28a4053SRui Paulo 			rate = ie[i + 2] & 0x7f;
2068e28a4053SRui Paulo 	}
2069e28a4053SRui Paulo 
2070e28a4053SRui Paulo 	ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
2071e28a4053SRui Paulo 	for (i = 0; ie && i < ie[1]; i++) {
2072e28a4053SRui Paulo 		if ((ie[i + 2] & 0x7f) > rate)
2073e28a4053SRui Paulo 			rate = ie[i + 2] & 0x7f;
2074e28a4053SRui Paulo 	}
2075e28a4053SRui Paulo 
2076e28a4053SRui Paulo 	return rate;
2077e28a4053SRui Paulo }
2078e28a4053SRui Paulo 
2079e28a4053SRui Paulo 
2080f05cddf9SRui Paulo /**
2081f05cddf9SRui Paulo  * wpa_scan_get_ie - Fetch a specified information element from a scan result
2082f05cddf9SRui Paulo  * @res: Scan result entry
2083f05cddf9SRui Paulo  * @ie: Information element identitifier (WLAN_EID_*)
2084f05cddf9SRui Paulo  * Returns: Pointer to the information element (id field) or %NULL if not found
2085f05cddf9SRui Paulo  *
2086f05cddf9SRui Paulo  * This function returns the first matching information element in the scan
2087f05cddf9SRui Paulo  * result.
2088f05cddf9SRui Paulo  */
wpa_scan_get_ie(const struct wpa_scan_res * res,u8 ie)2089e28a4053SRui Paulo const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
2090e28a4053SRui Paulo {
209185732ac8SCy Schubert 	size_t ie_len = res->ie_len;
209285732ac8SCy Schubert 
209385732ac8SCy Schubert 	/* Use the Beacon frame IEs if res->ie_len is not available */
209485732ac8SCy Schubert 	if (!ie_len)
209585732ac8SCy Schubert 		ie_len = res->beacon_ie_len;
209685732ac8SCy Schubert 
209785732ac8SCy Schubert 	return get_ie((const u8 *) (res + 1), ie_len, ie);
2098e28a4053SRui Paulo }
2099e28a4053SRui Paulo 
2100e28a4053SRui Paulo 
wpa_scan_get_ml_ie(const struct wpa_scan_res * res,u8 type)2101*a90b9d01SCy Schubert const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type)
2102*a90b9d01SCy Schubert {
2103*a90b9d01SCy Schubert 	size_t ie_len = res->ie_len;
2104*a90b9d01SCy Schubert 
2105*a90b9d01SCy Schubert 	/* Use the Beacon frame IEs if res->ie_len is not available */
2106*a90b9d01SCy Schubert 	if (!ie_len)
2107*a90b9d01SCy Schubert 		ie_len = res->beacon_ie_len;
2108*a90b9d01SCy Schubert 
2109*a90b9d01SCy Schubert 	return get_ml_ie((const u8 *) (res + 1), ie_len, type);
2110*a90b9d01SCy Schubert }
2111*a90b9d01SCy Schubert 
2112*a90b9d01SCy Schubert 
2113f05cddf9SRui Paulo /**
2114f05cddf9SRui Paulo  * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
2115f05cddf9SRui Paulo  * @res: Scan result entry
2116f05cddf9SRui Paulo  * @vendor_type: Vendor type (four octets starting the IE payload)
2117f05cddf9SRui Paulo  * Returns: Pointer to the information element (id field) or %NULL if not found
2118f05cddf9SRui Paulo  *
2119f05cddf9SRui Paulo  * This function returns the first matching information element in the scan
2120f05cddf9SRui Paulo  * result.
2121f05cddf9SRui Paulo  */
wpa_scan_get_vendor_ie(const struct wpa_scan_res * res,u32 vendor_type)2122e28a4053SRui Paulo const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
2123e28a4053SRui Paulo 				  u32 vendor_type)
2124e28a4053SRui Paulo {
2125c1d255d3SCy Schubert 	const u8 *ies;
2126c1d255d3SCy Schubert 	const struct element *elem;
2127e28a4053SRui Paulo 
2128c1d255d3SCy Schubert 	ies = (const u8 *) (res + 1);
2129e28a4053SRui Paulo 
2130c1d255d3SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, res->ie_len) {
2131c1d255d3SCy Schubert 		if (elem->datalen >= 4 &&
2132c1d255d3SCy Schubert 		    vendor_type == WPA_GET_BE32(elem->data))
2133c1d255d3SCy Schubert 			return &elem->id;
2134e28a4053SRui Paulo 	}
2135e28a4053SRui Paulo 
2136e28a4053SRui Paulo 	return NULL;
2137e28a4053SRui Paulo }
2138e28a4053SRui Paulo 
2139e28a4053SRui Paulo 
2140f05cddf9SRui Paulo /**
21415b9c547cSRui Paulo  * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
21425b9c547cSRui Paulo  * @res: Scan result entry
21435b9c547cSRui Paulo  * @vendor_type: Vendor type (four octets starting the IE payload)
21445b9c547cSRui Paulo  * Returns: Pointer to the information element (id field) or %NULL if not found
21455b9c547cSRui Paulo  *
21465b9c547cSRui Paulo  * This function returns the first matching information element in the scan
21475b9c547cSRui Paulo  * result.
21485b9c547cSRui Paulo  *
21495b9c547cSRui Paulo  * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
21505b9c547cSRui Paulo  * from Beacon frames instead of either Beacon or Probe Response frames.
21515b9c547cSRui Paulo  */
wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res * res,u32 vendor_type)21525b9c547cSRui Paulo const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
21535b9c547cSRui Paulo 					 u32 vendor_type)
21545b9c547cSRui Paulo {
2155c1d255d3SCy Schubert 	const u8 *ies;
2156c1d255d3SCy Schubert 	const struct element *elem;
21575b9c547cSRui Paulo 
21585b9c547cSRui Paulo 	if (res->beacon_ie_len == 0)
21595b9c547cSRui Paulo 		return NULL;
21605b9c547cSRui Paulo 
2161c1d255d3SCy Schubert 	ies = (const u8 *) (res + 1);
2162c1d255d3SCy Schubert 	ies += res->ie_len;
21635b9c547cSRui Paulo 
2164c1d255d3SCy Schubert 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies,
2165c1d255d3SCy Schubert 			    res->beacon_ie_len) {
2166c1d255d3SCy Schubert 		if (elem->datalen >= 4 &&
2167c1d255d3SCy Schubert 		    vendor_type == WPA_GET_BE32(elem->data))
2168c1d255d3SCy Schubert 			return &elem->id;
21695b9c547cSRui Paulo 	}
21705b9c547cSRui Paulo 
21715b9c547cSRui Paulo 	return NULL;
21725b9c547cSRui Paulo }
21735b9c547cSRui Paulo 
21745b9c547cSRui Paulo 
21755b9c547cSRui Paulo /**
2176f05cddf9SRui Paulo  * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
2177f05cddf9SRui Paulo  * @res: Scan result entry
2178f05cddf9SRui Paulo  * @vendor_type: Vendor type (four octets starting the IE payload)
2179f05cddf9SRui Paulo  * Returns: Pointer to the information element payload or %NULL if not found
2180f05cddf9SRui Paulo  *
2181f05cddf9SRui Paulo  * This function returns concatenated payload of possibly fragmented vendor
2182f05cddf9SRui Paulo  * specific information elements in the scan result. The caller is responsible
2183f05cddf9SRui Paulo  * for freeing the returned buffer.
2184f05cddf9SRui Paulo  */
wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res * res,u32 vendor_type)2185e28a4053SRui Paulo struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
2186e28a4053SRui Paulo 					     u32 vendor_type)
2187e28a4053SRui Paulo {
2188e28a4053SRui Paulo 	struct wpabuf *buf;
2189e28a4053SRui Paulo 	const u8 *end, *pos;
2190e28a4053SRui Paulo 
2191e28a4053SRui Paulo 	buf = wpabuf_alloc(res->ie_len);
2192e28a4053SRui Paulo 	if (buf == NULL)
2193e28a4053SRui Paulo 		return NULL;
2194e28a4053SRui Paulo 
2195e28a4053SRui Paulo 	pos = (const u8 *) (res + 1);
2196e28a4053SRui Paulo 	end = pos + res->ie_len;
2197e28a4053SRui Paulo 
2198780fb4a2SCy Schubert 	while (end - pos > 1) {
2199c1d255d3SCy Schubert 		u8 ie, len;
2200c1d255d3SCy Schubert 
2201c1d255d3SCy Schubert 		ie = pos[0];
2202c1d255d3SCy Schubert 		len = pos[1];
2203c1d255d3SCy Schubert 		if (len > end - pos - 2)
2204e28a4053SRui Paulo 			break;
2205c1d255d3SCy Schubert 		pos += 2;
2206c1d255d3SCy Schubert 		if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
2207c1d255d3SCy Schubert 		    vendor_type == WPA_GET_BE32(pos))
2208c1d255d3SCy Schubert 			wpabuf_put_data(buf, pos + 4, len - 4);
2209c1d255d3SCy Schubert 		pos += len;
2210e28a4053SRui Paulo 	}
2211e28a4053SRui Paulo 
2212e28a4053SRui Paulo 	if (wpabuf_len(buf) == 0) {
2213e28a4053SRui Paulo 		wpabuf_free(buf);
2214e28a4053SRui Paulo 		buf = NULL;
2215e28a4053SRui Paulo 	}
2216e28a4053SRui Paulo 
2217e28a4053SRui Paulo 	return buf;
2218e28a4053SRui Paulo }
2219e28a4053SRui Paulo 
2220e28a4053SRui Paulo 
wpas_channel_width_offset(enum chan_width cw)2221*a90b9d01SCy Schubert static int wpas_channel_width_offset(enum chan_width cw)
2222*a90b9d01SCy Schubert {
2223*a90b9d01SCy Schubert 	switch (cw) {
2224*a90b9d01SCy Schubert 	case CHAN_WIDTH_40:
2225*a90b9d01SCy Schubert 		return 1;
2226*a90b9d01SCy Schubert 	case CHAN_WIDTH_80:
2227*a90b9d01SCy Schubert 		return 2;
2228*a90b9d01SCy Schubert 	case CHAN_WIDTH_80P80:
2229*a90b9d01SCy Schubert 	case CHAN_WIDTH_160:
2230*a90b9d01SCy Schubert 		return 3;
2231*a90b9d01SCy Schubert 	case CHAN_WIDTH_320:
2232*a90b9d01SCy Schubert 		return 4;
2233*a90b9d01SCy Schubert 	default:
2234*a90b9d01SCy Schubert 		return 0;
2235*a90b9d01SCy Schubert 	}
2236*a90b9d01SCy Schubert }
2237*a90b9d01SCy Schubert 
2238*a90b9d01SCy Schubert 
2239*a90b9d01SCy Schubert /**
2240*a90b9d01SCy Schubert  * wpas_channel_width_tx_pwr - Calculate the max transmit power at the channel
2241*a90b9d01SCy Schubert  * width
2242*a90b9d01SCy Schubert  * @ies: Information elements
2243*a90b9d01SCy Schubert  * @ies_len: Length of elements
2244*a90b9d01SCy Schubert  * @cw: The channel width
2245*a90b9d01SCy Schubert  * Returns: The max transmit power at the channel width, TX_POWER_NO_CONSTRAINT
2246*a90b9d01SCy Schubert  * if it is not constrained.
2247*a90b9d01SCy Schubert  *
2248*a90b9d01SCy Schubert  * This function is only used to estimate the actual signal RSSI when associated
2249*a90b9d01SCy Schubert  * based on the beacon RSSI at the STA. Beacon frames are transmitted on 20 MHz
2250*a90b9d01SCy Schubert  * channels, while the Data frames usually use higher channel width. Therefore
2251*a90b9d01SCy Schubert  * their RSSIs may be different. Assuming there is a fixed gap between the TX
2252*a90b9d01SCy Schubert  * power limit of the STA defined by the Transmit Power Envelope element and the
2253*a90b9d01SCy Schubert  * TX power of the AP, the difference in the TX power of X MHz and Y MHz at the
2254*a90b9d01SCy Schubert  * STA equals to the difference at the AP, and the difference in the signal RSSI
2255*a90b9d01SCy Schubert  * at the STA. tx_pwr is a floating point number in the standard, but the error
2256*a90b9d01SCy Schubert  * of casting to int is trivial in comparing two BSSes.
2257*a90b9d01SCy Schubert  */
wpas_channel_width_tx_pwr(const u8 * ies,size_t ies_len,enum chan_width cw)2258*a90b9d01SCy Schubert static int wpas_channel_width_tx_pwr(const u8 *ies, size_t ies_len,
2259*a90b9d01SCy Schubert 				     enum chan_width cw)
2260*a90b9d01SCy Schubert {
2261*a90b9d01SCy Schubert 	int offset = wpas_channel_width_offset(cw);
2262*a90b9d01SCy Schubert 	const struct element *elem;
2263*a90b9d01SCy Schubert 	int max_tx_power = TX_POWER_NO_CONSTRAINT, tx_pwr = 0;
2264*a90b9d01SCy Schubert 
2265*a90b9d01SCy Schubert 	for_each_element_id(elem, WLAN_EID_TRANSMIT_POWER_ENVELOPE, ies,
2266*a90b9d01SCy Schubert 			    ies_len) {
2267*a90b9d01SCy Schubert 		int max_tx_pwr_count;
2268*a90b9d01SCy Schubert 		enum max_tx_pwr_interpretation tx_pwr_intrpn;
2269*a90b9d01SCy Schubert 		enum reg_6g_client_type client_type;
2270*a90b9d01SCy Schubert 
2271*a90b9d01SCy Schubert 		if (elem->datalen < 1)
2272*a90b9d01SCy Schubert 			continue;
2273*a90b9d01SCy Schubert 
2274*a90b9d01SCy Schubert 		/*
2275*a90b9d01SCy Schubert 		 * IEEE Std 802.11ax-2021, 9.4.2.161 (Transmit Power Envelope
2276*a90b9d01SCy Schubert 		 * element) defines Maximum Transmit Power Count (B0-B2),
2277*a90b9d01SCy Schubert 		 * Maximum Transmit Power Interpretation (B3-B5), and Maximum
2278*a90b9d01SCy Schubert 		 * Transmit Power Category (B6-B7).
2279*a90b9d01SCy Schubert 		 */
2280*a90b9d01SCy Schubert 		max_tx_pwr_count = elem->data[0] & 0x07;
2281*a90b9d01SCy Schubert 		tx_pwr_intrpn = (elem->data[0] >> 3) & 0x07;
2282*a90b9d01SCy Schubert 		client_type = (elem->data[0] >> 6) & 0x03;
2283*a90b9d01SCy Schubert 
2284*a90b9d01SCy Schubert 		if (client_type != REG_DEFAULT_CLIENT)
2285*a90b9d01SCy Schubert 			continue;
2286*a90b9d01SCy Schubert 
2287*a90b9d01SCy Schubert 		if (tx_pwr_intrpn == LOCAL_EIRP ||
2288*a90b9d01SCy Schubert 		    tx_pwr_intrpn == REGULATORY_CLIENT_EIRP) {
2289*a90b9d01SCy Schubert 			int offs;
2290*a90b9d01SCy Schubert 
2291*a90b9d01SCy Schubert 			max_tx_pwr_count = MIN(max_tx_pwr_count, 3);
2292*a90b9d01SCy Schubert 			offs = MIN(offset, max_tx_pwr_count) + 1;
2293*a90b9d01SCy Schubert 			if (elem->datalen <= offs)
2294*a90b9d01SCy Schubert 				continue;
2295*a90b9d01SCy Schubert 			tx_pwr = (signed char) elem->data[offs];
2296*a90b9d01SCy Schubert 			/*
2297*a90b9d01SCy Schubert 			 * Maximum Transmit Power subfield is encoded as an
2298*a90b9d01SCy Schubert 			 * 8-bit 2s complement signed integer in the range -64
2299*a90b9d01SCy Schubert 			 * dBm to 63 dBm with a 0.5 dB step. 63.5 dBm means no
2300*a90b9d01SCy Schubert 			 * local maximum transmit power constraint.
2301*a90b9d01SCy Schubert 			 */
2302*a90b9d01SCy Schubert 			if (tx_pwr == 127)
2303*a90b9d01SCy Schubert 				continue;
2304*a90b9d01SCy Schubert 			tx_pwr /= 2;
2305*a90b9d01SCy Schubert 			max_tx_power = MIN(max_tx_power, tx_pwr);
2306*a90b9d01SCy Schubert 		} else if (tx_pwr_intrpn == LOCAL_EIRP_PSD ||
2307*a90b9d01SCy Schubert 			   tx_pwr_intrpn == REGULATORY_CLIENT_EIRP_PSD) {
2308*a90b9d01SCy Schubert 			if (elem->datalen < 2)
2309*a90b9d01SCy Schubert 				continue;
2310*a90b9d01SCy Schubert 
2311*a90b9d01SCy Schubert 			tx_pwr = (signed char) elem->data[1];
2312*a90b9d01SCy Schubert 			/*
2313*a90b9d01SCy Schubert 			 * Maximum Transmit PSD subfield is encoded as an 8-bit
2314*a90b9d01SCy Schubert 			 * 2s complement signed integer. -128 indicates that the
2315*a90b9d01SCy Schubert 			 * corresponding 20 MHz channel cannot be used for
2316*a90b9d01SCy Schubert 			 * transmission. +127 indicates that no maximum PSD
2317*a90b9d01SCy Schubert 			 * limit is specified for the corresponding 20 MHz
2318*a90b9d01SCy Schubert 			 * channel.
2319*a90b9d01SCy Schubert 			 */
2320*a90b9d01SCy Schubert 			if (tx_pwr == 127 || tx_pwr == -128)
2321*a90b9d01SCy Schubert 				continue;
2322*a90b9d01SCy Schubert 
2323*a90b9d01SCy Schubert 			/*
2324*a90b9d01SCy Schubert 			 * The Maximum Transmit PSD subfield indicates the
2325*a90b9d01SCy Schubert 			 * maximum transmit PSD for the 20 MHz channel. Suppose
2326*a90b9d01SCy Schubert 			 * the PSD value is X dBm/MHz, the TX power of N MHz is
2327*a90b9d01SCy Schubert 			 * X + 10*log10(N) = X + 10*log10(20) + 10*log10(N/20) =
2328*a90b9d01SCy Schubert 			 * X + 13 + 3*log2(N/20)
2329*a90b9d01SCy Schubert 			 */
2330*a90b9d01SCy Schubert 			tx_pwr = tx_pwr / 2 + 13 + offset * 3;
2331*a90b9d01SCy Schubert 			max_tx_power = MIN(max_tx_power, tx_pwr);
2332*a90b9d01SCy Schubert 		}
2333*a90b9d01SCy Schubert 	}
2334*a90b9d01SCy Schubert 
2335*a90b9d01SCy Schubert 	return max_tx_power;
2336*a90b9d01SCy Schubert }
2337*a90b9d01SCy Schubert 
2338*a90b9d01SCy Schubert 
2339*a90b9d01SCy Schubert /**
2340*a90b9d01SCy Schubert  * Estimate the RSSI bump of channel width |cw| with respect to 20 MHz channel.
2341*a90b9d01SCy Schubert  * If the TX power has no constraint, it is unable to estimate the RSSI bump.
2342*a90b9d01SCy Schubert  */
wpas_channel_width_rssi_bump(const u8 * ies,size_t ies_len,enum chan_width cw)2343*a90b9d01SCy Schubert int wpas_channel_width_rssi_bump(const u8 *ies, size_t ies_len,
2344*a90b9d01SCy Schubert 				 enum chan_width cw)
2345*a90b9d01SCy Schubert {
2346*a90b9d01SCy Schubert 	int max_20mhz_tx_pwr = wpas_channel_width_tx_pwr(ies, ies_len,
2347*a90b9d01SCy Schubert 							 CHAN_WIDTH_20);
2348*a90b9d01SCy Schubert 	int max_cw_tx_pwr = wpas_channel_width_tx_pwr(ies, ies_len, cw);
2349*a90b9d01SCy Schubert 
2350*a90b9d01SCy Schubert 	return (max_20mhz_tx_pwr == TX_POWER_NO_CONSTRAINT ||
2351*a90b9d01SCy Schubert 		max_cw_tx_pwr == TX_POWER_NO_CONSTRAINT) ?
2352*a90b9d01SCy Schubert 		0 : (max_cw_tx_pwr - max_20mhz_tx_pwr);
2353*a90b9d01SCy Schubert }
2354*a90b9d01SCy Schubert 
2355*a90b9d01SCy Schubert 
wpas_adjust_snr_by_chanwidth(const u8 * ies,size_t ies_len,enum chan_width max_cw,int snr)2356*a90b9d01SCy Schubert int wpas_adjust_snr_by_chanwidth(const u8 *ies, size_t ies_len,
2357*a90b9d01SCy Schubert 				 enum chan_width max_cw, int snr)
2358*a90b9d01SCy Schubert {
2359*a90b9d01SCy Schubert 	int rssi_bump = wpas_channel_width_rssi_bump(ies, ies_len, max_cw);
2360*a90b9d01SCy Schubert 	/*
2361*a90b9d01SCy Schubert 	 * The noise has uniform power spectral density (PSD) across the
2362*a90b9d01SCy Schubert 	 * frequency band, its power is proportional to the channel width.
2363*a90b9d01SCy Schubert 	 * Suppose the PSD of noise is X dBm/MHz, the noise power of N MHz is
2364*a90b9d01SCy Schubert 	 * X + 10*log10(N), and the noise power bump with respect to 20 MHz is
2365*a90b9d01SCy Schubert 	 * 10*log10(N) - 10*log10(20) = 10*log10(N/20) = 3*log2(N/20)
2366*a90b9d01SCy Schubert 	 */
2367*a90b9d01SCy Schubert 	int noise_bump = 3 * wpas_channel_width_offset(max_cw);
2368*a90b9d01SCy Schubert 
2369*a90b9d01SCy Schubert 	return snr + rssi_bump - noise_bump;
2370*a90b9d01SCy Schubert }
2371*a90b9d01SCy Schubert 
2372*a90b9d01SCy Schubert 
2373e28a4053SRui Paulo /* Compare function for sorting scan results. Return >0 if @b is considered
2374e28a4053SRui Paulo  * better. */
wpa_scan_result_compar(const void * a,const void * b)2375e28a4053SRui Paulo static int wpa_scan_result_compar(const void *a, const void *b)
2376e28a4053SRui Paulo {
2377e28a4053SRui Paulo 	struct wpa_scan_res **_wa = (void *) a;
2378e28a4053SRui Paulo 	struct wpa_scan_res **_wb = (void *) b;
2379e28a4053SRui Paulo 	struct wpa_scan_res *wa = *_wa;
2380e28a4053SRui Paulo 	struct wpa_scan_res *wb = *_wb;
23815b9c547cSRui Paulo 	int wpa_a, wpa_b;
23825b9c547cSRui Paulo 	int snr_a, snr_b, snr_a_full, snr_b_full;
2383*a90b9d01SCy Schubert 	size_t ies_len;
2384*a90b9d01SCy Schubert 	const u8 *rsne_a, *rsne_b;
2385e28a4053SRui Paulo 
2386e28a4053SRui Paulo 	/* WPA/WPA2 support preferred */
2387e28a4053SRui Paulo 	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
2388e28a4053SRui Paulo 		wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
2389e28a4053SRui Paulo 	wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
2390e28a4053SRui Paulo 		wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
2391e28a4053SRui Paulo 
2392e28a4053SRui Paulo 	if (wpa_b && !wpa_a)
2393e28a4053SRui Paulo 		return 1;
2394e28a4053SRui Paulo 	if (!wpa_b && wpa_a)
2395e28a4053SRui Paulo 		return -1;
2396e28a4053SRui Paulo 
2397e28a4053SRui Paulo 	/* privacy support preferred */
2398e28a4053SRui Paulo 	if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
2399e28a4053SRui Paulo 	    (wb->caps & IEEE80211_CAP_PRIVACY))
2400e28a4053SRui Paulo 		return 1;
2401e28a4053SRui Paulo 	if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
2402e28a4053SRui Paulo 	    (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
2403e28a4053SRui Paulo 		return -1;
2404e28a4053SRui Paulo 
24055b9c547cSRui Paulo 	if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
2406*a90b9d01SCy Schubert 		/*
2407*a90b9d01SCy Schubert 		 * The scan result estimates SNR over 20 MHz, while Data frames
2408*a90b9d01SCy Schubert 		 * usually use wider channel width. The TX power and noise power
2409*a90b9d01SCy Schubert 		 * are both affected by the channel width.
2410*a90b9d01SCy Schubert 		 */
2411*a90b9d01SCy Schubert 		ies_len = wa->ie_len ? wa->ie_len : wa->beacon_ie_len;
2412*a90b9d01SCy Schubert 		snr_a_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wa + 1),
2413*a90b9d01SCy Schubert 							  ies_len, wa->max_cw,
2414*a90b9d01SCy Schubert 							  wa->snr);
2415*a90b9d01SCy Schubert 		snr_a = MIN(snr_a_full, GREAT_SNR);
2416*a90b9d01SCy Schubert 		ies_len = wb->ie_len ? wb->ie_len : wb->beacon_ie_len;
2417*a90b9d01SCy Schubert 		snr_b_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wb + 1),
2418*a90b9d01SCy Schubert 							  ies_len, wb->max_cw,
2419*a90b9d01SCy Schubert 							  wb->snr);
2420*a90b9d01SCy Schubert 		snr_b = MIN(snr_b_full, GREAT_SNR);
2421f05cddf9SRui Paulo 	} else {
24225b9c547cSRui Paulo 		/* Level is not in dBm, so we can't calculate
24235b9c547cSRui Paulo 		 * SNR. Just use raw level (units unknown). */
24245b9c547cSRui Paulo 		snr_a = snr_a_full = wa->level;
24255b9c547cSRui Paulo 		snr_b = snr_b_full = wb->level;
2426f05cddf9SRui Paulo 	}
2427f05cddf9SRui Paulo 
2428*a90b9d01SCy Schubert 	/* If SNR of a SAE BSS is good or at least as high as the PSK BSS,
2429*a90b9d01SCy Schubert 	 * prefer SAE over PSK for mixed WPA3-Personal transition mode and
2430*a90b9d01SCy Schubert 	 * WPA2-Personal deployments */
2431*a90b9d01SCy Schubert 	rsne_a = wpa_scan_get_ie(wa, WLAN_EID_RSN);
2432*a90b9d01SCy Schubert 	rsne_b = wpa_scan_get_ie(wb, WLAN_EID_RSN);
2433*a90b9d01SCy Schubert 	if (rsne_a && rsne_b) {
2434*a90b9d01SCy Schubert 		struct wpa_ie_data data;
2435*a90b9d01SCy Schubert 		bool psk_a = false, psk_b = false, sae_a = false, sae_b = false;
2436*a90b9d01SCy Schubert 
2437*a90b9d01SCy Schubert 		if (wpa_parse_wpa_ie_rsn(rsne_a, 2 + rsne_a[1], &data) == 0) {
2438*a90b9d01SCy Schubert 			psk_a = wpa_key_mgmt_wpa_psk_no_sae(data.key_mgmt);
2439*a90b9d01SCy Schubert 			sae_a = wpa_key_mgmt_sae(data.key_mgmt);
2440*a90b9d01SCy Schubert 		}
2441*a90b9d01SCy Schubert 		if (wpa_parse_wpa_ie_rsn(rsne_b, 2 + rsne_b[1], &data) == 0) {
2442*a90b9d01SCy Schubert 			psk_b = wpa_key_mgmt_wpa_psk_no_sae(data.key_mgmt);
2443*a90b9d01SCy Schubert 			sae_b = wpa_key_mgmt_sae(data.key_mgmt);
2444*a90b9d01SCy Schubert 		}
2445*a90b9d01SCy Schubert 
2446*a90b9d01SCy Schubert 		if (sae_a && !sae_b && psk_b &&
2447*a90b9d01SCy Schubert 		    (snr_a >= GREAT_SNR || snr_a >= snr_b))
2448*a90b9d01SCy Schubert 			return -1;
2449*a90b9d01SCy Schubert 		if (sae_b && !sae_a && psk_a &&
2450*a90b9d01SCy Schubert 		    (snr_b >= GREAT_SNR || snr_b >= snr_a))
2451*a90b9d01SCy Schubert 			return 1;
2452*a90b9d01SCy Schubert 	}
2453*a90b9d01SCy Schubert 
2454c1d255d3SCy Schubert 	/* If SNR is close, decide by max rate or frequency band. For cases
2455c1d255d3SCy Schubert 	 * involving the 6 GHz band, use the throughput estimate irrespective
2456c1d255d3SCy Schubert 	 * of the SNR difference since the LPI/VLP rules may result in
2457c1d255d3SCy Schubert 	 * significant differences in SNR for cases where the estimated
2458c1d255d3SCy Schubert 	 * throughput can be considerably higher with the lower SNR. */
2459c1d255d3SCy Schubert 	if (snr_a && snr_b && (abs(snr_b - snr_a) < 7 ||
2460c1d255d3SCy Schubert 			       is_6ghz_freq(wa->freq) ||
2461c1d255d3SCy Schubert 			       is_6ghz_freq(wb->freq))) {
24625b9c547cSRui Paulo 		if (wa->est_throughput != wb->est_throughput)
24634bc52338SCy Schubert 			return (int) wb->est_throughput -
24644bc52338SCy Schubert 				(int) wa->est_throughput;
246585732ac8SCy Schubert 	}
246685732ac8SCy Schubert 	if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
246785732ac8SCy Schubert 	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
2468c1d255d3SCy Schubert 		if (is_6ghz_freq(wa->freq) ^ is_6ghz_freq(wb->freq))
2469c1d255d3SCy Schubert 			return is_6ghz_freq(wa->freq) ? -1 : 1;
2470f05cddf9SRui Paulo 		if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
2471f05cddf9SRui Paulo 			return IS_5GHZ(wa->freq) ? -1 : 1;
2472e28a4053SRui Paulo 	}
2473e28a4053SRui Paulo 
2474f05cddf9SRui Paulo 	/* all things being equal, use SNR; if SNRs are
2475f05cddf9SRui Paulo 	 * identical, use quality values since some drivers may only report
2476f05cddf9SRui Paulo 	 * that value and leave the signal level zero */
24775b9c547cSRui Paulo 	if (snr_b_full == snr_a_full)
2478f05cddf9SRui Paulo 		return wb->qual - wa->qual;
24795b9c547cSRui Paulo 	return snr_b_full - snr_a_full;
2480f05cddf9SRui Paulo }
2481f05cddf9SRui Paulo 
2482f05cddf9SRui Paulo 
2483f05cddf9SRui Paulo #ifdef CONFIG_WPS
2484f05cddf9SRui Paulo /* Compare function for sorting scan results when searching a WPS AP for
2485f05cddf9SRui Paulo  * provisioning. Return >0 if @b is considered better. */
wpa_scan_result_wps_compar(const void * a,const void * b)2486f05cddf9SRui Paulo static int wpa_scan_result_wps_compar(const void *a, const void *b)
2487f05cddf9SRui Paulo {
2488f05cddf9SRui Paulo 	struct wpa_scan_res **_wa = (void *) a;
2489f05cddf9SRui Paulo 	struct wpa_scan_res **_wb = (void *) b;
2490f05cddf9SRui Paulo 	struct wpa_scan_res *wa = *_wa;
2491f05cddf9SRui Paulo 	struct wpa_scan_res *wb = *_wb;
2492f05cddf9SRui Paulo 	int uses_wps_a, uses_wps_b;
2493f05cddf9SRui Paulo 	struct wpabuf *wps_a, *wps_b;
2494f05cddf9SRui Paulo 	int res;
2495f05cddf9SRui Paulo 
2496f05cddf9SRui Paulo 	/* Optimization - check WPS IE existence before allocated memory and
2497f05cddf9SRui Paulo 	 * doing full reassembly. */
2498f05cddf9SRui Paulo 	uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
2499f05cddf9SRui Paulo 	uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
2500f05cddf9SRui Paulo 	if (uses_wps_a && !uses_wps_b)
2501f05cddf9SRui Paulo 		return -1;
2502f05cddf9SRui Paulo 	if (!uses_wps_a && uses_wps_b)
2503f05cddf9SRui Paulo 		return 1;
2504f05cddf9SRui Paulo 
2505f05cddf9SRui Paulo 	if (uses_wps_a && uses_wps_b) {
2506f05cddf9SRui Paulo 		wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
2507f05cddf9SRui Paulo 		wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
2508f05cddf9SRui Paulo 		res = wps_ap_priority_compar(wps_a, wps_b);
2509f05cddf9SRui Paulo 		wpabuf_free(wps_a);
2510f05cddf9SRui Paulo 		wpabuf_free(wps_b);
2511f05cddf9SRui Paulo 		if (res)
2512f05cddf9SRui Paulo 			return res;
2513f05cddf9SRui Paulo 	}
2514f05cddf9SRui Paulo 
2515f05cddf9SRui Paulo 	/*
2516f05cddf9SRui Paulo 	 * Do not use current AP security policy as a sorting criteria during
2517f05cddf9SRui Paulo 	 * WPS provisioning step since the AP may get reconfigured at the
2518f05cddf9SRui Paulo 	 * completion of provisioning.
2519f05cddf9SRui Paulo 	 */
2520f05cddf9SRui Paulo 
2521e28a4053SRui Paulo 	/* all things being equal, use signal level; if signal levels are
2522e28a4053SRui Paulo 	 * identical, use quality values since some drivers may only report
2523e28a4053SRui Paulo 	 * that value and leave the signal level zero */
2524e28a4053SRui Paulo 	if (wb->level == wa->level)
2525e28a4053SRui Paulo 		return wb->qual - wa->qual;
2526e28a4053SRui Paulo 	return wb->level - wa->level;
2527e28a4053SRui Paulo }
2528f05cddf9SRui Paulo #endif /* CONFIG_WPS */
2529f05cddf9SRui Paulo 
2530f05cddf9SRui Paulo 
dump_scan_res(struct wpa_scan_results * scan_res)2531f05cddf9SRui Paulo static void dump_scan_res(struct wpa_scan_results *scan_res)
2532f05cddf9SRui Paulo {
2533f05cddf9SRui Paulo #ifndef CONFIG_NO_STDOUT_DEBUG
2534f05cddf9SRui Paulo 	size_t i;
2535f05cddf9SRui Paulo 
2536f05cddf9SRui Paulo 	if (scan_res->res == NULL || scan_res->num == 0)
2537f05cddf9SRui Paulo 		return;
2538f05cddf9SRui Paulo 
2539f05cddf9SRui Paulo 	wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
2540f05cddf9SRui Paulo 
2541f05cddf9SRui Paulo 	for (i = 0; i < scan_res->num; i++) {
2542f05cddf9SRui Paulo 		struct wpa_scan_res *r = scan_res->res[i];
2543f05cddf9SRui Paulo 		u8 *pos;
2544*a90b9d01SCy Schubert 		const u8 *ssid_ie, *ssid = NULL;
2545*a90b9d01SCy Schubert 		size_t ssid_len = 0;
2546*a90b9d01SCy Schubert 
2547*a90b9d01SCy Schubert 		ssid_ie = wpa_scan_get_ie(r, WLAN_EID_SSID);
2548*a90b9d01SCy Schubert 		if (ssid_ie) {
2549*a90b9d01SCy Schubert 			ssid = ssid_ie + 2;
2550*a90b9d01SCy Schubert 			ssid_len = ssid_ie[1];
2551*a90b9d01SCy Schubert 		}
2552*a90b9d01SCy Schubert 
25535b9c547cSRui Paulo 		if (r->flags & WPA_SCAN_LEVEL_DBM) {
25545b9c547cSRui Paulo 			int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
25555b9c547cSRui Paulo 
2556*a90b9d01SCy Schubert 			wpa_printf(MSG_EXCESSIVE, MACSTR
2557*a90b9d01SCy Schubert 				   " ssid=%s freq=%d qual=%d noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
2558*a90b9d01SCy Schubert 				   MAC2STR(r->bssid),
2559*a90b9d01SCy Schubert 				   wpa_ssid_txt(ssid, ssid_len),
2560*a90b9d01SCy Schubert 				   r->freq, r->qual,
25615b9c547cSRui Paulo 				   r->noise, noise_valid ? "" : "~", r->level,
25625b9c547cSRui Paulo 				   r->snr, r->snr >= GREAT_SNR ? "*" : "",
25635b9c547cSRui Paulo 				   r->flags,
25645b9c547cSRui Paulo 				   r->age, r->est_throughput);
2565f05cddf9SRui Paulo 		} else {
2566*a90b9d01SCy Schubert 			wpa_printf(MSG_EXCESSIVE, MACSTR
2567*a90b9d01SCy Schubert 				   " ssid=%s freq=%d qual=%d noise=%d level=%d flags=0x%x age=%u est=%u",
2568*a90b9d01SCy Schubert 				   MAC2STR(r->bssid),
2569*a90b9d01SCy Schubert 				   wpa_ssid_txt(ssid, ssid_len),
2570*a90b9d01SCy Schubert 				   r->freq, r->qual,
25715b9c547cSRui Paulo 				   r->noise, r->level, r->flags, r->age,
25725b9c547cSRui Paulo 				   r->est_throughput);
2573f05cddf9SRui Paulo 		}
2574f05cddf9SRui Paulo 		pos = (u8 *) (r + 1);
2575f05cddf9SRui Paulo 		if (r->ie_len)
2576f05cddf9SRui Paulo 			wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
2577f05cddf9SRui Paulo 		pos += r->ie_len;
2578f05cddf9SRui Paulo 		if (r->beacon_ie_len)
2579f05cddf9SRui Paulo 			wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
2580f05cddf9SRui Paulo 				    pos, r->beacon_ie_len);
2581f05cddf9SRui Paulo 	}
2582f05cddf9SRui Paulo #endif /* CONFIG_NO_STDOUT_DEBUG */
2583f05cddf9SRui Paulo }
2584f05cddf9SRui Paulo 
2585f05cddf9SRui Paulo 
2586f05cddf9SRui Paulo /**
2587f05cddf9SRui Paulo  * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
2588f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
2589f05cddf9SRui Paulo  * @bssid: BSSID to check
2590f05cddf9SRui Paulo  * Returns: 0 if the BSSID is filtered or 1 if not
2591f05cddf9SRui Paulo  *
2592f05cddf9SRui Paulo  * This function is used to filter out specific BSSIDs from scan reslts mainly
2593f05cddf9SRui Paulo  * for testing purposes (SET bssid_filter ctrl_iface command).
2594f05cddf9SRui Paulo  */
wpa_supplicant_filter_bssid_match(struct wpa_supplicant * wpa_s,const u8 * bssid)2595f05cddf9SRui Paulo int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
2596f05cddf9SRui Paulo 				      const u8 *bssid)
2597f05cddf9SRui Paulo {
2598f05cddf9SRui Paulo 	size_t i;
2599f05cddf9SRui Paulo 
2600f05cddf9SRui Paulo 	if (wpa_s->bssid_filter == NULL)
2601f05cddf9SRui Paulo 		return 1;
2602f05cddf9SRui Paulo 
2603f05cddf9SRui Paulo 	for (i = 0; i < wpa_s->bssid_filter_count; i++) {
2604*a90b9d01SCy Schubert 		if (ether_addr_equal(wpa_s->bssid_filter + i * ETH_ALEN, bssid))
2605f05cddf9SRui Paulo 			return 1;
2606f05cddf9SRui Paulo 	}
2607f05cddf9SRui Paulo 
2608f05cddf9SRui Paulo 	return 0;
2609f05cddf9SRui Paulo }
2610f05cddf9SRui Paulo 
2611f05cddf9SRui Paulo 
filter_scan_res(struct wpa_supplicant * wpa_s,struct wpa_scan_results * res)2612*a90b9d01SCy Schubert static void filter_scan_res(struct wpa_supplicant *wpa_s,
2613f05cddf9SRui Paulo 			    struct wpa_scan_results *res)
2614f05cddf9SRui Paulo {
2615f05cddf9SRui Paulo 	size_t i, j;
2616f05cddf9SRui Paulo 
2617f05cddf9SRui Paulo 	if (wpa_s->bssid_filter == NULL)
2618f05cddf9SRui Paulo 		return;
2619f05cddf9SRui Paulo 
2620f05cddf9SRui Paulo 	for (i = 0, j = 0; i < res->num; i++) {
2621f05cddf9SRui Paulo 		if (wpa_supplicant_filter_bssid_match(wpa_s,
2622f05cddf9SRui Paulo 						      res->res[i]->bssid)) {
2623f05cddf9SRui Paulo 			res->res[j++] = res->res[i];
2624f05cddf9SRui Paulo 		} else {
2625f05cddf9SRui Paulo 			os_free(res->res[i]);
2626f05cddf9SRui Paulo 			res->res[i] = NULL;
2627f05cddf9SRui Paulo 		}
2628f05cddf9SRui Paulo 	}
2629f05cddf9SRui Paulo 
2630f05cddf9SRui Paulo 	if (res->num != j) {
2631f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
2632f05cddf9SRui Paulo 			   (int) (res->num - j));
2633f05cddf9SRui Paulo 		res->num = j;
2634f05cddf9SRui Paulo 	}
2635f05cddf9SRui Paulo }
2636e28a4053SRui Paulo 
2637e28a4053SRui Paulo 
scan_snr(struct wpa_scan_res * res)2638780fb4a2SCy Schubert void scan_snr(struct wpa_scan_res *res)
26395b9c547cSRui Paulo {
26405b9c547cSRui Paulo 	if (res->flags & WPA_SCAN_NOISE_INVALID) {
2641c1d255d3SCy Schubert 		res->noise = is_6ghz_freq(res->freq) ?
2642c1d255d3SCy Schubert 			DEFAULT_NOISE_FLOOR_6GHZ :
2643c1d255d3SCy Schubert 			(IS_5GHZ(res->freq) ?
2644c1d255d3SCy Schubert 			 DEFAULT_NOISE_FLOOR_5GHZ : DEFAULT_NOISE_FLOOR_2GHZ);
26455b9c547cSRui Paulo 	}
26465b9c547cSRui Paulo 
26475b9c547cSRui Paulo 	if (res->flags & WPA_SCAN_LEVEL_DBM) {
26485b9c547cSRui Paulo 		res->snr = res->level - res->noise;
26495b9c547cSRui Paulo 	} else {
26505b9c547cSRui Paulo 		/* Level is not in dBm, so we can't calculate
26515b9c547cSRui Paulo 		 * SNR. Just use raw level (units unknown). */
26525b9c547cSRui Paulo 		res->snr = res->level;
26535b9c547cSRui Paulo 	}
26545b9c547cSRui Paulo }
26555b9c547cSRui Paulo 
26565b9c547cSRui Paulo 
2657c1d255d3SCy Schubert /* Minimum SNR required to achieve a certain bitrate. */
2658c1d255d3SCy Schubert struct minsnr_bitrate_entry {
2659c1d255d3SCy Schubert 	int minsnr;
2660c1d255d3SCy Schubert 	unsigned int bitrate; /* in Mbps */
2661c1d255d3SCy Schubert };
2662c1d255d3SCy Schubert 
2663c1d255d3SCy Schubert /* VHT needs to be enabled in order to achieve MCS8 and MCS9 rates. */
2664c1d255d3SCy Schubert static const int vht_mcs = 8;
2665c1d255d3SCy Schubert 
2666c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht20_table[] = {
2667c1d255d3SCy Schubert 	{ 0, 0 },
2668c1d255d3SCy Schubert 	{ 2, 6500 },   /* HT20 MCS0 */
2669c1d255d3SCy Schubert 	{ 5, 13000 },  /* HT20 MCS1 */
2670c1d255d3SCy Schubert 	{ 9, 19500 },  /* HT20 MCS2 */
2671c1d255d3SCy Schubert 	{ 11, 26000 }, /* HT20 MCS3 */
2672c1d255d3SCy Schubert 	{ 15, 39000 }, /* HT20 MCS4 */
2673c1d255d3SCy Schubert 	{ 18, 52000 }, /* HT20 MCS5 */
2674c1d255d3SCy Schubert 	{ 20, 58500 }, /* HT20 MCS6 */
2675c1d255d3SCy Schubert 	{ 25, 65000 }, /* HT20 MCS7 */
2676c1d255d3SCy Schubert 	{ 29, 78000 }, /* VHT20 MCS8 */
2677c1d255d3SCy Schubert 	{ -1, 78000 }  /* SNR > 29 */
2678c1d255d3SCy Schubert };
2679c1d255d3SCy Schubert 
2680c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht40_table[] = {
2681c1d255d3SCy Schubert 	{ 0, 0 },
2682c1d255d3SCy Schubert 	{ 5, 13500 },   /* HT40 MCS0 */
2683c1d255d3SCy Schubert 	{ 8, 27000 },   /* HT40 MCS1 */
2684c1d255d3SCy Schubert 	{ 12, 40500 },  /* HT40 MCS2 */
2685c1d255d3SCy Schubert 	{ 14, 54000 },  /* HT40 MCS3 */
2686c1d255d3SCy Schubert 	{ 18, 81000 },  /* HT40 MCS4 */
2687c1d255d3SCy Schubert 	{ 21, 108000 }, /* HT40 MCS5 */
2688c1d255d3SCy Schubert 	{ 23, 121500 }, /* HT40 MCS6 */
2689c1d255d3SCy Schubert 	{ 28, 135000 }, /* HT40 MCS7 */
2690c1d255d3SCy Schubert 	{ 32, 162000 }, /* VHT40 MCS8 */
2691c1d255d3SCy Schubert 	{ 34, 180000 }, /* VHT40 MCS9 */
2692c1d255d3SCy Schubert 	{ -1, 180000 }  /* SNR > 34 */
2693c1d255d3SCy Schubert };
2694c1d255d3SCy Schubert 
2695c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht80_table[] = {
2696c1d255d3SCy Schubert 	{ 0, 0 },
2697c1d255d3SCy Schubert 	{ 8, 29300 },   /* VHT80 MCS0 */
2698c1d255d3SCy Schubert 	{ 11, 58500 },  /* VHT80 MCS1 */
2699c1d255d3SCy Schubert 	{ 15, 87800 },  /* VHT80 MCS2 */
2700c1d255d3SCy Schubert 	{ 17, 117000 }, /* VHT80 MCS3 */
2701c1d255d3SCy Schubert 	{ 21, 175500 }, /* VHT80 MCS4 */
2702c1d255d3SCy Schubert 	{ 24, 234000 }, /* VHT80 MCS5 */
2703c1d255d3SCy Schubert 	{ 26, 263300 }, /* VHT80 MCS6 */
2704c1d255d3SCy Schubert 	{ 31, 292500 }, /* VHT80 MCS7 */
2705c1d255d3SCy Schubert 	{ 35, 351000 }, /* VHT80 MCS8 */
2706c1d255d3SCy Schubert 	{ 37, 390000 }, /* VHT80 MCS9 */
2707c1d255d3SCy Schubert 	{ -1, 390000 }  /* SNR > 37 */
2708c1d255d3SCy Schubert };
2709c1d255d3SCy Schubert 
2710c1d255d3SCy Schubert 
2711c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht160_table[] = {
2712c1d255d3SCy Schubert 	{ 0, 0 },
2713c1d255d3SCy Schubert 	{ 11, 58500 },  /* VHT160 MCS0 */
2714c1d255d3SCy Schubert 	{ 14, 117000 }, /* VHT160 MCS1 */
2715c1d255d3SCy Schubert 	{ 18, 175500 }, /* VHT160 MCS2 */
2716c1d255d3SCy Schubert 	{ 20, 234000 }, /* VHT160 MCS3 */
2717c1d255d3SCy Schubert 	{ 24, 351000 }, /* VHT160 MCS4 */
2718c1d255d3SCy Schubert 	{ 27, 468000 }, /* VHT160 MCS5 */
2719c1d255d3SCy Schubert 	{ 29, 526500 }, /* VHT160 MCS6 */
2720c1d255d3SCy Schubert 	{ 34, 585000 }, /* VHT160 MCS7 */
2721c1d255d3SCy Schubert 	{ 38, 702000 }, /* VHT160 MCS8 */
2722c1d255d3SCy Schubert 	{ 40, 780000 }, /* VHT160 MCS9 */
2723c1d255d3SCy Schubert 	{ -1, 780000 }  /* SNR > 37 */
2724c1d255d3SCy Schubert };
2725c1d255d3SCy Schubert 
2726*a90b9d01SCy Schubert /* EHT needs to be enabled in order to achieve MCS12 and MCS13 rates. */
2727*a90b9d01SCy Schubert #define EHT_MCS 12
2728c1d255d3SCy Schubert 
2729c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he20_table[] = {
2730c1d255d3SCy Schubert 	{ 0, 0 },
2731c1d255d3SCy Schubert 	{ 2, 8600 },    /* HE20 MCS0 */
2732c1d255d3SCy Schubert 	{ 5, 17200 },   /* HE20 MCS1 */
2733c1d255d3SCy Schubert 	{ 9, 25800 },   /* HE20 MCS2 */
2734c1d255d3SCy Schubert 	{ 11, 34400 },  /* HE20 MCS3 */
2735c1d255d3SCy Schubert 	{ 15, 51600 },  /* HE20 MCS4 */
2736c1d255d3SCy Schubert 	{ 18, 68800 },  /* HE20 MCS5 */
2737c1d255d3SCy Schubert 	{ 20, 77400 },  /* HE20 MCS6 */
2738c1d255d3SCy Schubert 	{ 25, 86000 },  /* HE20 MCS7 */
2739c1d255d3SCy Schubert 	{ 29, 103200 }, /* HE20 MCS8 */
2740c1d255d3SCy Schubert 	{ 31, 114700 }, /* HE20 MCS9 */
2741c1d255d3SCy Schubert 	{ 34, 129000 }, /* HE20 MCS10 */
2742c1d255d3SCy Schubert 	{ 36, 143400 }, /* HE20 MCS11 */
2743*a90b9d01SCy Schubert 	{ 39, 154900 }, /* EHT20 MCS12 */
2744*a90b9d01SCy Schubert 	{ 42, 172100 }, /* EHT20 MCS13 */
2745*a90b9d01SCy Schubert 	{ -1, 172100 }  /* SNR > 42 */
2746c1d255d3SCy Schubert };
2747c1d255d3SCy Schubert 
2748c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he40_table[] = {
2749c1d255d3SCy Schubert 	{ 0, 0 },
2750c1d255d3SCy Schubert 	{ 5, 17200 },   /* HE40 MCS0 */
2751c1d255d3SCy Schubert 	{ 8, 34400 },   /* HE40 MCS1 */
2752c1d255d3SCy Schubert 	{ 12, 51600 },  /* HE40 MCS2 */
2753c1d255d3SCy Schubert 	{ 14, 68800 },  /* HE40 MCS3 */
2754c1d255d3SCy Schubert 	{ 18, 103200 }, /* HE40 MCS4 */
2755c1d255d3SCy Schubert 	{ 21, 137600 }, /* HE40 MCS5 */
2756c1d255d3SCy Schubert 	{ 23, 154900 }, /* HE40 MCS6 */
2757c1d255d3SCy Schubert 	{ 28, 172100 }, /* HE40 MCS7 */
2758c1d255d3SCy Schubert 	{ 32, 206500 }, /* HE40 MCS8 */
2759c1d255d3SCy Schubert 	{ 34, 229400 }, /* HE40 MCS9 */
2760c1d255d3SCy Schubert 	{ 37, 258100 }, /* HE40 MCS10 */
2761c1d255d3SCy Schubert 	{ 39, 286800 }, /* HE40 MCS11 */
2762*a90b9d01SCy Schubert 	{ 42, 309500 }, /* EHT40 MCS12 */
2763*a90b9d01SCy Schubert 	{ 45, 344100 }, /* EHT40 MCS13 */
2764*a90b9d01SCy Schubert 	{ -1, 344100 }  /* SNR > 45 */
2765c1d255d3SCy Schubert };
2766c1d255d3SCy Schubert 
2767c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he80_table[] = {
2768c1d255d3SCy Schubert 	{ 0, 0 },
2769c1d255d3SCy Schubert 	{ 8, 36000 },   /* HE80 MCS0 */
2770c1d255d3SCy Schubert 	{ 11, 72100 },  /* HE80 MCS1 */
2771c1d255d3SCy Schubert 	{ 15, 108100 }, /* HE80 MCS2 */
2772c1d255d3SCy Schubert 	{ 17, 144100 }, /* HE80 MCS3 */
2773c1d255d3SCy Schubert 	{ 21, 216200 }, /* HE80 MCS4 */
2774c1d255d3SCy Schubert 	{ 24, 288200 }, /* HE80 MCS5 */
2775c1d255d3SCy Schubert 	{ 26, 324300 }, /* HE80 MCS6 */
2776c1d255d3SCy Schubert 	{ 31, 360300 }, /* HE80 MCS7 */
2777c1d255d3SCy Schubert 	{ 35, 432400 }, /* HE80 MCS8 */
2778c1d255d3SCy Schubert 	{ 37, 480400 }, /* HE80 MCS9 */
2779c1d255d3SCy Schubert 	{ 40, 540400 }, /* HE80 MCS10 */
2780c1d255d3SCy Schubert 	{ 42, 600500 }, /* HE80 MCS11 */
2781*a90b9d01SCy Schubert 	{ 45, 648500 }, /* EHT80 MCS12 */
2782*a90b9d01SCy Schubert 	{ 48, 720600 }, /* EHT80 MCS13 */
2783*a90b9d01SCy Schubert 	{ -1, 720600 }  /* SNR > 48 */
2784c1d255d3SCy Schubert };
2785c1d255d3SCy Schubert 
2786c1d255d3SCy Schubert 
2787c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he160_table[] = {
2788c1d255d3SCy Schubert 	{ 0, 0 },
2789c1d255d3SCy Schubert 	{ 11, 72100 },   /* HE160 MCS0 */
2790c1d255d3SCy Schubert 	{ 14, 144100 },  /* HE160 MCS1 */
2791c1d255d3SCy Schubert 	{ 18, 216200 },  /* HE160 MCS2 */
2792c1d255d3SCy Schubert 	{ 20, 288200 },  /* HE160 MCS3 */
2793c1d255d3SCy Schubert 	{ 24, 432400 },  /* HE160 MCS4 */
2794c1d255d3SCy Schubert 	{ 27, 576500 },  /* HE160 MCS5 */
2795c1d255d3SCy Schubert 	{ 29, 648500 },  /* HE160 MCS6 */
2796c1d255d3SCy Schubert 	{ 34, 720600 },  /* HE160 MCS7 */
2797c1d255d3SCy Schubert 	{ 38, 864700 },  /* HE160 MCS8 */
2798c1d255d3SCy Schubert 	{ 40, 960800 },  /* HE160 MCS9 */
2799c1d255d3SCy Schubert 	{ 43, 1080900 }, /* HE160 MCS10 */
2800c1d255d3SCy Schubert 	{ 45, 1201000 }, /* HE160 MCS11 */
2801*a90b9d01SCy Schubert 	{ 48, 1297100 }, /* EHT160 MCS12 */
2802*a90b9d01SCy Schubert 	{ 51, 1441200 }, /* EHT160 MCS13 */
2803*a90b9d01SCy Schubert 	{ -1, 1441200 }  /* SNR > 51 */
2804c1d255d3SCy Schubert };
2805c1d255d3SCy Schubert 
2806*a90b9d01SCy Schubert /* See IEEE P802.11be/D2.0, Table 36-86: EHT-MCSs for 4x996-tone RU, NSS,u = 1
2807*a90b9d01SCy Schubert  */
2808*a90b9d01SCy Schubert static const struct minsnr_bitrate_entry eht320_table[] = {
2809*a90b9d01SCy Schubert 	{ 0, 0 },
2810*a90b9d01SCy Schubert 	{ 14, 144100 },   /* EHT320 MCS0 */
2811*a90b9d01SCy Schubert 	{ 17, 288200 },   /* EHT320 MCS1 */
2812*a90b9d01SCy Schubert 	{ 21, 432400 },   /* EHT320 MCS2 */
2813*a90b9d01SCy Schubert 	{ 23, 576500 },   /* EHT320 MCS3 */
2814*a90b9d01SCy Schubert 	{ 27, 864700 },   /* EHT320 MCS4 */
2815*a90b9d01SCy Schubert 	{ 30, 1152900 },  /* EHT320 MCS5 */
2816*a90b9d01SCy Schubert 	{ 32, 1297100 },  /* EHT320 MCS6 */
2817*a90b9d01SCy Schubert 	{ 37, 1441200 },  /* EHT320 MCS7 */
2818*a90b9d01SCy Schubert 	{ 41, 1729400 },  /* EHT320 MCS8 */
2819*a90b9d01SCy Schubert 	{ 43, 1921500 },  /* EHT320 MCS9 */
2820*a90b9d01SCy Schubert 	{ 46, 2161800 },  /* EHT320 MCS10 */
2821*a90b9d01SCy Schubert 	{ 48, 2401900 },  /* EHT320 MCS11 */
2822*a90b9d01SCy Schubert 	{ 51, 2594100 },  /* EHT320 MCS12 */
2823*a90b9d01SCy Schubert 	{ 54, 2882400 },  /* EHT320 MCS13 */
2824*a90b9d01SCy Schubert 	{ -1, 2882400 }   /* SNR > 54 */
2825*a90b9d01SCy Schubert };
2826c1d255d3SCy Schubert 
interpolate_rate(int snr,int snr0,int snr1,int rate0,int rate1)2827c1d255d3SCy Schubert static unsigned int interpolate_rate(int snr, int snr0, int snr1,
2828c1d255d3SCy Schubert 				     int rate0, int rate1)
28295b9c547cSRui Paulo {
2830c1d255d3SCy Schubert 	return rate0 + (snr - snr0) * (rate1 - rate0) / (snr1 - snr0);
28315b9c547cSRui Paulo }
28325b9c547cSRui Paulo 
28335b9c547cSRui Paulo 
max_rate(const struct minsnr_bitrate_entry table[],int snr,bool vht)2834c1d255d3SCy Schubert static unsigned int max_rate(const struct minsnr_bitrate_entry table[],
2835c1d255d3SCy Schubert 			     int snr, bool vht)
28365b9c547cSRui Paulo {
2837c1d255d3SCy Schubert 	const struct minsnr_bitrate_entry *prev, *entry = table;
2838c1d255d3SCy Schubert 
2839c1d255d3SCy Schubert 	while ((entry->minsnr != -1) &&
2840c1d255d3SCy Schubert 	       (snr >= entry->minsnr) &&
2841c1d255d3SCy Schubert 	       (vht || entry - table <= vht_mcs))
2842c1d255d3SCy Schubert 		entry++;
2843c1d255d3SCy Schubert 	if (entry == table)
2844c1d255d3SCy Schubert 		return entry->bitrate;
2845c1d255d3SCy Schubert 	prev = entry - 1;
2846c1d255d3SCy Schubert 	if (entry->minsnr == -1 || (!vht && entry - table > vht_mcs))
2847c1d255d3SCy Schubert 		return prev->bitrate;
2848c1d255d3SCy Schubert 	return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate,
2849c1d255d3SCy Schubert 				entry->bitrate);
2850c1d255d3SCy Schubert }
2851c1d255d3SCy Schubert 
2852c1d255d3SCy Schubert 
max_ht20_rate(int snr,bool vht)2853c1d255d3SCy Schubert static unsigned int max_ht20_rate(int snr, bool vht)
2854c1d255d3SCy Schubert {
2855c1d255d3SCy Schubert 	return max_rate(vht20_table, snr, vht);
2856c1d255d3SCy Schubert }
2857c1d255d3SCy Schubert 
2858c1d255d3SCy Schubert 
max_ht40_rate(int snr,bool vht)2859c1d255d3SCy Schubert static unsigned int max_ht40_rate(int snr, bool vht)
2860c1d255d3SCy Schubert {
2861c1d255d3SCy Schubert 	return max_rate(vht40_table, snr, vht);
28625b9c547cSRui Paulo }
28635b9c547cSRui Paulo 
28645b9c547cSRui Paulo 
max_vht80_rate(int snr)28655b9c547cSRui Paulo static unsigned int max_vht80_rate(int snr)
28665b9c547cSRui Paulo {
2867c1d255d3SCy Schubert 	return max_rate(vht80_table, snr, 1);
28685b9c547cSRui Paulo }
28695b9c547cSRui Paulo 
28705b9c547cSRui Paulo 
max_vht160_rate(int snr)2871c1d255d3SCy Schubert static unsigned int max_vht160_rate(int snr)
28725b9c547cSRui Paulo {
2873c1d255d3SCy Schubert 	return max_rate(vht160_table, snr, 1);
2874c1d255d3SCy Schubert }
2875c1d255d3SCy Schubert 
2876c1d255d3SCy Schubert 
max_he_eht_rate(const struct minsnr_bitrate_entry table[],int snr,bool eht)2877*a90b9d01SCy Schubert static unsigned int max_he_eht_rate(const struct minsnr_bitrate_entry table[],
2878*a90b9d01SCy Schubert 				    int snr, bool eht)
2879c1d255d3SCy Schubert {
2880c1d255d3SCy Schubert 	const struct minsnr_bitrate_entry *prev, *entry = table;
2881c1d255d3SCy Schubert 
2882*a90b9d01SCy Schubert 	while (entry->minsnr != -1 && snr >= entry->minsnr &&
2883*a90b9d01SCy Schubert 	       (eht || entry - table <= EHT_MCS))
2884c1d255d3SCy Schubert 		entry++;
2885c1d255d3SCy Schubert 	if (entry == table)
2886c1d255d3SCy Schubert 		return 0;
2887c1d255d3SCy Schubert 	prev = entry - 1;
2888*a90b9d01SCy Schubert 	if (entry->minsnr == -1 || (!eht && entry - table > EHT_MCS))
2889c1d255d3SCy Schubert 		return prev->bitrate;
2890c1d255d3SCy Schubert 	return interpolate_rate(snr, prev->minsnr, entry->minsnr,
2891c1d255d3SCy Schubert 				prev->bitrate, entry->bitrate);
2892c1d255d3SCy Schubert }
2893c1d255d3SCy Schubert 
2894c1d255d3SCy Schubert 
wpas_get_est_tpt(const struct wpa_supplicant * wpa_s,const u8 * ies,size_t ies_len,int rate,int snr,int freq,enum chan_width * max_cw)2895c1d255d3SCy Schubert unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
2896c1d255d3SCy Schubert 			      const u8 *ies, size_t ies_len, int rate,
2897*a90b9d01SCy Schubert 			      int snr, int freq, enum chan_width *max_cw)
2898c1d255d3SCy Schubert {
2899c1d255d3SCy Schubert 	struct hostapd_hw_modes *hw_mode;
29005b9c547cSRui Paulo 	unsigned int est, tmp;
2901c1d255d3SCy Schubert 	const u8 *ie;
2902*a90b9d01SCy Schubert 	/*
2903*a90b9d01SCy Schubert 	 * No need to apply a bump to the noise here because the
2904*a90b9d01SCy Schubert 	 * minsnr_bitrate_entry tables are based on MCS tables where this has
2905*a90b9d01SCy Schubert 	 * been taken into account.
2906*a90b9d01SCy Schubert 	 */
2907*a90b9d01SCy Schubert 	int adjusted_snr;
2908*a90b9d01SCy Schubert 	bool ht40 = false, vht80 = false, vht160 = false;
29095b9c547cSRui Paulo 
29105b9c547cSRui Paulo 	/* Limit based on estimated SNR */
29115b9c547cSRui Paulo 	if (rate > 1 * 2 && snr < 1)
29125b9c547cSRui Paulo 		rate = 1 * 2;
29135b9c547cSRui Paulo 	else if (rate > 2 * 2 && snr < 4)
29145b9c547cSRui Paulo 		rate = 2 * 2;
29155b9c547cSRui Paulo 	else if (rate > 6 * 2 && snr < 5)
29165b9c547cSRui Paulo 		rate = 6 * 2;
29175b9c547cSRui Paulo 	else if (rate > 9 * 2 && snr < 6)
29185b9c547cSRui Paulo 		rate = 9 * 2;
29195b9c547cSRui Paulo 	else if (rate > 12 * 2 && snr < 7)
29205b9c547cSRui Paulo 		rate = 12 * 2;
2921c1d255d3SCy Schubert 	else if (rate > 12 * 2 && snr < 8)
2922c1d255d3SCy Schubert 		rate = 14 * 2;
2923c1d255d3SCy Schubert 	else if (rate > 12 * 2 && snr < 9)
2924c1d255d3SCy Schubert 		rate = 16 * 2;
29255b9c547cSRui Paulo 	else if (rate > 18 * 2 && snr < 10)
29265b9c547cSRui Paulo 		rate = 18 * 2;
29275b9c547cSRui Paulo 	else if (rate > 24 * 2 && snr < 11)
29285b9c547cSRui Paulo 		rate = 24 * 2;
2929c1d255d3SCy Schubert 	else if (rate > 24 * 2 && snr < 12)
2930c1d255d3SCy Schubert 		rate = 27 * 2;
2931c1d255d3SCy Schubert 	else if (rate > 24 * 2 && snr < 13)
2932c1d255d3SCy Schubert 		rate = 30 * 2;
2933c1d255d3SCy Schubert 	else if (rate > 24 * 2 && snr < 14)
2934c1d255d3SCy Schubert 		rate = 33 * 2;
29355b9c547cSRui Paulo 	else if (rate > 36 * 2 && snr < 15)
29365b9c547cSRui Paulo 		rate = 36 * 2;
2937c1d255d3SCy Schubert 	else if (rate > 36 * 2 && snr < 16)
2938c1d255d3SCy Schubert 		rate = 39 * 2;
2939c1d255d3SCy Schubert 	else if (rate > 36 * 2 && snr < 17)
2940c1d255d3SCy Schubert 		rate = 42 * 2;
2941c1d255d3SCy Schubert 	else if (rate > 36 * 2 && snr < 18)
2942c1d255d3SCy Schubert 		rate = 45 * 2;
29435b9c547cSRui Paulo 	else if (rate > 48 * 2 && snr < 19)
29445b9c547cSRui Paulo 		rate = 48 * 2;
2945c1d255d3SCy Schubert 	else if (rate > 48 * 2 && snr < 20)
2946c1d255d3SCy Schubert 		rate = 51 * 2;
29475b9c547cSRui Paulo 	else if (rate > 54 * 2 && snr < 21)
29485b9c547cSRui Paulo 		rate = 54 * 2;
29495b9c547cSRui Paulo 	est = rate * 500;
29505b9c547cSRui Paulo 
2951c1d255d3SCy Schubert 	hw_mode = get_mode_with_freq(wpa_s->hw.modes, wpa_s->hw.num_modes,
2952c1d255d3SCy Schubert 				     freq);
2953c1d255d3SCy Schubert 
2954c1d255d3SCy Schubert 	if (hw_mode && hw_mode->ht_capab) {
2955c1d255d3SCy Schubert 		ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
29565b9c547cSRui Paulo 		if (ie) {
2957*a90b9d01SCy Schubert 			*max_cw = CHAN_WIDTH_20;
2958c1d255d3SCy Schubert 			tmp = max_ht20_rate(snr, false);
29595b9c547cSRui Paulo 			if (tmp > est)
29605b9c547cSRui Paulo 				est = tmp;
29615b9c547cSRui Paulo 		}
29625b9c547cSRui Paulo 	}
29635b9c547cSRui Paulo 
2964*a90b9d01SCy Schubert 	ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
2965*a90b9d01SCy Schubert 	if (ie && ie[1] >= 2 &&
2966*a90b9d01SCy Schubert 	    (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK))
2967*a90b9d01SCy Schubert 		ht40 = true;
2968*a90b9d01SCy Schubert 
2969c1d255d3SCy Schubert 	if (hw_mode &&
2970c1d255d3SCy Schubert 	    (hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
2971*a90b9d01SCy Schubert 		if (ht40) {
2972*a90b9d01SCy Schubert 			*max_cw = CHAN_WIDTH_40;
2973*a90b9d01SCy Schubert 			adjusted_snr = snr +
2974*a90b9d01SCy Schubert 				wpas_channel_width_rssi_bump(ies, ies_len,
2975*a90b9d01SCy Schubert 							     CHAN_WIDTH_40);
2976*a90b9d01SCy Schubert 			tmp = max_ht40_rate(adjusted_snr, false);
29775b9c547cSRui Paulo 			if (tmp > est)
29785b9c547cSRui Paulo 				est = tmp;
29795b9c547cSRui Paulo 		}
29805b9c547cSRui Paulo 	}
29815b9c547cSRui Paulo 
2982*a90b9d01SCy Schubert 	/* Determine VHT BSS bandwidth based on IEEE Std 802.11-2020,
2983*a90b9d01SCy Schubert 	 * Table 11-23 (VHT BSS bandwidth) */
2984c1d255d3SCy Schubert 	ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
2985c1d255d3SCy Schubert 	if (ie && ie[1] >= 3) {
2986c1d255d3SCy Schubert 		u8 cw = ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK;
2987c1d255d3SCy Schubert 		u8 seg0 = ie[3];
2988c1d255d3SCy Schubert 		u8 seg1 = ie[4];
2989c1d255d3SCy Schubert 
2990c1d255d3SCy Schubert 		if (cw)
2991c1d255d3SCy Schubert 			vht80 = true;
2992c1d255d3SCy Schubert 		if (cw == 2 ||
2993*a90b9d01SCy Schubert 		    (cw == 3 && (seg1 > 0 && abs(seg1 - seg0) == 16)))
2994c1d255d3SCy Schubert 			vht160 = true;
2995c1d255d3SCy Schubert 		if (cw == 1 &&
2996c1d255d3SCy Schubert 		    ((seg1 > 0 && abs(seg1 - seg0) == 8) ||
2997c1d255d3SCy Schubert 		     (seg1 > 0 && abs(seg1 - seg0) == 16)))
2998c1d255d3SCy Schubert 			vht160 = true;
2999c1d255d3SCy Schubert 	}
3000c1d255d3SCy Schubert 
3001*a90b9d01SCy Schubert 	if (hw_mode && hw_mode->vht_capab) {
3002*a90b9d01SCy Schubert 		/* Use +1 to assume VHT is always faster than HT */
3003*a90b9d01SCy Schubert 		ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
3004*a90b9d01SCy Schubert 		if (ie) {
3005*a90b9d01SCy Schubert 			if (*max_cw == CHAN_WIDTH_UNKNOWN)
3006*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_20;
3007*a90b9d01SCy Schubert 			tmp = max_ht20_rate(snr, true) + 1;
3008*a90b9d01SCy Schubert 			if (tmp > est)
3009*a90b9d01SCy Schubert 				est = tmp;
3010*a90b9d01SCy Schubert 
3011*a90b9d01SCy Schubert 			if (ht40) {
3012*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_40;
3013*a90b9d01SCy Schubert 				adjusted_snr = snr +
3014*a90b9d01SCy Schubert 					wpas_channel_width_rssi_bump(
3015*a90b9d01SCy Schubert 						ies, ies_len, CHAN_WIDTH_40);
3016*a90b9d01SCy Schubert 				tmp = max_ht40_rate(adjusted_snr, true) + 1;
3017*a90b9d01SCy Schubert 				if (tmp > est)
3018*a90b9d01SCy Schubert 					est = tmp;
3019*a90b9d01SCy Schubert 			}
3020*a90b9d01SCy Schubert 
3021c1d255d3SCy Schubert 			if (vht80) {
3022*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_80;
3023*a90b9d01SCy Schubert 				adjusted_snr = snr +
3024*a90b9d01SCy Schubert 					wpas_channel_width_rssi_bump(
3025*a90b9d01SCy Schubert 						ies, ies_len, CHAN_WIDTH_80);
3026*a90b9d01SCy Schubert 				tmp = max_vht80_rate(adjusted_snr) + 1;
30275b9c547cSRui Paulo 				if (tmp > est)
30285b9c547cSRui Paulo 					est = tmp;
30295b9c547cSRui Paulo 			}
3030c1d255d3SCy Schubert 
3031c1d255d3SCy Schubert 			if (vht160 &&
3032c1d255d3SCy Schubert 			    (hw_mode->vht_capab &
3033c1d255d3SCy Schubert 			     (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
3034c1d255d3SCy Schubert 			      VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
3035*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_160;
3036*a90b9d01SCy Schubert 				adjusted_snr = snr +
3037*a90b9d01SCy Schubert 					wpas_channel_width_rssi_bump(
3038*a90b9d01SCy Schubert 						ies, ies_len, CHAN_WIDTH_160);
3039*a90b9d01SCy Schubert 				tmp = max_vht160_rate(adjusted_snr) + 1;
3040c1d255d3SCy Schubert 				if (tmp > est)
3041c1d255d3SCy Schubert 					est = tmp;
30425b9c547cSRui Paulo 			}
30435b9c547cSRui Paulo 		}
3044c1d255d3SCy Schubert 	}
3045c1d255d3SCy Schubert 
3046c1d255d3SCy Schubert 	if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) {
3047c1d255d3SCy Schubert 		/* Use +2 to assume HE is always faster than HT/VHT */
3048c1d255d3SCy Schubert 		struct ieee80211_he_capabilities *he;
3049*a90b9d01SCy Schubert 		struct ieee80211_eht_capabilities *eht;
3050c1d255d3SCy Schubert 		struct he_capabilities *own_he;
3051*a90b9d01SCy Schubert 		u8 cw, boost = 2;
3052*a90b9d01SCy Schubert 		const u8 *eht_ie;
3053*a90b9d01SCy Schubert 		bool is_eht = false;
3054c1d255d3SCy Schubert 
3055c1d255d3SCy Schubert 		ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES);
3056c1d255d3SCy Schubert 		if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN))
3057c1d255d3SCy Schubert 			return est;
3058c1d255d3SCy Schubert 		he = (struct ieee80211_he_capabilities *) &ie[3];
3059c1d255d3SCy Schubert 		own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA];
3060c1d255d3SCy Schubert 
3061*a90b9d01SCy Schubert 		/* Use +3 to assume EHT is always faster than HE */
3062*a90b9d01SCy Schubert 		if (hw_mode->eht_capab[IEEE80211_MODE_INFRA].eht_supported) {
3063*a90b9d01SCy Schubert 			eht_ie = get_ie_ext(ies, ies_len,
3064*a90b9d01SCy Schubert 					    WLAN_EID_EXT_EHT_CAPABILITIES);
3065*a90b9d01SCy Schubert 			if (eht_ie &&
3066*a90b9d01SCy Schubert 			    (eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN)) {
3067*a90b9d01SCy Schubert 				is_eht = true;
3068*a90b9d01SCy Schubert 				boost = 3;
3069*a90b9d01SCy Schubert 			}
3070*a90b9d01SCy Schubert 		}
3071*a90b9d01SCy Schubert 
3072*a90b9d01SCy Schubert 		if (*max_cw == CHAN_WIDTH_UNKNOWN)
3073*a90b9d01SCy Schubert 			*max_cw = CHAN_WIDTH_20;
3074*a90b9d01SCy Schubert 		tmp = max_he_eht_rate(he20_table, snr, is_eht) + boost;
3075c1d255d3SCy Schubert 		if (tmp > est)
3076c1d255d3SCy Schubert 			est = tmp;
3077c1d255d3SCy Schubert 
3078c1d255d3SCy Schubert 		cw = he->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
3079c1d255d3SCy Schubert 			own_he->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
3080*a90b9d01SCy Schubert 		if ((cw &
3081*a90b9d01SCy Schubert 		     (IS_2P4GHZ(freq) ?
3082*a90b9d01SCy Schubert 		      HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
3083*a90b9d01SCy Schubert 		      HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) && ht40) {
3084*a90b9d01SCy Schubert 			if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3085*a90b9d01SCy Schubert 			    *max_cw < CHAN_WIDTH_40)
3086*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_40;
3087*a90b9d01SCy Schubert 			adjusted_snr = snr + wpas_channel_width_rssi_bump(
3088*a90b9d01SCy Schubert 				ies, ies_len, CHAN_WIDTH_40);
3089*a90b9d01SCy Schubert 			tmp = max_he_eht_rate(he40_table, adjusted_snr,
3090*a90b9d01SCy Schubert 					      is_eht) + boost;
3091c1d255d3SCy Schubert 			if (tmp > est)
3092c1d255d3SCy Schubert 				est = tmp;
3093c1d255d3SCy Schubert 		}
3094c1d255d3SCy Schubert 
3095c1d255d3SCy Schubert 		if (!IS_2P4GHZ(freq) &&
3096*a90b9d01SCy Schubert 		    (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) &&
3097*a90b9d01SCy Schubert 		    (!IS_5GHZ(freq) || vht80)) {
3098*a90b9d01SCy Schubert 			if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3099*a90b9d01SCy Schubert 			    *max_cw < CHAN_WIDTH_80)
3100*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_80;
3101*a90b9d01SCy Schubert 			adjusted_snr = snr + wpas_channel_width_rssi_bump(
3102*a90b9d01SCy Schubert 				ies, ies_len, CHAN_WIDTH_80);
3103*a90b9d01SCy Schubert 			tmp = max_he_eht_rate(he80_table, adjusted_snr,
3104*a90b9d01SCy Schubert 					      is_eht) + boost;
3105c1d255d3SCy Schubert 			if (tmp > est)
3106c1d255d3SCy Schubert 				est = tmp;
3107c1d255d3SCy Schubert 		}
3108c1d255d3SCy Schubert 
3109c1d255d3SCy Schubert 		if (!IS_2P4GHZ(freq) &&
3110c1d255d3SCy Schubert 		    (cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
3111*a90b9d01SCy Schubert 			   HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) &&
3112*a90b9d01SCy Schubert 		    (!IS_5GHZ(freq) || vht160)) {
3113*a90b9d01SCy Schubert 			if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3114*a90b9d01SCy Schubert 			    *max_cw < CHAN_WIDTH_160)
3115*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_160;
3116*a90b9d01SCy Schubert 			adjusted_snr = snr + wpas_channel_width_rssi_bump(
3117*a90b9d01SCy Schubert 				ies, ies_len, CHAN_WIDTH_160);
3118*a90b9d01SCy Schubert 			tmp = max_he_eht_rate(he160_table, adjusted_snr,
3119*a90b9d01SCy Schubert 					      is_eht) + boost;
3120*a90b9d01SCy Schubert 			if (tmp > est)
3121*a90b9d01SCy Schubert 				est = tmp;
3122*a90b9d01SCy Schubert 		}
3123*a90b9d01SCy Schubert 
3124*a90b9d01SCy Schubert 		if (!is_eht)
3125*a90b9d01SCy Schubert 			return est;
3126*a90b9d01SCy Schubert 
3127*a90b9d01SCy Schubert 		eht = (struct ieee80211_eht_capabilities *) &eht_ie[3];
3128*a90b9d01SCy Schubert 
3129*a90b9d01SCy Schubert 		if (is_6ghz_freq(freq) &&
3130*a90b9d01SCy Schubert 		    (eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
3131*a90b9d01SCy Schubert 		     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
3132*a90b9d01SCy Schubert 			if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3133*a90b9d01SCy Schubert 			    *max_cw < CHAN_WIDTH_320)
3134*a90b9d01SCy Schubert 				*max_cw = CHAN_WIDTH_320;
3135*a90b9d01SCy Schubert 			adjusted_snr = snr + wpas_channel_width_rssi_bump(
3136*a90b9d01SCy Schubert 				ies, ies_len, CHAN_WIDTH_320);
3137*a90b9d01SCy Schubert 			tmp = max_he_eht_rate(eht320_table, adjusted_snr, true);
3138c1d255d3SCy Schubert 			if (tmp > est)
3139c1d255d3SCy Schubert 				est = tmp;
3140c1d255d3SCy Schubert 		}
3141c1d255d3SCy Schubert 	}
3142c1d255d3SCy Schubert 
3143c1d255d3SCy Schubert 	return est;
3144c1d255d3SCy Schubert }
3145c1d255d3SCy Schubert 
3146c1d255d3SCy Schubert 
scan_est_throughput(struct wpa_supplicant * wpa_s,struct wpa_scan_res * res)3147c1d255d3SCy Schubert void scan_est_throughput(struct wpa_supplicant *wpa_s,
3148c1d255d3SCy Schubert 			 struct wpa_scan_res *res)
3149c1d255d3SCy Schubert {
3150c1d255d3SCy Schubert 	int rate; /* max legacy rate in 500 kb/s units */
3151c1d255d3SCy Schubert 	int snr = res->snr;
3152c1d255d3SCy Schubert 	const u8 *ies = (const void *) (res + 1);
3153c1d255d3SCy Schubert 	size_t ie_len = res->ie_len;
3154c1d255d3SCy Schubert 
3155c1d255d3SCy Schubert 	if (res->est_throughput)
3156c1d255d3SCy Schubert 		return;
3157c1d255d3SCy Schubert 
3158c1d255d3SCy Schubert 	/* Get maximum legacy rate */
3159c1d255d3SCy Schubert 	rate = wpa_scan_get_max_rate(res);
3160c1d255d3SCy Schubert 
3161c1d255d3SCy Schubert 	if (!ie_len)
3162c1d255d3SCy Schubert 		ie_len = res->beacon_ie_len;
3163*a90b9d01SCy Schubert 	res->est_throughput = wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr,
3164*a90b9d01SCy Schubert 					       res->freq, &res->max_cw);
31655b9c547cSRui Paulo 
31665b9c547cSRui Paulo 	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
31675b9c547cSRui Paulo }
31685b9c547cSRui Paulo 
31695b9c547cSRui Paulo 
3170e28a4053SRui Paulo /**
3171e28a4053SRui Paulo  * wpa_supplicant_get_scan_results - Get scan results
3172e28a4053SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
3173e28a4053SRui Paulo  * @info: Information about what was scanned or %NULL if not available
3174e28a4053SRui Paulo  * @new_scan: Whether a new scan was performed
3175*a90b9d01SCy Schubert  * @bssid: Return BSS entries only for a single BSSID, %NULL for all
3176e28a4053SRui Paulo  * Returns: Scan results, %NULL on failure
3177e28a4053SRui Paulo  *
3178e28a4053SRui Paulo  * This function request the current scan results from the driver and updates
3179e28a4053SRui Paulo  * the local BSS list wpa_s->bss. The caller is responsible for freeing the
3180e28a4053SRui Paulo  * results with wpa_scan_results_free().
3181e28a4053SRui Paulo  */
3182e28a4053SRui Paulo struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant * wpa_s,struct scan_info * info,int new_scan,const u8 * bssid)3183e28a4053SRui Paulo wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
3184*a90b9d01SCy Schubert 				struct scan_info *info, int new_scan,
3185*a90b9d01SCy Schubert 				const u8 *bssid)
3186e28a4053SRui Paulo {
3187e28a4053SRui Paulo 	struct wpa_scan_results *scan_res;
3188e28a4053SRui Paulo 	size_t i;
3189f05cddf9SRui Paulo 	int (*compar)(const void *, const void *) = wpa_scan_result_compar;
3190e28a4053SRui Paulo 
3191*a90b9d01SCy Schubert 	scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
3192e28a4053SRui Paulo 	if (scan_res == NULL) {
3193f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
3194e28a4053SRui Paulo 		return NULL;
3195e28a4053SRui Paulo 	}
31965b9c547cSRui Paulo 	if (scan_res->fetch_time.sec == 0) {
31975b9c547cSRui Paulo 		/*
31985b9c547cSRui Paulo 		 * Make sure we have a valid timestamp if the driver wrapper
31995b9c547cSRui Paulo 		 * does not set this.
32005b9c547cSRui Paulo 		 */
32015b9c547cSRui Paulo 		os_get_reltime(&scan_res->fetch_time);
32025b9c547cSRui Paulo 	}
3203f05cddf9SRui Paulo 	filter_scan_res(wpa_s, scan_res);
3204f05cddf9SRui Paulo 
32055b9c547cSRui Paulo 	for (i = 0; i < scan_res->num; i++) {
32065b9c547cSRui Paulo 		struct wpa_scan_res *scan_res_item = scan_res->res[i];
32075b9c547cSRui Paulo 
32085b9c547cSRui Paulo 		scan_snr(scan_res_item);
32095b9c547cSRui Paulo 		scan_est_throughput(wpa_s, scan_res_item);
32105b9c547cSRui Paulo 	}
32115b9c547cSRui Paulo 
3212f05cddf9SRui Paulo #ifdef CONFIG_WPS
32135b9c547cSRui Paulo 	if (wpas_wps_searching(wpa_s)) {
3214f05cddf9SRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
3215f05cddf9SRui Paulo 			"provisioning rules");
3216f05cddf9SRui Paulo 		compar = wpa_scan_result_wps_compar;
3217f05cddf9SRui Paulo 	}
3218f05cddf9SRui Paulo #endif /* CONFIG_WPS */
3219e28a4053SRui Paulo 
322085732ac8SCy Schubert 	if (scan_res->res) {
322185732ac8SCy Schubert 		qsort(scan_res->res, scan_res->num,
322285732ac8SCy Schubert 		      sizeof(struct wpa_scan_res *), compar);
322385732ac8SCy Schubert 	}
3224f05cddf9SRui Paulo 	dump_scan_res(scan_res);
3225e28a4053SRui Paulo 
322685732ac8SCy Schubert 	if (wpa_s->ignore_post_flush_scan_res) {
322785732ac8SCy Schubert 		/* FLUSH command aborted an ongoing scan and these are the
322885732ac8SCy Schubert 		 * results from the aborted scan. Do not process the results to
322985732ac8SCy Schubert 		 * maintain flushed state. */
323085732ac8SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG,
323185732ac8SCy Schubert 			"Do not update BSS table based on pending post-FLUSH scan results");
323285732ac8SCy Schubert 		wpa_s->ignore_post_flush_scan_res = 0;
323385732ac8SCy Schubert 		return scan_res;
323485732ac8SCy Schubert 	}
323585732ac8SCy Schubert 
3236e28a4053SRui Paulo 	wpa_bss_update_start(wpa_s);
3237e28a4053SRui Paulo 	for (i = 0; i < scan_res->num; i++)
32385b9c547cSRui Paulo 		wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
32395b9c547cSRui Paulo 					&scan_res->fetch_time);
3240e28a4053SRui Paulo 	wpa_bss_update_end(wpa_s, info, new_scan);
3241e28a4053SRui Paulo 
3242e28a4053SRui Paulo 	return scan_res;
3243e28a4053SRui Paulo }
3244e28a4053SRui Paulo 
3245e28a4053SRui Paulo 
3246f05cddf9SRui Paulo /**
3247f05cddf9SRui Paulo  * wpa_supplicant_update_scan_results - Update scan results from the driver
3248f05cddf9SRui Paulo  * @wpa_s: Pointer to wpa_supplicant data
3249*a90b9d01SCy Schubert  * @bssid: Update BSS entries only for a single BSSID, %NULL for all
3250f05cddf9SRui Paulo  * Returns: 0 on success, -1 on failure
3251f05cddf9SRui Paulo  *
3252f05cddf9SRui Paulo  * This function updates the BSS table within wpa_supplicant based on the
3253f05cddf9SRui Paulo  * currently available scan results from the driver without requesting a new
3254f05cddf9SRui Paulo  * scan. This is used in cases where the driver indicates an association
3255f05cddf9SRui Paulo  * (including roaming within ESS) and wpa_supplicant does not yet have the
3256f05cddf9SRui Paulo  * needed information to complete the connection (e.g., to perform validation
3257f05cddf9SRui Paulo  * steps in 4-way handshake).
3258f05cddf9SRui Paulo  */
wpa_supplicant_update_scan_results(struct wpa_supplicant * wpa_s,const u8 * bssid)3259*a90b9d01SCy Schubert int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
3260*a90b9d01SCy Schubert 				       const u8 *bssid)
3261e28a4053SRui Paulo {
3262e28a4053SRui Paulo 	struct wpa_scan_results *scan_res;
3263*a90b9d01SCy Schubert 	scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0, bssid);
3264e28a4053SRui Paulo 	if (scan_res == NULL)
3265e28a4053SRui Paulo 		return -1;
3266e28a4053SRui Paulo 	wpa_scan_results_free(scan_res);
3267e28a4053SRui Paulo 
3268e28a4053SRui Paulo 	return 0;
3269e28a4053SRui Paulo }
32705b9c547cSRui Paulo 
32715b9c547cSRui Paulo 
32725b9c547cSRui Paulo /**
32735b9c547cSRui Paulo  * scan_only_handler - Reports scan results
32745b9c547cSRui Paulo  */
scan_only_handler(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res)32755b9c547cSRui Paulo void scan_only_handler(struct wpa_supplicant *wpa_s,
32765b9c547cSRui Paulo 		       struct wpa_scan_results *scan_res)
32775b9c547cSRui Paulo {
32785b9c547cSRui Paulo 	wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
32795b9c547cSRui Paulo 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
32805b9c547cSRui Paulo 	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
32815b9c547cSRui Paulo 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
32825b9c547cSRui Paulo 			     wpa_s->manual_scan_id);
32835b9c547cSRui Paulo 		wpa_s->manual_scan_use_id = 0;
32845b9c547cSRui Paulo 	} else {
32855b9c547cSRui Paulo 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
32865b9c547cSRui Paulo 	}
32875b9c547cSRui Paulo 	wpas_notify_scan_results(wpa_s);
32885b9c547cSRui Paulo 	wpas_notify_scan_done(wpa_s, 1);
32895b9c547cSRui Paulo 	if (wpa_s->scan_work) {
32905b9c547cSRui Paulo 		struct wpa_radio_work *work = wpa_s->scan_work;
32915b9c547cSRui Paulo 		wpa_s->scan_work = NULL;
32925b9c547cSRui Paulo 		radio_work_done(work);
32935b9c547cSRui Paulo 	}
3294780fb4a2SCy Schubert 
3295780fb4a2SCy Schubert 	if (wpa_s->wpa_state == WPA_SCANNING)
3296780fb4a2SCy Schubert 		wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
32975b9c547cSRui Paulo }
32985b9c547cSRui Paulo 
32995b9c547cSRui Paulo 
wpas_scan_scheduled(struct wpa_supplicant * wpa_s)33005b9c547cSRui Paulo int wpas_scan_scheduled(struct wpa_supplicant *wpa_s)
33015b9c547cSRui Paulo {
33025b9c547cSRui Paulo 	return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
33035b9c547cSRui Paulo }
33045b9c547cSRui Paulo 
33055b9c547cSRui Paulo 
33065b9c547cSRui Paulo struct wpa_driver_scan_params *
wpa_scan_clone_params(const struct wpa_driver_scan_params * src)33075b9c547cSRui Paulo wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
33085b9c547cSRui Paulo {
33095b9c547cSRui Paulo 	struct wpa_driver_scan_params *params;
33105b9c547cSRui Paulo 	size_t i;
33115b9c547cSRui Paulo 	u8 *n;
33125b9c547cSRui Paulo 
33135b9c547cSRui Paulo 	params = os_zalloc(sizeof(*params));
33145b9c547cSRui Paulo 	if (params == NULL)
33155b9c547cSRui Paulo 		return NULL;
33165b9c547cSRui Paulo 
33175b9c547cSRui Paulo 	for (i = 0; i < src->num_ssids; i++) {
33185b9c547cSRui Paulo 		if (src->ssids[i].ssid) {
331985732ac8SCy Schubert 			n = os_memdup(src->ssids[i].ssid,
332085732ac8SCy Schubert 				      src->ssids[i].ssid_len);
33215b9c547cSRui Paulo 			if (n == NULL)
33225b9c547cSRui Paulo 				goto failed;
33235b9c547cSRui Paulo 			params->ssids[i].ssid = n;
33245b9c547cSRui Paulo 			params->ssids[i].ssid_len = src->ssids[i].ssid_len;
33255b9c547cSRui Paulo 		}
33265b9c547cSRui Paulo 	}
33275b9c547cSRui Paulo 	params->num_ssids = src->num_ssids;
33285b9c547cSRui Paulo 
33295b9c547cSRui Paulo 	if (src->extra_ies) {
333085732ac8SCy Schubert 		n = os_memdup(src->extra_ies, src->extra_ies_len);
33315b9c547cSRui Paulo 		if (n == NULL)
33325b9c547cSRui Paulo 			goto failed;
33335b9c547cSRui Paulo 		params->extra_ies = n;
33345b9c547cSRui Paulo 		params->extra_ies_len = src->extra_ies_len;
33355b9c547cSRui Paulo 	}
33365b9c547cSRui Paulo 
33375b9c547cSRui Paulo 	if (src->freqs) {
33385b9c547cSRui Paulo 		int len = int_array_len(src->freqs);
333985732ac8SCy Schubert 		params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int));
33405b9c547cSRui Paulo 		if (params->freqs == NULL)
33415b9c547cSRui Paulo 			goto failed;
33425b9c547cSRui Paulo 	}
33435b9c547cSRui Paulo 
33445b9c547cSRui Paulo 	if (src->filter_ssids) {
334585732ac8SCy Schubert 		params->filter_ssids = os_memdup(src->filter_ssids,
334685732ac8SCy Schubert 						 sizeof(*params->filter_ssids) *
33475b9c547cSRui Paulo 						 src->num_filter_ssids);
33485b9c547cSRui Paulo 		if (params->filter_ssids == NULL)
33495b9c547cSRui Paulo 			goto failed;
33505b9c547cSRui Paulo 		params->num_filter_ssids = src->num_filter_ssids;
33515b9c547cSRui Paulo 	}
33525b9c547cSRui Paulo 
33535b9c547cSRui Paulo 	params->filter_rssi = src->filter_rssi;
33545b9c547cSRui Paulo 	params->p2p_probe = src->p2p_probe;
33555b9c547cSRui Paulo 	params->only_new_results = src->only_new_results;
33565b9c547cSRui Paulo 	params->low_priority = src->low_priority;
335785732ac8SCy Schubert 	params->duration = src->duration;
335885732ac8SCy Schubert 	params->duration_mandatory = src->duration_mandatory;
335985732ac8SCy Schubert 	params->oce_scan = src->oce_scan;
3360*a90b9d01SCy Schubert 	params->link_id = src->link_id;
33615b9c547cSRui Paulo 
3362780fb4a2SCy Schubert 	if (src->sched_scan_plans_num > 0) {
3363780fb4a2SCy Schubert 		params->sched_scan_plans =
336485732ac8SCy Schubert 			os_memdup(src->sched_scan_plans,
336585732ac8SCy Schubert 				  sizeof(*src->sched_scan_plans) *
3366780fb4a2SCy Schubert 				  src->sched_scan_plans_num);
3367780fb4a2SCy Schubert 		if (!params->sched_scan_plans)
3368780fb4a2SCy Schubert 			goto failed;
3369780fb4a2SCy Schubert 
3370780fb4a2SCy Schubert 		params->sched_scan_plans_num = src->sched_scan_plans_num;
3371780fb4a2SCy Schubert 	}
3372780fb4a2SCy Schubert 
3373c1d255d3SCy Schubert 	if (src->mac_addr_rand &&
3374c1d255d3SCy Schubert 	    wpa_setup_mac_addr_rand_params(params, src->mac_addr))
33755b9c547cSRui Paulo 		goto failed;
33765b9c547cSRui Paulo 
3377780fb4a2SCy Schubert 	if (src->bssid) {
3378780fb4a2SCy Schubert 		u8 *bssid;
3379780fb4a2SCy Schubert 
338085732ac8SCy Schubert 		bssid = os_memdup(src->bssid, ETH_ALEN);
3381780fb4a2SCy Schubert 		if (!bssid)
3382780fb4a2SCy Schubert 			goto failed;
3383780fb4a2SCy Schubert 		params->bssid = bssid;
3384780fb4a2SCy Schubert 	}
3385780fb4a2SCy Schubert 
338685732ac8SCy Schubert 	params->relative_rssi_set = src->relative_rssi_set;
338785732ac8SCy Schubert 	params->relative_rssi = src->relative_rssi;
338885732ac8SCy Schubert 	params->relative_adjust_band = src->relative_adjust_band;
338985732ac8SCy Schubert 	params->relative_adjust_rssi = src->relative_adjust_rssi;
3390c1d255d3SCy Schubert 	params->p2p_include_6ghz = src->p2p_include_6ghz;
3391*a90b9d01SCy Schubert 	params->non_coloc_6ghz = src->non_coloc_6ghz;
3392*a90b9d01SCy Schubert 	params->min_probe_req_content = src->min_probe_req_content;
33935b9c547cSRui Paulo 	return params;
33945b9c547cSRui Paulo 
33955b9c547cSRui Paulo failed:
33965b9c547cSRui Paulo 	wpa_scan_free_params(params);
33975b9c547cSRui Paulo 	return NULL;
33985b9c547cSRui Paulo }
33995b9c547cSRui Paulo 
34005b9c547cSRui Paulo 
wpa_scan_free_params(struct wpa_driver_scan_params * params)34015b9c547cSRui Paulo void wpa_scan_free_params(struct wpa_driver_scan_params *params)
34025b9c547cSRui Paulo {
34035b9c547cSRui Paulo 	size_t i;
34045b9c547cSRui Paulo 
34055b9c547cSRui Paulo 	if (params == NULL)
34065b9c547cSRui Paulo 		return;
34075b9c547cSRui Paulo 
34085b9c547cSRui Paulo 	for (i = 0; i < params->num_ssids; i++)
34095b9c547cSRui Paulo 		os_free((u8 *) params->ssids[i].ssid);
34105b9c547cSRui Paulo 	os_free((u8 *) params->extra_ies);
34115b9c547cSRui Paulo 	os_free(params->freqs);
34125b9c547cSRui Paulo 	os_free(params->filter_ssids);
3413780fb4a2SCy Schubert 	os_free(params->sched_scan_plans);
34145b9c547cSRui Paulo 
34155b9c547cSRui Paulo 	/*
34165b9c547cSRui Paulo 	 * Note: params->mac_addr_mask points to same memory allocation and
34175b9c547cSRui Paulo 	 * must not be freed separately.
34185b9c547cSRui Paulo 	 */
34195b9c547cSRui Paulo 	os_free((u8 *) params->mac_addr);
34205b9c547cSRui Paulo 
3421780fb4a2SCy Schubert 	os_free((u8 *) params->bssid);
3422780fb4a2SCy Schubert 
34235b9c547cSRui Paulo 	os_free(params);
34245b9c547cSRui Paulo }
34255b9c547cSRui Paulo 
34265b9c547cSRui Paulo 
wpas_start_pno(struct wpa_supplicant * wpa_s)34275b9c547cSRui Paulo int wpas_start_pno(struct wpa_supplicant *wpa_s)
34285b9c547cSRui Paulo {
3429c1d255d3SCy Schubert 	int ret;
3430c1d255d3SCy Schubert 	size_t prio, i, num_ssid, num_match_ssid;
34315b9c547cSRui Paulo 	struct wpa_ssid *ssid;
34325b9c547cSRui Paulo 	struct wpa_driver_scan_params params;
3433780fb4a2SCy Schubert 	struct sched_scan_plan scan_plan;
3434780fb4a2SCy Schubert 	unsigned int max_sched_scan_ssids;
34355b9c547cSRui Paulo 
34365b9c547cSRui Paulo 	if (!wpa_s->sched_scan_supported)
34375b9c547cSRui Paulo 		return -1;
34385b9c547cSRui Paulo 
3439780fb4a2SCy Schubert 	if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
3440780fb4a2SCy Schubert 		max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
3441780fb4a2SCy Schubert 	else
3442780fb4a2SCy Schubert 		max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
3443780fb4a2SCy Schubert 	if (max_sched_scan_ssids < 1)
3444780fb4a2SCy Schubert 		return -1;
3445780fb4a2SCy Schubert 
34465b9c547cSRui Paulo 	if (wpa_s->pno || wpa_s->pno_sched_pending)
34475b9c547cSRui Paulo 		return 0;
34485b9c547cSRui Paulo 
34495b9c547cSRui Paulo 	if ((wpa_s->wpa_state > WPA_SCANNING) &&
345085732ac8SCy Schubert 	    (wpa_s->wpa_state < WPA_COMPLETED)) {
34515b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "PNO: In assoc process");
34525b9c547cSRui Paulo 		return -EAGAIN;
34535b9c547cSRui Paulo 	}
34545b9c547cSRui Paulo 
34555b9c547cSRui Paulo 	if (wpa_s->wpa_state == WPA_SCANNING) {
34565b9c547cSRui Paulo 		wpa_supplicant_cancel_scan(wpa_s);
34575b9c547cSRui Paulo 		if (wpa_s->sched_scanning) {
34585b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
34595b9c547cSRui Paulo 				   "ongoing sched scan");
34605b9c547cSRui Paulo 			wpa_supplicant_cancel_sched_scan(wpa_s);
34615b9c547cSRui Paulo 			wpa_s->pno_sched_pending = 1;
34625b9c547cSRui Paulo 			return 0;
34635b9c547cSRui Paulo 		}
34645b9c547cSRui Paulo 	}
34655b9c547cSRui Paulo 
3466780fb4a2SCy Schubert 	if (wpa_s->sched_scan_stop_req) {
3467780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
3468780fb4a2SCy Schubert 			   "Schedule PNO after previous sched scan has stopped");
3469780fb4a2SCy Schubert 		wpa_s->pno_sched_pending = 1;
3470780fb4a2SCy Schubert 		return 0;
3471780fb4a2SCy Schubert 	}
3472780fb4a2SCy Schubert 
34735b9c547cSRui Paulo 	os_memset(&params, 0, sizeof(params));
34745b9c547cSRui Paulo 
34755b9c547cSRui Paulo 	num_ssid = num_match_ssid = 0;
34765b9c547cSRui Paulo 	ssid = wpa_s->conf->ssid;
34775b9c547cSRui Paulo 	while (ssid) {
34785b9c547cSRui Paulo 		if (!wpas_network_disabled(wpa_s, ssid)) {
34795b9c547cSRui Paulo 			num_match_ssid++;
34805b9c547cSRui Paulo 			if (ssid->scan_ssid)
34815b9c547cSRui Paulo 				num_ssid++;
34825b9c547cSRui Paulo 		}
34835b9c547cSRui Paulo 		ssid = ssid->next;
34845b9c547cSRui Paulo 	}
34855b9c547cSRui Paulo 
34865b9c547cSRui Paulo 	if (num_match_ssid == 0) {
34875b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
34885b9c547cSRui Paulo 		return -1;
34895b9c547cSRui Paulo 	}
34905b9c547cSRui Paulo 
34915b9c547cSRui Paulo 	if (num_match_ssid > num_ssid) {
34925b9c547cSRui Paulo 		params.num_ssids++; /* wildcard */
34935b9c547cSRui Paulo 		num_ssid++;
34945b9c547cSRui Paulo 	}
34955b9c547cSRui Paulo 
3496780fb4a2SCy Schubert 	if (num_ssid > max_sched_scan_ssids) {
34975b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
3498780fb4a2SCy Schubert 			   "%u", max_sched_scan_ssids, (unsigned int) num_ssid);
3499780fb4a2SCy Schubert 		num_ssid = max_sched_scan_ssids;
35005b9c547cSRui Paulo 	}
35015b9c547cSRui Paulo 
35025b9c547cSRui Paulo 	if (num_match_ssid > wpa_s->max_match_sets) {
35035b9c547cSRui Paulo 		num_match_ssid = wpa_s->max_match_sets;
35045b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
35055b9c547cSRui Paulo 	}
35065b9c547cSRui Paulo 	params.filter_ssids = os_calloc(num_match_ssid,
35075b9c547cSRui Paulo 					sizeof(struct wpa_driver_scan_filter));
35085b9c547cSRui Paulo 	if (params.filter_ssids == NULL)
35095b9c547cSRui Paulo 		return -1;
35105b9c547cSRui Paulo 
35115b9c547cSRui Paulo 	i = 0;
35125b9c547cSRui Paulo 	prio = 0;
35135b9c547cSRui Paulo 	ssid = wpa_s->conf->pssid[prio];
35145b9c547cSRui Paulo 	while (ssid) {
35155b9c547cSRui Paulo 		if (!wpas_network_disabled(wpa_s, ssid)) {
35165b9c547cSRui Paulo 			if (ssid->scan_ssid && params.num_ssids < num_ssid) {
35175b9c547cSRui Paulo 				params.ssids[params.num_ssids].ssid =
35185b9c547cSRui Paulo 					ssid->ssid;
35195b9c547cSRui Paulo 				params.ssids[params.num_ssids].ssid_len =
35205b9c547cSRui Paulo 					 ssid->ssid_len;
35215b9c547cSRui Paulo 				params.num_ssids++;
35225b9c547cSRui Paulo 			}
35235b9c547cSRui Paulo 			os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
35245b9c547cSRui Paulo 				  ssid->ssid_len);
35255b9c547cSRui Paulo 			params.filter_ssids[i].ssid_len = ssid->ssid_len;
35265b9c547cSRui Paulo 			params.num_filter_ssids++;
35275b9c547cSRui Paulo 			i++;
35285b9c547cSRui Paulo 			if (i == num_match_ssid)
35295b9c547cSRui Paulo 				break;
35305b9c547cSRui Paulo 		}
35315b9c547cSRui Paulo 		if (ssid->pnext)
35325b9c547cSRui Paulo 			ssid = ssid->pnext;
35335b9c547cSRui Paulo 		else if (prio + 1 == wpa_s->conf->num_prio)
35345b9c547cSRui Paulo 			break;
35355b9c547cSRui Paulo 		else
35365b9c547cSRui Paulo 			ssid = wpa_s->conf->pssid[++prio];
35375b9c547cSRui Paulo 	}
35385b9c547cSRui Paulo 
35395b9c547cSRui Paulo 	if (wpa_s->conf->filter_rssi)
35405b9c547cSRui Paulo 		params.filter_rssi = wpa_s->conf->filter_rssi;
35415b9c547cSRui Paulo 
3542780fb4a2SCy Schubert 	if (wpa_s->sched_scan_plans_num) {
3543780fb4a2SCy Schubert 		params.sched_scan_plans = wpa_s->sched_scan_plans;
3544780fb4a2SCy Schubert 		params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
3545780fb4a2SCy Schubert 	} else {
3546780fb4a2SCy Schubert 		/* Set one scan plan that will run infinitely */
3547780fb4a2SCy Schubert 		if (wpa_s->conf->sched_scan_interval)
3548780fb4a2SCy Schubert 			scan_plan.interval = wpa_s->conf->sched_scan_interval;
3549780fb4a2SCy Schubert 		else
3550780fb4a2SCy Schubert 			scan_plan.interval = 10;
3551780fb4a2SCy Schubert 
3552780fb4a2SCy Schubert 		scan_plan.iterations = 0;
3553780fb4a2SCy Schubert 		params.sched_scan_plans = &scan_plan;
3554780fb4a2SCy Schubert 		params.sched_scan_plans_num = 1;
3555780fb4a2SCy Schubert 	}
35565b9c547cSRui Paulo 
355785732ac8SCy Schubert 	params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
355885732ac8SCy Schubert 
35595b9c547cSRui Paulo 	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
35605b9c547cSRui Paulo 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
35615b9c547cSRui Paulo 		params.freqs = wpa_s->manual_sched_scan_freqs;
35625b9c547cSRui Paulo 	}
35635b9c547cSRui Paulo 
356485732ac8SCy Schubert 	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
3565c1d255d3SCy Schubert 	    wpa_s->wpa_state <= WPA_SCANNING)
3566c1d255d3SCy Schubert 		wpa_setup_mac_addr_rand_params(&params, wpa_s->mac_addr_pno);
35675b9c547cSRui Paulo 
356885732ac8SCy Schubert 	wpa_scan_set_relative_rssi_params(wpa_s, &params);
356985732ac8SCy Schubert 
3570780fb4a2SCy Schubert 	ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
35715b9c547cSRui Paulo 	os_free(params.filter_ssids);
3572c1d255d3SCy Schubert 	os_free(params.mac_addr);
35735b9c547cSRui Paulo 	if (ret == 0)
35745b9c547cSRui Paulo 		wpa_s->pno = 1;
35755b9c547cSRui Paulo 	else
35765b9c547cSRui Paulo 		wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
35775b9c547cSRui Paulo 	return ret;
35785b9c547cSRui Paulo }
35795b9c547cSRui Paulo 
35805b9c547cSRui Paulo 
wpas_stop_pno(struct wpa_supplicant * wpa_s)35815b9c547cSRui Paulo int wpas_stop_pno(struct wpa_supplicant *wpa_s)
35825b9c547cSRui Paulo {
35835b9c547cSRui Paulo 	int ret = 0;
35845b9c547cSRui Paulo 
35855b9c547cSRui Paulo 	if (!wpa_s->pno)
35865b9c547cSRui Paulo 		return 0;
35875b9c547cSRui Paulo 
35885b9c547cSRui Paulo 	ret = wpa_supplicant_stop_sched_scan(wpa_s);
3589780fb4a2SCy Schubert 	wpa_s->sched_scan_stop_req = 1;
35905b9c547cSRui Paulo 
35915b9c547cSRui Paulo 	wpa_s->pno = 0;
35925b9c547cSRui Paulo 	wpa_s->pno_sched_pending = 0;
35935b9c547cSRui Paulo 
35945b9c547cSRui Paulo 	if (wpa_s->wpa_state == WPA_SCANNING)
35955b9c547cSRui Paulo 		wpa_supplicant_req_scan(wpa_s, 0, 0);
35965b9c547cSRui Paulo 
35975b9c547cSRui Paulo 	return ret;
35985b9c547cSRui Paulo }
35995b9c547cSRui Paulo 
36005b9c547cSRui Paulo 
wpas_mac_addr_rand_scan_clear(struct wpa_supplicant * wpa_s,unsigned int type)36015b9c547cSRui Paulo void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
36025b9c547cSRui Paulo 				    unsigned int type)
36035b9c547cSRui Paulo {
36045b9c547cSRui Paulo 	type &= MAC_ADDR_RAND_ALL;
36055b9c547cSRui Paulo 	wpa_s->mac_addr_rand_enable &= ~type;
36065b9c547cSRui Paulo 
36075b9c547cSRui Paulo 	if (type & MAC_ADDR_RAND_SCAN) {
36085b9c547cSRui Paulo 		os_free(wpa_s->mac_addr_scan);
36095b9c547cSRui Paulo 		wpa_s->mac_addr_scan = NULL;
36105b9c547cSRui Paulo 	}
36115b9c547cSRui Paulo 
36125b9c547cSRui Paulo 	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
36135b9c547cSRui Paulo 		os_free(wpa_s->mac_addr_sched_scan);
36145b9c547cSRui Paulo 		wpa_s->mac_addr_sched_scan = NULL;
36155b9c547cSRui Paulo 	}
36165b9c547cSRui Paulo 
36175b9c547cSRui Paulo 	if (type & MAC_ADDR_RAND_PNO) {
36185b9c547cSRui Paulo 		os_free(wpa_s->mac_addr_pno);
36195b9c547cSRui Paulo 		wpa_s->mac_addr_pno = NULL;
36205b9c547cSRui Paulo 	}
36215b9c547cSRui Paulo }
36225b9c547cSRui Paulo 
36235b9c547cSRui Paulo 
wpas_mac_addr_rand_scan_set(struct wpa_supplicant * wpa_s,unsigned int type,const u8 * addr,const u8 * mask)36245b9c547cSRui Paulo int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
36255b9c547cSRui Paulo 				unsigned int type, const u8 *addr,
36265b9c547cSRui Paulo 				const u8 *mask)
36275b9c547cSRui Paulo {
36285b9c547cSRui Paulo 	u8 *tmp = NULL;
36295b9c547cSRui Paulo 
36304bc52338SCy Schubert 	if ((wpa_s->mac_addr_rand_supported & type) != type ) {
36314bc52338SCy Schubert 		wpa_printf(MSG_INFO,
36324bc52338SCy Schubert 			   "scan: MAC randomization type %u != supported=%u",
36334bc52338SCy Schubert 			   type, wpa_s->mac_addr_rand_supported);
36344bc52338SCy Schubert 		return -1;
36354bc52338SCy Schubert 	}
36364bc52338SCy Schubert 
36375b9c547cSRui Paulo 	wpas_mac_addr_rand_scan_clear(wpa_s, type);
36385b9c547cSRui Paulo 
36395b9c547cSRui Paulo 	if (addr) {
36405b9c547cSRui Paulo 		tmp = os_malloc(2 * ETH_ALEN);
36415b9c547cSRui Paulo 		if (!tmp)
36425b9c547cSRui Paulo 			return -1;
36435b9c547cSRui Paulo 		os_memcpy(tmp, addr, ETH_ALEN);
36445b9c547cSRui Paulo 		os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
36455b9c547cSRui Paulo 	}
36465b9c547cSRui Paulo 
36475b9c547cSRui Paulo 	if (type == MAC_ADDR_RAND_SCAN) {
36485b9c547cSRui Paulo 		wpa_s->mac_addr_scan = tmp;
36495b9c547cSRui Paulo 	} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
36505b9c547cSRui Paulo 		wpa_s->mac_addr_sched_scan = tmp;
36515b9c547cSRui Paulo 	} else if (type == MAC_ADDR_RAND_PNO) {
36525b9c547cSRui Paulo 		wpa_s->mac_addr_pno = tmp;
36535b9c547cSRui Paulo 	} else {
36545b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
36555b9c547cSRui Paulo 			   "scan: Invalid MAC randomization type=0x%x",
36565b9c547cSRui Paulo 			   type);
36575b9c547cSRui Paulo 		os_free(tmp);
36585b9c547cSRui Paulo 		return -1;
36595b9c547cSRui Paulo 	}
36605b9c547cSRui Paulo 
36615b9c547cSRui Paulo 	wpa_s->mac_addr_rand_enable |= type;
36625b9c547cSRui Paulo 	return 0;
36635b9c547cSRui Paulo }
3664780fb4a2SCy Schubert 
3665780fb4a2SCy Schubert 
wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant * wpa_s,unsigned int type,u8 * mask)3666c1d255d3SCy Schubert int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
3667c1d255d3SCy Schubert 				     unsigned int type, u8 *mask)
3668c1d255d3SCy Schubert {
3669c1d255d3SCy Schubert 	const u8 *to_copy;
3670c1d255d3SCy Schubert 
3671c1d255d3SCy Schubert 	if ((wpa_s->mac_addr_rand_enable & type) != type)
3672c1d255d3SCy Schubert 		return -1;
3673c1d255d3SCy Schubert 
3674c1d255d3SCy Schubert 	if (type == MAC_ADDR_RAND_SCAN) {
3675c1d255d3SCy Schubert 		to_copy = wpa_s->mac_addr_scan;
3676c1d255d3SCy Schubert 	} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
3677c1d255d3SCy Schubert 		to_copy = wpa_s->mac_addr_sched_scan;
3678c1d255d3SCy Schubert 	} else if (type == MAC_ADDR_RAND_PNO) {
3679c1d255d3SCy Schubert 		to_copy = wpa_s->mac_addr_pno;
3680c1d255d3SCy Schubert 	} else {
3681c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
3682c1d255d3SCy Schubert 			   "scan: Invalid MAC randomization type=0x%x",
3683c1d255d3SCy Schubert 			   type);
3684c1d255d3SCy Schubert 		return -1;
3685c1d255d3SCy Schubert 	}
3686c1d255d3SCy Schubert 
3687c1d255d3SCy Schubert 	os_memcpy(mask, to_copy + ETH_ALEN, ETH_ALEN);
3688c1d255d3SCy Schubert 	return 0;
3689c1d255d3SCy Schubert }
3690c1d255d3SCy Schubert 
3691c1d255d3SCy Schubert 
wpas_abort_ongoing_scan(struct wpa_supplicant * wpa_s)3692780fb4a2SCy Schubert int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
3693780fb4a2SCy Schubert {
369485732ac8SCy Schubert 	struct wpa_radio_work *work;
369585732ac8SCy Schubert 	struct wpa_radio *radio = wpa_s->radio;
3696780fb4a2SCy Schubert 
369785732ac8SCy Schubert 	dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
369885732ac8SCy Schubert 		if (work->wpa_s != wpa_s || !work->started ||
369985732ac8SCy Schubert 		    (os_strcmp(work->type, "scan") != 0 &&
370085732ac8SCy Schubert 		     os_strcmp(work->type, "p2p-scan") != 0))
370185732ac8SCy Schubert 			continue;
3702780fb4a2SCy Schubert 		wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
370385732ac8SCy Schubert 		return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie);
3704780fb4a2SCy Schubert 	}
3705780fb4a2SCy Schubert 
370685732ac8SCy Schubert 	wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort");
370785732ac8SCy Schubert 	return -1;
3708780fb4a2SCy Schubert }
3709780fb4a2SCy Schubert 
3710780fb4a2SCy Schubert 
wpas_sched_scan_plans_set(struct wpa_supplicant * wpa_s,const char * cmd)3711780fb4a2SCy Schubert int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
3712780fb4a2SCy Schubert {
3713780fb4a2SCy Schubert 	struct sched_scan_plan *scan_plans = NULL;
3714780fb4a2SCy Schubert 	const char *token, *context = NULL;
3715780fb4a2SCy Schubert 	unsigned int num = 0;
3716780fb4a2SCy Schubert 
3717780fb4a2SCy Schubert 	if (!cmd)
3718780fb4a2SCy Schubert 		return -1;
3719780fb4a2SCy Schubert 
3720780fb4a2SCy Schubert 	if (!cmd[0]) {
3721780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "Clear sched scan plans");
3722780fb4a2SCy Schubert 		os_free(wpa_s->sched_scan_plans);
3723780fb4a2SCy Schubert 		wpa_s->sched_scan_plans = NULL;
3724780fb4a2SCy Schubert 		wpa_s->sched_scan_plans_num = 0;
3725780fb4a2SCy Schubert 		return 0;
3726780fb4a2SCy Schubert 	}
3727780fb4a2SCy Schubert 
3728780fb4a2SCy Schubert 	while ((token = cstr_token(cmd, " ", &context))) {
3729780fb4a2SCy Schubert 		int ret;
3730780fb4a2SCy Schubert 		struct sched_scan_plan *scan_plan, *n;
3731780fb4a2SCy Schubert 
3732780fb4a2SCy Schubert 		n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
3733780fb4a2SCy Schubert 		if (!n)
3734780fb4a2SCy Schubert 			goto fail;
3735780fb4a2SCy Schubert 
3736780fb4a2SCy Schubert 		scan_plans = n;
3737780fb4a2SCy Schubert 		scan_plan = &scan_plans[num];
3738780fb4a2SCy Schubert 		num++;
3739780fb4a2SCy Schubert 
3740780fb4a2SCy Schubert 		ret = sscanf(token, "%u:%u", &scan_plan->interval,
3741780fb4a2SCy Schubert 			     &scan_plan->iterations);
3742780fb4a2SCy Schubert 		if (ret <= 0 || ret > 2 || !scan_plan->interval) {
3743780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR,
3744780fb4a2SCy Schubert 				   "Invalid sched scan plan input: %s", token);
3745780fb4a2SCy Schubert 			goto fail;
3746780fb4a2SCy Schubert 		}
3747780fb4a2SCy Schubert 
3748780fb4a2SCy Schubert 		if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
3749780fb4a2SCy Schubert 			wpa_printf(MSG_WARNING,
3750780fb4a2SCy Schubert 				   "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
3751780fb4a2SCy Schubert 				   num, scan_plan->interval,
3752780fb4a2SCy Schubert 				   wpa_s->max_sched_scan_plan_interval);
3753780fb4a2SCy Schubert 			scan_plan->interval =
3754780fb4a2SCy Schubert 				wpa_s->max_sched_scan_plan_interval;
3755780fb4a2SCy Schubert 		}
3756780fb4a2SCy Schubert 
3757780fb4a2SCy Schubert 		if (ret == 1) {
3758780fb4a2SCy Schubert 			scan_plan->iterations = 0;
3759780fb4a2SCy Schubert 			break;
3760780fb4a2SCy Schubert 		}
3761780fb4a2SCy Schubert 
3762780fb4a2SCy Schubert 		if (!scan_plan->iterations) {
3763780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR,
3764780fb4a2SCy Schubert 				   "scan plan %u: Number of iterations cannot be zero",
3765780fb4a2SCy Schubert 				   num);
3766780fb4a2SCy Schubert 			goto fail;
3767780fb4a2SCy Schubert 		}
3768780fb4a2SCy Schubert 
3769780fb4a2SCy Schubert 		if (scan_plan->iterations >
3770780fb4a2SCy Schubert 		    wpa_s->max_sched_scan_plan_iterations) {
3771780fb4a2SCy Schubert 			wpa_printf(MSG_WARNING,
3772780fb4a2SCy Schubert 				   "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
3773780fb4a2SCy Schubert 				   num, scan_plan->iterations,
3774780fb4a2SCy Schubert 				   wpa_s->max_sched_scan_plan_iterations);
3775780fb4a2SCy Schubert 			scan_plan->iterations =
3776780fb4a2SCy Schubert 				wpa_s->max_sched_scan_plan_iterations;
3777780fb4a2SCy Schubert 		}
3778780fb4a2SCy Schubert 
3779780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
3780780fb4a2SCy Schubert 			   "scan plan %u: interval=%u iterations=%u",
3781780fb4a2SCy Schubert 			   num, scan_plan->interval, scan_plan->iterations);
3782780fb4a2SCy Schubert 	}
3783780fb4a2SCy Schubert 
3784780fb4a2SCy Schubert 	if (!scan_plans) {
3785780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "Invalid scan plans entry");
3786780fb4a2SCy Schubert 		goto fail;
3787780fb4a2SCy Schubert 	}
3788780fb4a2SCy Schubert 
3789780fb4a2SCy Schubert 	if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
3790780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
3791780fb4a2SCy Schubert 			   "All scan plans but the last must specify a number of iterations");
3792780fb4a2SCy Schubert 		goto fail;
3793780fb4a2SCy Schubert 	}
3794780fb4a2SCy Schubert 
3795780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
3796780fb4a2SCy Schubert 		   num, scan_plans[num - 1].interval);
3797780fb4a2SCy Schubert 
3798780fb4a2SCy Schubert 	if (num > wpa_s->max_sched_scan_plans) {
3799780fb4a2SCy Schubert 		wpa_printf(MSG_WARNING,
3800780fb4a2SCy Schubert 			   "Too many scheduled scan plans (only %u supported)",
3801780fb4a2SCy Schubert 			   wpa_s->max_sched_scan_plans);
3802780fb4a2SCy Schubert 		wpa_printf(MSG_WARNING,
3803780fb4a2SCy Schubert 			   "Use only the first %u scan plans, and the last one (in infinite loop)",
3804780fb4a2SCy Schubert 			   wpa_s->max_sched_scan_plans - 1);
3805780fb4a2SCy Schubert 		os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
3806780fb4a2SCy Schubert 			  &scan_plans[num - 1], sizeof(*scan_plans));
3807780fb4a2SCy Schubert 		num = wpa_s->max_sched_scan_plans;
3808780fb4a2SCy Schubert 	}
3809780fb4a2SCy Schubert 
3810780fb4a2SCy Schubert 	os_free(wpa_s->sched_scan_plans);
3811780fb4a2SCy Schubert 	wpa_s->sched_scan_plans = scan_plans;
3812780fb4a2SCy Schubert 	wpa_s->sched_scan_plans_num = num;
3813780fb4a2SCy Schubert 
3814780fb4a2SCy Schubert 	return 0;
3815780fb4a2SCy Schubert 
3816780fb4a2SCy Schubert fail:
3817780fb4a2SCy Schubert 	os_free(scan_plans);
3818780fb4a2SCy Schubert 	wpa_printf(MSG_ERROR, "invalid scan plans list");
3819780fb4a2SCy Schubert 	return -1;
3820780fb4a2SCy Schubert }
3821780fb4a2SCy Schubert 
3822780fb4a2SCy Schubert 
3823780fb4a2SCy Schubert /**
3824780fb4a2SCy Schubert  * wpas_scan_reset_sched_scan - Reset sched_scan state
3825780fb4a2SCy Schubert  * @wpa_s: Pointer to wpa_supplicant data
3826780fb4a2SCy Schubert  *
3827780fb4a2SCy Schubert  * This function is used to cancel a running scheduled scan and to reset an
3828780fb4a2SCy Schubert  * internal scan state to continue with a regular scan on the following
3829780fb4a2SCy Schubert  * wpa_supplicant_req_scan() calls.
3830780fb4a2SCy Schubert  */
wpas_scan_reset_sched_scan(struct wpa_supplicant * wpa_s)3831780fb4a2SCy Schubert void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s)
3832780fb4a2SCy Schubert {
3833780fb4a2SCy Schubert 	wpa_s->normal_scans = 0;
3834780fb4a2SCy Schubert 	if (wpa_s->sched_scanning) {
3835780fb4a2SCy Schubert 		wpa_s->sched_scan_timed_out = 0;
3836780fb4a2SCy Schubert 		wpa_s->prev_sched_ssid = NULL;
3837780fb4a2SCy Schubert 		wpa_supplicant_cancel_sched_scan(wpa_s);
3838780fb4a2SCy Schubert 	}
3839780fb4a2SCy Schubert }
3840780fb4a2SCy Schubert 
3841780fb4a2SCy Schubert 
wpas_scan_restart_sched_scan(struct wpa_supplicant * wpa_s)3842780fb4a2SCy Schubert void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s)
3843780fb4a2SCy Schubert {
3844780fb4a2SCy Schubert 	/* simulate timeout to restart the sched scan */
3845780fb4a2SCy Schubert 	wpa_s->sched_scan_timed_out = 1;
3846780fb4a2SCy Schubert 	wpa_s->prev_sched_ssid = NULL;
3847780fb4a2SCy Schubert 	wpa_supplicant_cancel_sched_scan(wpa_s);
3848780fb4a2SCy Schubert }
3849