diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 403dfa3c5..6fd72c5aa 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -416,6 +416,28 @@ struct wpa_driver_scan_params { */ const u8 *mac_addr_mask; + /** + * sched_scan_plans - Scan plans for scheduled scan + * + * Each scan plan consists of the number of iterations to scan and the + * interval between scans. When a scan plan finishes (i.e., it was run + * for the specified number of iterations), the next scan plan is + * executed. The scan plans are executed in the order they appear in + * the array (lower index first). The last scan plan will run infinitely + * (until requested to stop), thus must not specify the number of + * iterations. All other scan plans must specify the number of + * iterations. + */ + struct sched_scan_plan { + u32 interval; /* In seconds */ + u32 iterations; /* Zero to run infinitely */ + } *sched_scan_plans; + + /** + * sched_scan_plans_num - Number of scan plans in sched_scan_plans array + */ + unsigned int sched_scan_plans_num; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with @@ -1242,6 +1264,15 @@ struct wpa_driver_capa { /** Maximum number of supported active probe SSIDs for sched_scan */ int max_sched_scan_ssids; + /** Maximum number of supported scan plans for scheduled scan */ + unsigned int max_sched_scan_plans; + + /** Maximum interval in a scan plan. In seconds */ + u32 max_sched_scan_plan_interval; + + /** Maximum number of iterations in a single scan plan */ + u32 max_sched_scan_plan_iterations; + /** Whether sched_scan (offloaded scanning) is supported */ int sched_scan_supported; @@ -3004,7 +3035,6 @@ struct wpa_driver_ops { * sched_scan - Request the driver to initiate scheduled scan * @priv: Private driver interface data * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure * * This operation should be used for scheduled scan offload to @@ -3015,8 +3045,7 @@ struct wpa_driver_ops { * and if not provided or if it returns -1, we fall back to * normal host-scheduled scans. */ - int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, - u32 interval); + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params); /** * stop_sched_scan - Request the driver to stop a scheduled scan diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index bcb09217b..0fa2c7ea8 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -7545,7 +7545,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.mac_addr_rand_scan_supported=%d\n" "capa.conc_capab=%u\n" "capa.max_conc_chan_2_4=%u\n" - "capa.max_conc_chan_5_0=%u\n", + "capa.max_conc_chan_5_0=%u\n" + "capa.max_sched_scan_plans=%u\n" + "capa.max_sched_scan_plan_interval=%u\n" + "capa.max_sched_scan_plan_iterations=%u\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, @@ -7564,7 +7567,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.mac_addr_rand_scan_supported, drv->capa.conc_capab, drv->capa.max_conc_chan_2_4, - drv->capa.max_conc_chan_5_0); + drv->capa.max_conc_chan_5_0, + drv->capa.max_sched_scan_plans, + drv->capa.max_sched_scan_plan_interval, + drv->capa.max_sched_scan_plan_iterations); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index ed03ac4e9..f042c3c9f 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -276,8 +276,7 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval); + struct wpa_driver_scan_params *params); int wpa_driver_nl80211_stop_sched_scan(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 1b5752fbb..c74ed5ff7 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -499,6 +499,19 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_sched_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] && + tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] && + tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) { + capa->max_sched_scan_plans = + nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]); + + capa->max_sched_scan_plan_interval = + nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]); + + capa->max_sched_scan_plan_iterations = + nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]); + } + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) capa->max_match_sets = nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); @@ -711,6 +724,12 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, drv->capa.max_csa_counters = 1; } + if (!drv->capa.max_sched_scan_plans) { + drv->capa.max_sched_scan_plans = 1; + drv->capa.max_sched_scan_plan_interval = UINT32_MAX; + drv->capa.max_sched_scan_plan_iterations = 0; + } + return 0; } diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index ec2eb2835..2ff254e43 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -1,5 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - Scanning + * Copyright(c) 2015 Intel Deutschland GmbH * Copyright (c) 2002-2014, Jouni Malinen * Copyright (c) 2007, Johannes Berg * Copyright (c) 2009-2010, Atheros Communications @@ -308,16 +309,82 @@ fail: } +static int +nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + struct wpa_driver_scan_params *params) +{ + struct nlattr *plans; + struct sched_scan_plan *scan_plans = params->sched_scan_plans; + unsigned int i; + + plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS); + if (!plans) + return -1; + + for (i = 0; i < params->sched_scan_plans_num; i++) { + struct nlattr *plan = nla_nest_start(msg, i + 1); + + if (!plan) + return -1; + + if (!scan_plans[i].interval || + scan_plans[i].interval > + drv->capa.max_sched_scan_plan_interval) { + wpa_printf(MSG_DEBUG, + "nl80211: sched scan plan no. %u: Invalid interval: %u", + i, scan_plans[i].interval); + return -1; + } + + if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL, + scan_plans[i].interval)) + return -1; + + if (scan_plans[i].iterations > + drv->capa.max_sched_scan_plan_iterations) { + wpa_printf(MSG_DEBUG, + "nl80211: sched scan plan no. %u: Invalid number of iterations: %u", + i, scan_plans[i].iterations); + return -1; + } + + if (scan_plans[i].iterations && + nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS, + scan_plans[i].iterations)) + return -1; + + nla_nest_end(msg, plan); + + /* + * All the scan plans must specify the number of iterations + * except the last plan, which will run infinitely. So if the + * number of iterations is not specified, this ought to be the + * last scan plan. + */ + if (!scan_plans[i].iterations) + break; + } + + if (i != params->sched_scan_plans_num - 1) { + wpa_printf(MSG_DEBUG, + "nl80211: All sched scan plans but the last must specify number of iterations"); + return -1; + } + + nla_nest_end(msg, plans); + return 0; +} + + /** * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan * @priv: Pointer to private driver data from wpa_driver_nl80211_init() * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure or if not supported */ int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval) + struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -332,11 +399,27 @@ int wpa_driver_nl80211_sched_scan(void *priv, return android_pno_start(bss, params); #endif /* ANDROID */ + if (!params->sched_scan_plans_num || + params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) { + wpa_printf(MSG_ERROR, + "nl80211: Invalid number of sched scan plans: %u", + params->sched_scan_plans_num); + return -1; + } + msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params); - if (!msg || - nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval)) + if (!msg) goto fail; + if (drv->capa.max_sched_scan_plan_iterations) { + if (nl80211_sched_scan_add_scan_plans(drv, msg, params)) + goto fail; + } else { + if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, + params->sched_scan_plans[0].interval * 1000)) + goto fail; + } + if ((drv->num_filter_ssids && (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || params->filter_rssi) { @@ -399,8 +482,7 @@ int wpa_driver_nl80211_sched_scan(void *priv, goto fail; } - wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " - "scan interval %d msec", ret, interval); + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret); fail: nlmsg_free(msg); diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index b9d213a98..699fd4f76 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -100,12 +100,10 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - u32 interval) + struct wpa_driver_scan_params *params) { if (wpa_s->driver->sched_scan) - return wpa_s->driver->sched_scan(wpa_s->drv_priv, - params, interval); + return wpa_s->driver->sched_scan(wpa_s->drv_priv, params); return -1; } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index a39922fd5..25a1011b3 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -271,14 +271,23 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, int interval) { 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, interval * 1000); + ret = wpa_drv_sched_scan(wpa_s, params); if (ret) wpa_supplicant_notify_scanning(wpa_s, 0); else wpa_s->sched_scanning = 1; + params->sched_scan_plans = NULL; + params->sched_scan_plans_num = 0; return ret; } @@ -2217,6 +2226,19 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; + if (src->sched_scan_plans_num > 0) { + params->sched_scan_plans = + os_malloc(sizeof(*src->sched_scan_plans) * + src->sched_scan_plans_num); + if (!params->sched_scan_plans) + goto failed; + + os_memcpy(params->sched_scan_plans, src->sched_scan_plans, + sizeof(*src->sched_scan_plans) * + src->sched_scan_plans_num); + params->sched_scan_plans_num = src->sched_scan_plans_num; + } + if (src->mac_addr_rand) { params->mac_addr_rand = src->mac_addr_rand; @@ -2254,6 +2276,7 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params) os_free((u8 *) params->extra_ies); os_free(params->freqs); os_free(params->filter_ssids); + os_free(params->sched_scan_plans); /* * Note: params->mac_addr_mask points to same memory allocation and