nl80211: Add support for multiple scan plans for scheduled scan

Add 'scan plans' to driver scan parameters for scheduled scan.
Each 'scan plan' specifies the number of iterations to run the scan
request and the interval between iterations. When a scan plan
finishes (i.e., it was run for the specified number of iterations),
the next scan plan is executed. The last scan plan will run
infinitely.

The maximum number of supported scan plans, the maximum number of
iterations for a single scan plan and the maximum scan interval
are advertised by the driver.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
This commit is contained in:
Avraham Stern 2015-11-17 15:08:23 +02:00 committed by Jouni Malinen
parent f0154bf434
commit 09ea4309b6
7 changed files with 175 additions and 19 deletions

View file

@ -416,6 +416,28 @@ struct wpa_driver_scan_params {
*/ */
const u8 *mac_addr_mask; 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 * NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with * 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 */ /** Maximum number of supported active probe SSIDs for sched_scan */
int max_sched_scan_ssids; 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 */ /** Whether sched_scan (offloaded scanning) is supported */
int sched_scan_supported; int sched_scan_supported;
@ -3004,7 +3035,6 @@ struct wpa_driver_ops {
* sched_scan - Request the driver to initiate scheduled scan * sched_scan - Request the driver to initiate scheduled scan
* @priv: Private driver interface data * @priv: Private driver interface data
* @params: Scan parameters * @params: Scan parameters
* @interval: Interval between scan cycles in milliseconds
* Returns: 0 on success, -1 on failure * Returns: 0 on success, -1 on failure
* *
* This operation should be used for scheduled scan offload to * 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 * and if not provided or if it returns -1, we fall back to
* normal host-scheduled scans. * normal host-scheduled scans.
*/ */
int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
u32 interval);
/** /**
* stop_sched_scan - Request the driver to stop a scheduled scan * stop_sched_scan - Request the driver to stop a scheduled scan

View file

@ -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.mac_addr_rand_scan_supported=%d\n"
"capa.conc_capab=%u\n" "capa.conc_capab=%u\n"
"capa.max_conc_chan_2_4=%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.key_mgmt,
drv->capa.enc, drv->capa.enc,
drv->capa.auth, 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.mac_addr_rand_scan_supported,
drv->capa.conc_capab, drv->capa.conc_capab,
drv->capa.max_conc_chan_2_4, 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)) if (os_snprintf_error(end - pos, res))
return pos - buf; return pos - buf;
pos += res; pos += res;

View file

@ -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, int wpa_driver_nl80211_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params); struct wpa_driver_scan_params *params);
int wpa_driver_nl80211_sched_scan(void *priv, int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params, struct wpa_driver_scan_params *params);
u32 interval);
int wpa_driver_nl80211_stop_sched_scan(void *priv); int wpa_driver_nl80211_stop_sched_scan(void *priv);
struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);

View file

@ -499,6 +499,19 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
capa->max_sched_scan_ssids = capa->max_sched_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_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]) if (tb[NL80211_ATTR_MAX_MATCH_SETS])
capa->max_match_sets = capa->max_match_sets =
nla_get_u8(tb[NL80211_ATTR_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; 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; return 0;
} }

View file

@ -1,5 +1,6 @@
/* /*
* Driver interaction with Linux nl80211/cfg80211 - Scanning * Driver interaction with Linux nl80211/cfg80211 - Scanning
* Copyright(c) 2015 Intel Deutschland GmbH
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
* Copyright (c) 2009-2010, Atheros Communications * 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 * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
* @priv: Pointer to private driver data from wpa_driver_nl80211_init() * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
* @params: Scan parameters * @params: Scan parameters
* @interval: Interval between scan cycles in milliseconds
* Returns: 0 on success, -1 on failure or if not supported * Returns: 0 on success, -1 on failure or if not supported
*/ */
int wpa_driver_nl80211_sched_scan(void *priv, int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params, struct wpa_driver_scan_params *params)
u32 interval)
{ {
struct i802_bss *bss = priv; struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv; 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); return android_pno_start(bss, params);
#endif /* ANDROID */ #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); msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
if (!msg || if (!msg)
nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
goto fail; 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 && if ((drv->num_filter_ssids &&
(int) drv->num_filter_ssids <= drv->capa.max_match_sets) || (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
params->filter_rssi) { params->filter_rssi) {
@ -399,8 +482,7 @@ int wpa_driver_nl80211_sched_scan(void *priv,
goto fail; goto fail;
} }
wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
"scan interval %d msec", ret, interval);
fail: fail:
nlmsg_free(msg); nlmsg_free(msg);

View file

@ -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, static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params, struct wpa_driver_scan_params *params)
u32 interval)
{ {
if (wpa_s->driver->sched_scan) if (wpa_s->driver->sched_scan)
return wpa_s->driver->sched_scan(wpa_s->drv_priv, return wpa_s->driver->sched_scan(wpa_s->drv_priv, params);
params, interval);
return -1; return -1;
} }

View file

@ -271,14 +271,23 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
int interval) int interval)
{ {
int ret; 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); 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) if (ret)
wpa_supplicant_notify_scanning(wpa_s, 0); wpa_supplicant_notify_scanning(wpa_s, 0);
else else
wpa_s->sched_scanning = 1; wpa_s->sched_scanning = 1;
params->sched_scan_plans = NULL;
params->sched_scan_plans_num = 0;
return ret; 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->only_new_results = src->only_new_results;
params->low_priority = src->low_priority; 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) { if (src->mac_addr_rand) {
params->mac_addr_rand = 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((u8 *) params->extra_ies);
os_free(params->freqs); os_free(params->freqs);
os_free(params->filter_ssids); os_free(params->filter_ssids);
os_free(params->sched_scan_plans);
/* /*
* Note: params->mac_addr_mask points to same memory allocation and * Note: params->mac_addr_mask points to same memory allocation and