@@ -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);
}
@@ -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
@@ -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;
};
@@ -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 */
@@ -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, ¶ms, interval);
+ ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms);
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;
+}
@@ -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);
@@ -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;
}
@@ -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
@@ -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 */