[PATCH 2/2] wpa_supplicant: Add support for scheduled scan plans configurations

Ilan Peer ilan.peer at intel.com
Tue Nov 17 05:08:24 PST 2015


From: Avraham Stern <avraham.stern at intel.com>

Add the option to configure scheduled scan plans in the config file.
Each scan plan specifies the interval between scans and the number
of scan iterations. The last plan will run infinitely and thus
specifies only the interval between scan iterations.

usage:
sched_scan_plans=<interval:iterations> <interval2:iterations2> ... <interval>

Signed-off-by: Avraham Stern <avraham.stern at intel.com>
---
 wpa_supplicant/autoscan.c          |  27 ++++-
 wpa_supplicant/config.c            |   3 +
 wpa_supplicant/config.h            |  11 ++
 wpa_supplicant/config_file.c       |   3 +
 wpa_supplicant/scan.c              | 210 +++++++++++++++++++++++++++++++------
 wpa_supplicant/scan.h              |   3 +-
 wpa_supplicant/wpa_supplicant.c    |  11 ++
 wpa_supplicant/wpa_supplicant.conf |  18 ++++
 wpa_supplicant/wpa_supplicant_i.h  |   7 +-
 9 files changed, 257 insertions(+), 36 deletions(-)

diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index a2cf7a5..d12eb21 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -1,6 +1,7 @@
 /*
  * WPA Supplicant - auto scan
  * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ * Copyright 2015	Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -50,6 +51,11 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
 	size_t nlen;
 	int i;
 	const struct autoscan_ops *ops = NULL;
+	struct sched_scan_plan *scan_plans;
+
+	/* Give preference to scheduled scan plans if supported/configured */
+	if (wpa_s->sched_scan_plans)
+		return 0;
 
 	if (wpa_s->autoscan && wpa_s->autoscan_priv)
 		return 0;
@@ -79,11 +85,23 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
 		return -1;
 	}
 
+	scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans));
+	if (!scan_plans)
+		return -1;
+
 	wpa_s->autoscan_params = NULL;
 
 	wpa_s->autoscan_priv = ops->init(wpa_s, params);
-	if (wpa_s->autoscan_priv == NULL)
+	if (!wpa_s->autoscan_priv) {
+		os_free(scan_plans);
 		return -1;
+	}
+
+	scan_plans[0].interval = 5;
+	scan_plans[0].iterations = 0;
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = scan_plans;
+	wpa_s->sched_scan_plans_num = 1;
 	wpa_s->autoscan = ops;
 
 	wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
@@ -116,7 +134,10 @@ void autoscan_deinit(struct wpa_supplicant *wpa_s)
 		wpa_s->autoscan_priv = NULL;
 
 		wpa_s->scan_interval = 5;
-		wpa_s->sched_scan_interval = 0;
+
+		os_free(wpa_s->sched_scan_plans);
+		wpa_s->sched_scan_plans = NULL;
+		wpa_s->sched_scan_plans_num = 0;
 	}
 }
 
@@ -134,7 +155,7 @@ int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
 			return -1;
 
 		wpa_s->scan_interval = interval;
-		wpa_s->sched_scan_interval = interval;
+		wpa_s->sched_scan_plans[0].interval = interval;
 
 		request_scan(wpa_s);
 	}
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index db5de5f..0e4f544 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2271,6 +2271,8 @@ void wpa_config_free(struct wpa_config *config)
 	os_free(config->bgscan);
 	os_free(config->wowlan_triggers);
 	os_free(config->fst_group_id);
+	os_free(config->sched_scan_plans);
+
 	os_free(config);
 }
 
@@ -4248,6 +4250,7 @@ static const struct global_parse_data global_fields[] = {
 	{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
 #endif /* CONFIG_FST */
 	{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
+	{ STR(sched_scan_plans), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 2dd1475..b090ad9 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1263,6 +1263,17 @@ struct wpa_config {
 	  * of 4-Way Handshake or message 1 of Group Key Handshake.
 	  */
 	 int wpa_rsc_relaxation;
+
+	/**
+	 * sched_scan_plans - scan plans for scheduled scan
+	 *
+	 * Each scan plan specifies the interval between scans and the number of
+	 * iterations. The last scan plan only specifies the scan interval and
+	 * will be run infinitely.
+	 *
+	 * format: <interval:iterations> <interval2:iterations2> ... <interval>
+	 */
+	 char *sched_scan_plans;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 215388c..224aa85 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1303,6 +1303,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 	if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION)
 		fprintf(f, "wpa_rsc_relaxation=%d\n",
 			config->wpa_rsc_relaxation);
+
+	if (config->sched_scan_plans)
+		fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 9f908e2..c80e56c 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -267,17 +267,9 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 
 
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params,
-				    int interval)
+				    struct wpa_driver_scan_params *params)
 {
 	int ret;
-	struct sched_scan_plan scan_plan = {
-		.interval = interval,
-		.iterations = 0,
-	};
-
-	params->sched_scan_plans = &scan_plan;
-	params->sched_scan_plans_num = 1;
 
 	wpa_supplicant_notify_scanning(wpa_s, 1);
 	ret = wpa_drv_sched_scan(wpa_s, params);
@@ -286,8 +278,6 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
 	else
 		wpa_s->sched_scanning = 1;
 
-	params->sched_scan_plans = NULL;
-	params->sched_scan_plans_num = 0;
 	return ret;
 }
 
@@ -1191,6 +1181,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 	unsigned int max_sched_scan_ssids;
 	int wildcard = 0;
 	int need_ssids;
+	struct sched_scan_plan scan_plan;
 
 	if (!wpa_s->sched_scan_supported)
 		return -1;
@@ -1280,11 +1271,6 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 
 	if (!ssid || !wpa_s->prev_sched_ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
-		if (wpa_s->conf->sched_scan_interval)
-			wpa_s->sched_scan_interval =
-				wpa_s->conf->sched_scan_interval;
-		if (wpa_s->sched_scan_interval == 0)
-			wpa_s->sched_scan_interval = 10;
 		wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 		wpa_s->first_sched_scan = 1;
 		ssid = wpa_s->conf->ssid;
@@ -1369,14 +1355,52 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 	scan_params = ¶ms;
 
 scan:
-	if (ssid || !wpa_s->first_sched_scan) {
+	wpa_s->sched_scan_timed_out = 0;
+
+	/*
+	 * We cannot support multiple scan plans if the scan request includes
+	 * too many SSID's, so in this case use only the last scan plan and make
+	 * it run infinitely. It will be stopped by the timeout.
+	 */
+	if (wpa_s->sched_scan_plans_num == 1 ||
+	    (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
+		params.sched_scan_plans = wpa_s->sched_scan_plans;
+		params.sched_scan_plans_num =
+			wpa_s->sched_scan_plans_num;
+	} else if (wpa_s->sched_scan_plans_num > 1) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %d timeout %d",
-			wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+			"Too many SSID's. Default to using single scheduled_scan plan");
+		params.sched_scan_plans =
+			&wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num - 1];
+		params.sched_scan_plans_num = 1;
 	} else {
+		if (wpa_s->conf->sched_scan_interval)
+			scan_plan.interval =
+				wpa_s->conf->sched_scan_interval;
+		else
+			scan_plan.interval = 10;
+
+		if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
+			wpa_printf(MSG_WARNING,
+				   "Scan interval too long(%u), use the maximum allowed(%u)",
+				   scan_plan.interval,
+				   wpa_s->max_sched_scan_plan_interval);
+			scan_plan.interval =
+				wpa_s->max_sched_scan_plan_interval;
+		}
+
+		scan_plan.iterations = 0;
+		params.sched_scan_plans = &scan_plan;
+		params.sched_scan_plans_num = 1;
+	}
+
+	if (ssid || !wpa_s->first_sched_scan) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %d (no timeout)",
-			wpa_s->sched_scan_interval);
+			"Starting sched scan: interval %u timeout %d",
+			params.sched_scan_plans[0].interval,
+			wpa_s->sched_scan_timeout);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
 	}
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -1390,8 +1414,7 @@ scan:
 		}
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
-					      wpa_s->sched_scan_interval);
+	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
 	wpabuf_free(extra_ie);
 	os_free(params.filter_ssids);
 	if (ret) {
@@ -1409,9 +1432,12 @@ scan:
 				       wpa_s, NULL);
 		wpa_s->first_sched_scan = 0;
 		wpa_s->sched_scan_timeout /= 2;
-		wpa_s->sched_scan_interval *= 2;
-		if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
-			wpa_s->sched_scan_interval = 10;
+		params.sched_scan_plans[0].interval *= 2;
+		if ((unsigned int)wpa_s->sched_scan_timeout <
+		    params.sched_scan_plans[0].interval ||
+		    params.sched_scan_plans[0].interval >
+		    wpa_s->max_sched_scan_plan_interval) {
+			params.sched_scan_plans[0].interval = 10;
 			wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 		}
 	}
@@ -2290,10 +2316,11 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params)
 
 int wpas_start_pno(struct wpa_supplicant *wpa_s)
 {
-	int ret, interval, prio;
+	int ret, prio;
 	size_t i, num_ssid, num_match_ssid;
 	struct wpa_ssid *ssid;
 	struct wpa_driver_scan_params params;
+	struct sched_scan_plan scan_plan;
 
 	if (!wpa_s->sched_scan_supported)
 		return -1;
@@ -2387,8 +2414,21 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
 	if (wpa_s->conf->filter_rssi)
 		params.filter_rssi = wpa_s->conf->filter_rssi;
 
-	interval = wpa_s->conf->sched_scan_interval ?
-		wpa_s->conf->sched_scan_interval : 10;
+	if (wpa_s->sched_scan_plans_num) {
+		params.sched_scan_plans = wpa_s->sched_scan_plans;
+		params.sched_scan_plans_num =
+			wpa_s->sched_scan_plans_num;
+	} else {
+		/* Set one scan plan that will run infinitely */
+		if (wpa_s->conf->sched_scan_interval)
+			scan_plan.interval = wpa_s->conf->sched_scan_interval;
+		else
+			scan_plan.interval = 10;
+
+		scan_plan.iterations = 0;
+		params.sched_scan_plans = &scan_plan;
+		params.sched_scan_plans_num = 1;
+	}
 
 	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
@@ -2403,7 +2443,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
 		}
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+	ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
 	os_free(params.filter_ssids);
 	if (ret == 0)
 		wpa_s->pno = 1;
@@ -2488,3 +2528,113 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
 	wpa_s->mac_addr_rand_enable |= type;
 	return 0;
 }
+
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct sched_scan_plan *scan_plans = NULL;
+	const char *token, *context = NULL;
+	size_t num = 0;
+
+	if (!cmd)
+		return -1;
+
+	while ((token = cstr_token(cmd, " ", &context))) {
+		int ret;
+		struct sched_scan_plan *scan_plan, *n;
+
+		n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
+		if (!n)
+			goto fail;
+
+		scan_plans = n;
+		scan_plan = &scan_plans[num];
+		num++;
+
+		ret = sscanf(token, "%u:%u", &scan_plan->interval,
+			     &scan_plan->iterations);
+		if (ret <= 0 || ret > 2 || !scan_plan->interval) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid sched scan plan input: %s", token);
+			goto fail;
+		}
+
+		if (!scan_plan->interval) {
+			wpa_printf(MSG_ERROR,
+				   "scan plan %zu: Interval cannot be zero",
+				   num);
+			goto fail;
+		}
+
+		if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
+			wpa_printf(MSG_WARNING,
+				   "scan plan %zu: Scan interval too long(%u), use the maximum allowed(%u)",
+				   num, scan_plan->interval,
+				   wpa_s->max_sched_scan_plan_interval);
+			scan_plan->interval =
+				wpa_s->max_sched_scan_plan_interval;
+		}
+
+		if (ret == 1) {
+			scan_plan->iterations = 0;
+			break;
+		}
+
+		if (!scan_plan->iterations) {
+			wpa_printf(MSG_ERROR,
+				   "scan plan %zu: Number of iterations cannot be zero",
+				   num);
+			goto fail;
+		}
+
+		if (scan_plan->iterations >
+		    wpa_s->max_sched_scan_plan_iterations) {
+			wpa_printf(MSG_WARNING,
+				   "scan plan %zu: Too many iterations(%u), use the maximum allowed(%u)",
+				   num, scan_plan->iterations,
+				   wpa_s->max_sched_scan_plan_iterations);
+			scan_plan->iterations =
+				wpa_s->max_sched_scan_plan_iterations;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "scan plan %zu: interval=%u iterations=%u", num,
+			   scan_plan->interval, scan_plan->iterations);
+	}
+
+	if (!scan_plans) {
+		wpa_printf(MSG_ERROR, "Invalid scan plans entry");
+		goto fail;
+	}
+
+	if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
+		wpa_printf(MSG_ERROR,
+			   "All scan plans but the last must specify a number of iterations");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "scan plan %zu (last plan): interval=%u", num,
+		   scan_plans[num - 1].interval);
+
+	if (num > wpa_s->max_sched_scan_plans) {
+		wpa_printf(MSG_WARNING,
+			   "Too many scheduled scan plans (only %zu supported)",
+			   wpa_s->max_sched_scan_plans);
+		wpa_printf(MSG_WARNING,
+			   "Use only the first %zu scan plans, and the last one (in infinite loop)",
+			   wpa_s->max_sched_scan_plans - 1);
+		os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
+			  &scan_plans[num - 1], sizeof(*scan_plans));
+		num = wpa_s->max_sched_scan_plans;
+	}
+
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = scan_plans;
+	wpa_s->sched_scan_plans_num = num;
+	return 0;
+
+fail:
+	os_free(scan_plans);
+	wpa_printf(MSG_ERROR, "invalid scan plans list");
+	return -1;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 7650f5a..9385ef2 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -40,8 +40,7 @@ void scan_only_handler(struct wpa_supplicant *wpa_s,
 		       struct wpa_scan_results *scan_res);
 int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params,
-				    int interval);
+				    struct wpa_driver_scan_params *params);
 int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
 struct wpa_driver_scan_params *
 wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 7631a79..520b7c6 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -545,6 +545,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 	}
 
 	wmm_ac_notify_disassoc(wpa_s);
+
+	wpa_s->sched_scan_plans_num = 0;
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = NULL;
 }
 
 
@@ -4527,6 +4531,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 		wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
 		wpa_s->max_scan_ssids = capa.max_scan_ssids;
 		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+		wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+		wpa_s->max_sched_scan_plan_interval =
+			capa.max_sched_scan_plan_interval;
+		wpa_s->max_sched_scan_plan_iterations =
+			capa.max_sched_scan_plan_iterations;
 		wpa_s->sched_scan_supported = capa.sched_scan_supported;
 		wpa_s->max_match_sets = capa.max_match_sets;
 		wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
@@ -4666,6 +4675,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 
 	wpas_rrm_reset(wpa_s);
 
+
+	wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
 	return 0;
 }
 
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index bcb6247..8ce4a93 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -315,6 +315,8 @@ fast_reauth=1
 # For periodic module, parameters would be <fixed interval>
 #autoscan=periodic:30
 # So a delay of 30 seconds will be applied between each scan
+# Note that if sched_scan_plans are configured and supported by
+# the driver, autoscan is ignored.
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
@@ -616,6 +618,22 @@ fast_reauth=1
 # Hotspot 2.0
 # hs20=1
 
+# Scheduled scan plans
+# A space delimited list of scan plans. Each scan plan specifies the scan
+# interval and number of iterations, delimited by a colon. The last scan plan
+# will run infinitely and thus must specify only the interval and not the number
+# of iterations.
+# The driver advertises the maximum number of scan plans supported. If more scan
+# plans than supported are configured, only the first ones are set (up to the
+# maximum supported). The last scan plan that specifies only the interval is
+# always set as the last plan.
+# If the scan interval or the number of iterations for a scan plan exceeds the
+# maximum supported, it will be set to the maximum supported value.
+# Format:
+# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval>
+# Example:
+# sched_scan_plans=10:100 20:200 30
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 5caa63e..d8d6c53 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -511,9 +511,10 @@ struct wpa_supplicant {
 
 	struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
 	int sched_scan_timeout;
-	int sched_scan_interval;
 	int first_sched_scan;
 	int sched_scan_timed_out;
+	struct sched_scan_plan *sched_scan_plans;
+	size_t sched_scan_plans_num;
 
 	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 				 struct wpa_scan_results *scan_res);
@@ -645,6 +646,9 @@ struct wpa_supplicant {
 
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
+	size_t max_sched_scan_plans;
+	unsigned int max_sched_scan_plan_interval;
+	unsigned int max_sched_scan_plan_iterations;
 	int sched_scan_supported;
 	unsigned int max_match_sets;
 	unsigned int max_remain_on_chan;
@@ -1179,4 +1183,5 @@ void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
 
 #endif /* CONFIG_FST */
 
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
 #endif /* WPA_SUPPLICANT_I_H */
-- 
1.9.1




More information about the Hostap mailing list