diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c index a2cf7a5ef..d12eb2158 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 bdcfe9f72..b8b19de89 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2281,6 +2281,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); } @@ -4258,6 +4260,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 2dd147574..e93a0a3f8 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: ... + */ + char *sched_scan_plans; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index be7215791..80e3e5600 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1304,6 +1304,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 25a1011b3..fd01f4871 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,51 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) scan_params = ¶ms; 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, + "Too many SSIDs. 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 timeout %d", - wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + "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: interval %d (no timeout)", - wpa_s->sched_scan_interval); + wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)"); } wpa_setband_scan_freqs(wpa_s, scan_params); @@ -1390,8 +1413,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 +1431,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 +2315,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 +2413,20 @@ 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 +2441,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; @@ -2499,3 +2537,114 @@ int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s) 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; + unsigned int 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 %u: Interval cannot be zero", + num); + goto fail; + } + + if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) { + wpa_printf(MSG_WARNING, + "scan plan %u: 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 %u: 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 %u: 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 %u: 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 %u (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 %u supported)", + wpa_s->max_sched_scan_plans); + wpa_printf(MSG_WARNING, + "Use only the first %u 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 0f1c8e498..93ec9b38b 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 94b5eff34..c9b78de1d 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; } @@ -4600,6 +4604,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; @@ -4739,6 +4748,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 bcb6247f4..2ce1cc4e3 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -314,7 +314,9 @@ fast_reauth=1 # up to the limit of 300 seconds (3, 9, 27 ... 300) # For periodic module, parameters would be #autoscan=periodic:30 -# So a delay of 30 seconds will be applied between each scan +# So a delay of 30 seconds will be applied between each scan. +# Note: 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,27 @@ 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= ... +# +# 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 c66539d07..a8b273bc0 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; + unsigned int 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; @@ -1184,4 +1188,6 @@ 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 */