Commit 3b06d277 authored by Avraham Stern's avatar Avraham Stern Committed by Johannes Berg

cfg80211: Add multiple scan plans for scheduled scan

Add the option to configure multiple 'scan plans' for scheduled scan.
Each 'scan plan' defines the number of scan cycles and the interval
between scans. The scan plans are executed in the order they were
configured. The last scan plan will always run infinitely and thus
defines only the interval between scans.
The maximum number of scan plans supported by the device and the
maximum number of iterations in a single scan plan are advertised
to userspace so it can configure the scan plans appropriately.

When scheduled scan results are received there is no way to know which
scan plan is being currently executed, so there is no way to know when
the next scan iteration will start. This is not a problem, however.
The scan start timestamp is only used for flushing old scan results,
and there is no difference between flushing all results received until
the end of the previous iteration or the start of the current one,
since no results will be received in between.
Signed-off-by: default avatarAvraham Stern <avraham.stern@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent af614261
......@@ -3312,7 +3312,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
}
/* fw uses seconds, also make sure that it's >0 */
interval = max_t(u16, 1, request->interval / 1000);
interval = max_t(u16, 1, request->scan_plans[0].interval);
ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
interval, interval,
......
......@@ -629,6 +629,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
kfree(mvm->d3_resume_sram);
if (mvm->nd_config) {
kfree(mvm->nd_config->match_sets);
kfree(mvm->nd_config->scan_plans);
kfree(mvm->nd_config);
mvm->nd_config = NULL;
}
......
......@@ -1271,12 +1271,12 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
if (req->interval > U16_MAX) {
if (req->scan_plans[0].interval > U16_MAX) {
IWL_DEBUG_SCAN(mvm,
"interval value is > 16-bits, set to max possible\n");
params.interval = U16_MAX;
} else {
params.interval = req->interval / MSEC_PER_SEC;
params.interval = req->scan_plans[0].interval;
}
/* In theory, LMAC scans can handle a 32-bit delay, but since
......
......@@ -350,7 +350,8 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
cfg->bss_type = SCAN_BSS_TYPE_ANY;
/* currently NL80211 supports only a single interval */
for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
cfg->intervals[i] = cpu_to_le32(req->interval);
cfg->intervals[i] = cpu_to_le32(req->scan_plans[0].interval *
MSEC_PER_SEC);
cfg->ssid_len = 0;
ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
......
......@@ -228,13 +228,15 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
wl18xx_adjust_channels(cmd, cmd_channels);
if (c->num_short_intervals && c->long_interval &&
c->long_interval > req->interval) {
cmd->short_cycles_msec = cpu_to_le16(req->interval);
c->long_interval > req->scan_plans[0].interval * MSEC_PER_SEC) {
cmd->short_cycles_msec =
cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC);
cmd->long_cycles_msec = cpu_to_le16(c->long_interval);
cmd->short_cycles_count = c->num_short_intervals;
} else {
cmd->short_cycles_msec = 0;
cmd->long_cycles_msec = cpu_to_le16(req->interval);
cmd->long_cycles_msec =
cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC);
cmd->short_cycles_count = 0;
}
wl1271_debug(DEBUG_SCAN, "short_interval: %d, long_interval: %d, num_short: %d",
......
......@@ -5,6 +5,7 @@
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -1500,6 +1501,20 @@ struct cfg80211_match_set {
s32 rssi_thold;
};
/**
* struct cfg80211_sched_scan_plan - scan plan for scheduled scan
*
* @interval: interval between scheduled scan iterations. In seconds.
* @iterations: number of scan iterations in this scan plan. Zero means
* infinite loop.
* The last scan plan will always have this parameter set to zero,
* all other scan plans will have a finite number of iterations.
*/
struct cfg80211_sched_scan_plan {
u32 interval;
u32 iterations;
};
/**
* struct cfg80211_sched_scan_request - scheduled scan request description
*
......@@ -1507,7 +1522,6 @@ struct cfg80211_match_set {
* @n_ssids: number of SSIDs
* @n_channels: total number of channels to scan
* @scan_width: channel width for scanning
* @interval: interval between each scheduled scan cycle
* @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets
* @flags: bit field of flags controlling operation
......@@ -1526,6 +1540,9 @@ struct cfg80211_match_set {
* @mac_addr_mask: MAC address mask used with randomisation, bits that
* are 0 in the mask should be randomised, bits that are 1 should
* be taken from the @mac_addr
* @scan_plans: scan plans to be executed in this scheduled scan. Lowest
* index must be executed first.
* @n_scan_plans: number of scan plans, at least 1.
* @rcu_head: RCU callback used to free the struct
* @owner_nlportid: netlink portid of owner (if this should is a request
* owned by a particular socket)
......@@ -1539,7 +1556,6 @@ struct cfg80211_sched_scan_request {
int n_ssids;
u32 n_channels;
enum nl80211_bss_scan_width scan_width;
u32 interval;
const u8 *ie;
size_t ie_len;
u32 flags;
......@@ -1547,6 +1563,8 @@ struct cfg80211_sched_scan_request {
int n_match_sets;
s32 min_rssi_thold;
u32 delay;
struct cfg80211_sched_scan_plan *scan_plans;
int n_scan_plans;
u8 mac_addr[ETH_ALEN] __aligned(2);
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
......@@ -3076,6 +3094,12 @@ struct wiphy_vendor_command {
* include fixed IEs like supported rates
* @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled
* scans
* @max_sched_scan_plans: maximum number of scan plans (scan interval and number
* of iterations) for scheduled scan supported by the device.
* @max_sched_scan_plan_interval: maximum interval (in seconds) for a
* single scan plan supported by the device.
* @max_sched_scan_plan_iterations: maximum number of iterations for a single
* scan plan supported by the device.
* @coverage_class: current coverage class
* @fw_version: firmware version for ethtool reporting
* @hw_version: hardware version for ethtool reporting
......@@ -3183,6 +3207,9 @@ struct wiphy {
u8 max_match_sets;
u16 max_scan_ie_len;
u16 max_sched_scan_ie_len;
u32 max_sched_scan_plans;
u32 max_sched_scan_plan_interval;
u32 max_sched_scan_plan_iterations;
int n_cipher_suites;
const u32 *cipher_suites;
......
......@@ -10,6 +10,7 @@
* Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
* Copyright 2015 Intel Deutschland GmbH
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
......@@ -328,7 +329,15 @@
* partial scan results may be available
*
* @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
* intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
* intervals and certain number of cycles, as specified by
* %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
* not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
* scheduled scan will run in an infinite loop with the specified interval.
* These attributes are mutually exculsive,
* i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
* NL80211_ATTR_SCHED_SCAN_PLANS is defined.
* If for some reason scheduled scan is aborted by the driver, all scan
* plans are canceled (including scan plans that did not start yet).
* Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
* are passed, they are used in the probe requests. For
* broadcast, a broadcast SSID must be passed (ie. an empty
......@@ -1761,6 +1770,19 @@ enum nl80211_commands {
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
*
* @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
* scheduled scan supported by the device (u32), a wiphy attribute.
* @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
* a scan plan (u32), a wiphy attribute.
* @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
* a scan plan (u32), a wiphy attribute.
* @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
* Each scan plan defines the number of scan iterations and the interval
* between scans. The last scan plan will always run infinitely,
* thus it must not specify the number of iterations, only the interval
* between scans. The scan plans are executed sequentially.
* Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
......@@ -2130,6 +2152,11 @@ enum nl80211_attrs {
NL80211_ATTR_REG_INDOOR,
NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
NL80211_ATTR_SCHED_SCAN_PLANS,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
......@@ -4593,4 +4620,28 @@ enum nl80211_tdls_peer_capability {
NL80211_TDLS_PEER_WMM = 1<<2,
};
/**
* enum nl80211_sched_scan_plan - scanning plan for scheduled scan
* @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
* @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
* seconds (u32).
* @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
* scan plan (u32). The last scan plan must not specify this attribute
* because it will run infinitely. A value of zero is invalid as it will
* make the scan plan meaningless.
* @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
* currently defined
* @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
*/
enum nl80211_sched_scan_plan {
__NL80211_SCHED_SCAN_PLAN_INVALID,
NL80211_SCHED_SCAN_PLAN_INTERVAL,
NL80211_SCHED_SCAN_PLAN_ITERATIONS,
/* keep last */
__NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
NL80211_SCHED_SCAN_PLAN_MAX =
__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
};
#endif /* __LINUX_NL80211_H */
......@@ -461,6 +461,9 @@ use_default_name:
rdev->wiphy.max_num_csa_counters = 1;
rdev->wiphy.max_sched_scan_plans = 1;
rdev->wiphy.max_sched_scan_plan_interval = U32_MAX;
return &rdev->wiphy;
}
EXPORT_SYMBOL(wiphy_new_nm);
......
......@@ -479,6 +479,12 @@ nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
};
static const struct nla_policy
nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
[NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 },
[NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
};
static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct cfg80211_registered_device **rdev,
......@@ -1304,7 +1310,13 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
rdev->wiphy.max_sched_scan_ie_len) ||
nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
rdev->wiphy.max_match_sets))
rdev->wiphy.max_match_sets) ||
nla_put_u32(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
rdev->wiphy.max_sched_scan_plans) ||
nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
rdev->wiphy.max_sched_scan_plan_interval) ||
nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
rdev->wiphy.max_sched_scan_plan_iterations))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
......@@ -5974,14 +5986,100 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
static int
nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
struct cfg80211_sched_scan_request *request,
struct nlattr **attrs)
{
int tmp, err, i = 0;
struct nlattr *attr;
if (!attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
u32 interval;
/*
* If scan plans are not specified,
* %NL80211_ATTR_SCHED_SCAN_INTERVAL must be specified. In this
* case one scan plan will be set with the specified scan
* interval and infinite number of iterations.
*/
if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return -EINVAL;
interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
if (!interval)
return -EINVAL;
request->scan_plans[0].interval =
DIV_ROUND_UP(interval, MSEC_PER_SEC);
if (!request->scan_plans[0].interval)
return -EINVAL;
if (request->scan_plans[0].interval >
wiphy->max_sched_scan_plan_interval)
request->scan_plans[0].interval =
wiphy->max_sched_scan_plan_interval;
return 0;
}
nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) {
struct nlattr *plan[NL80211_SCHED_SCAN_PLAN_MAX + 1];
if (WARN_ON(i >= n_plans))
return -EINVAL;
err = nla_parse(plan, NL80211_SCHED_SCAN_PLAN_MAX,
nla_data(attr), nla_len(attr),
nl80211_plan_policy);
if (err)
return err;
if (!plan[NL80211_SCHED_SCAN_PLAN_INTERVAL])
return -EINVAL;
request->scan_plans[i].interval =
nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]);
if (!request->scan_plans[i].interval ||
request->scan_plans[i].interval >
wiphy->max_sched_scan_plan_interval)
return -EINVAL;
if (plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]) {
request->scan_plans[i].iterations =
nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]);
if (!request->scan_plans[i].iterations ||
(request->scan_plans[i].iterations >
wiphy->max_sched_scan_plan_iterations))
return -EINVAL;
} else if (i < n_plans - 1) {
/*
* All scan plans but the last one must specify
* a finite number of iterations
*/
return -EINVAL;
}
i++;
}
/*
* The last scan plan must not specify the number of
* iterations, it is supposed to run infinitely
*/
if (request->scan_plans[n_plans - 1].iterations)
return -EINVAL;
return 0;
}
static struct cfg80211_sched_scan_request *
nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
struct nlattr **attrs)
{
struct cfg80211_sched_scan_request *request;
struct nlattr *attr;
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
u32 interval;
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i, n_plans = 0;
enum ieee80211_band band;
size_t ie_len;
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
......@@ -5990,13 +6088,6 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
return ERR_PTR(-EINVAL);
if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return ERR_PTR(-EINVAL);
interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
if (interval == 0)
return ERR_PTR(-EINVAL);
if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
n_channels = validate_scan_freqs(
attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
......@@ -6060,9 +6151,37 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
if (ie_len > wiphy->max_sched_scan_ie_len)
return ERR_PTR(-EINVAL);
if (attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
/*
* NL80211_ATTR_SCHED_SCAN_INTERVAL must not be specified since
* each scan plan already specifies its own interval
*/
if (attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return ERR_PTR(-EINVAL);
nla_for_each_nested(attr,
attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp)
n_plans++;
} else {
/*
* The scan interval attribute is kept for backward
* compatibility. If no scan plans are specified and sched scan
* interval is specified, one scan plan will be set with this
* scan interval and infinite number of iterations.
*/
if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return ERR_PTR(-EINVAL);
n_plans = 1;
}
if (!n_plans || n_plans > wiphy->max_sched_scan_plans)
return ERR_PTR(-EINVAL);
request = kzalloc(sizeof(*request)
+ sizeof(*request->ssids) * n_ssids
+ sizeof(*request->match_sets) * n_match_sets
+ sizeof(*request->scan_plans) * n_plans
+ sizeof(*request->channels) * n_channels
+ ie_len, GFP_KERNEL);
if (!request)
......@@ -6090,6 +6209,18 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
}
request->n_match_sets = n_match_sets;
if (n_match_sets)
request->scan_plans = (void *)(request->match_sets +
n_match_sets);
else if (request->ie)
request->scan_plans = (void *)(request->ie + ie_len);
else if (n_ssids)
request->scan_plans = (void *)(request->ssids + n_ssids);
else
request->scan_plans = (void *)(request->channels + n_channels);
request->n_scan_plans = n_plans;
i = 0;
if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
/* user specified, bail out if channel not found */
......@@ -6252,7 +6383,10 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
request->delay =
nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
request->interval = interval;
err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs);
if (err)
goto out_free;
request->scan_start = jiffies;
return request;
......@@ -8850,7 +8984,7 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
static int nl80211_send_wowlan_nd(struct sk_buff *msg,
struct cfg80211_sched_scan_request *req)
{
struct nlattr *nd, *freqs, *matches, *match;
struct nlattr *nd, *freqs, *matches, *match, *scan_plans, *scan_plan;
int i;
if (!req)
......@@ -8860,7 +8994,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
if (!nd)
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval))
if (req->n_scan_plans == 1 &&
nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
req->scan_plans[0].interval * 1000))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
......@@ -8887,6 +9023,23 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
nla_nest_end(msg, matches);
}
scan_plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
if (!scan_plans)
return -ENOBUFS;
for (i = 0; i < req->n_scan_plans; i++) {
scan_plan = nla_nest_start(msg, i + 1);
if (!scan_plan ||
nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
req->scan_plans[i].interval) ||
(req->scan_plans[i].iterations &&
nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
req->scan_plans[i].iterations)))
return -ENOBUFS;
nla_nest_end(msg, scan_plan);
}
nla_nest_end(msg, scan_plans);
nla_nest_end(msg, nd);
return 0;
......
......@@ -266,8 +266,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->scan_start);
spin_unlock_bh(&rdev->bss_lock);
request->scan_start =
jiffies + msecs_to_jiffies(request->interval);
request->scan_start = jiffies;
}
nl80211_send_sched_scan_results(rdev, request->dev);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment