Skip to content

Commit

Permalink
nl80211: allow BSS data to include CLOCK_BOOTTIME timestamp
Browse files Browse the repository at this point in the history
For location and connectivity services, userspace would often like
to know the time when the BSS was last seen. The current "last seen"
value is calculated in a way that makes it less useful, especially
if the system suspended in the meantime.

Add the ability for the driver to report a real CLOCK_BOOTTIME stamp
that can then be reported to userspace (if present).

Drivers wishing to use this must be converted to the new API to call
cfg80211_inform_bss_data() or cfg80211_inform_bss_frame_data(). They
need to ensure the reported value is accurate enough even when the
frame might have been buffered in the device (e.g. firmware.)

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
[modified to use struct, inlines]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Dmitry Shmidt authored and jmberg-intel committed Oct 13, 2015
1 parent 93f0490 commit 6e19bc4
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 60 deletions.
5 changes: 3 additions & 2 deletions Documentation/DocBook/80211.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,9 @@
!Finclude/net/cfg80211.h cfg80211_scan_request
!Finclude/net/cfg80211.h cfg80211_scan_done
!Finclude/net/cfg80211.h cfg80211_bss
!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
!Finclude/net/cfg80211.h cfg80211_inform_bss_width
!Finclude/net/cfg80211.h cfg80211_inform_bss
!Finclude/net/cfg80211.h cfg80211_inform_bss_frame_data
!Finclude/net/cfg80211.h cfg80211_inform_bss_data
!Finclude/net/cfg80211.h cfg80211_unlink_bss
!Finclude/net/cfg80211.h cfg80211_find_ie
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
Expand Down
95 changes: 76 additions & 19 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,26 @@ enum cfg80211_signal_type {
CFG80211_SIGNAL_TYPE_UNSPEC,
};

/**
* struct cfg80211_inform_bss - BSS inform data
* @chan: channel the frame was received on
* @scan_width: scan width that was used
* @signal: signal strength value, according to the wiphy's
* signal type
* @boottime_ns: timestamp (CLOCK_BOOTTIME) when the information was
* received; should match the time when the frame was actually
* received by the device (not just by the host, in case it was
* buffered on the device) and be accurate to about 10ms.
* If the frame isn't buffered, just passing the return value of
* ktime_get_boot_ns() is likely appropriate.
*/
struct cfg80211_inform_bss {
struct ieee80211_channel *chan;
enum nl80211_bss_scan_width scan_width;
s32 signal;
u64 boottime_ns;
};

/**
* struct cfg80211_bss_ie_data - BSS entry IE data
* @tsf: TSF contained in the frame that carried these IEs
Expand Down Expand Up @@ -3958,14 +3978,11 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy);

/**
* cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
*
* cfg80211_inform_bss_frame_data - inform cfg80211 of a received BSS frame
* @wiphy: the wiphy reporting the BSS
* @rx_channel: The channel the frame was received on
* @scan_width: width of the control channel
* @data: the BSS metadata
* @mgmt: the management frame (probe response or beacon)
* @len: length of the management frame
* @signal: the signal strength, type depends on the wiphy's signal_type
* @gfp: context flags
*
* This informs cfg80211 that BSS information was found and
Expand All @@ -3975,21 +3992,40 @@ void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy);
* Or %NULL on error.
*/
struct cfg80211_bss * __must_check
cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp);

static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp);
s32 signal, gfp_t gfp)
{
struct cfg80211_inform_bss data = {
.chan = rx_channel,
.scan_width = scan_width,
.signal = signal,
};

return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
}

static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
{
return cfg80211_inform_bss_width_frame(wiphy, rx_channel,
NL80211_BSS_CHAN_WIDTH_20,
mgmt, len, signal, gfp);
struct cfg80211_inform_bss data = {
.chan = rx_channel,
.scan_width = NL80211_BSS_CHAN_WIDTH_20,
.signal = signal,
};

return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
}

/**
Expand All @@ -4006,19 +4042,17 @@ enum cfg80211_bss_frame_type {
};

/**
* cfg80211_inform_bss_width - inform cfg80211 of a new BSS
* cfg80211_inform_bss_data - inform cfg80211 of a new BSS
*
* @wiphy: the wiphy reporting the BSS
* @rx_channel: The channel the frame was received on
* @scan_width: width of the control channel
* @data: the BSS metadata
* @ftype: frame type (if known)
* @bssid: the BSSID of the BSS
* @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
* @capability: the capability field sent by the peer
* @beacon_interval: the beacon interval announced by the peer
* @ie: additional IEs sent by the peer
* @ielen: length of the additional IEs
* @signal: the signal strength, type depends on the wiphy's signal_type
* @gfp: context flags
*
* This informs cfg80211 that BSS information was found and
Expand All @@ -4028,13 +4062,32 @@ enum cfg80211_bss_frame_type {
* Or %NULL on error.
*/
struct cfg80211_bss * __must_check
cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
gfp_t gfp);

static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_width(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp);
s32 signal, gfp_t gfp)
{
struct cfg80211_inform_bss data = {
.chan = rx_channel,
.scan_width = scan_width,
.signal = signal,
};

return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
capability, beacon_interval, ie, ielen,
gfp);
}

static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss(struct wiphy *wiphy,
Expand All @@ -4044,11 +4097,15 @@ cfg80211_inform_bss(struct wiphy *wiphy,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
return cfg80211_inform_bss_width(wiphy, rx_channel,
NL80211_BSS_CHAN_WIDTH_20, ftype,
bssid, tsf, capability,
beacon_interval, ie, ielen, signal,
gfp);
struct cfg80211_inform_bss data = {
.chan = rx_channel,
.scan_width = NL80211_BSS_CHAN_WIDTH_20,
.signal = signal,
};

return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
capability, beacon_interval, ie, ielen,
gfp);
}

struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
Expand Down
4 changes: 4 additions & 0 deletions include/uapi/linux/nl80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -3364,6 +3364,9 @@ enum nl80211_bss_scan_width {
* (not present if no beacon frame has been received yet)
* @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
* @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
* @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
* was last updated by a received frame. The value is expected to be
* accurate to about 10ms. (u64, nanoseconds)
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
Expand All @@ -3383,6 +3386,7 @@ enum nl80211_bss {
NL80211_BSS_CHAN_WIDTH,
NL80211_BSS_BEACON_TSF,
NL80211_BSS_PRESP_DATA,
NL80211_BSS_LAST_SEEN_BOOTTIME,

/* keep last */
__NL80211_BSS_AFTER_LAST,
Expand Down
1 change: 1 addition & 0 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ struct cfg80211_internal_bss {
struct list_head list;
struct list_head hidden_list;
struct rb_node rbn;
u64 ts_boottime;
unsigned long ts;
unsigned long refcount;
atomic_t hold;
Expand Down
5 changes: 5 additions & 0 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -6605,6 +6605,11 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
jiffies_to_msecs(jiffies - intbss->ts)))
goto nla_put_failure;

if (intbss->ts_boottime &&
nla_put_u64(msg, NL80211_BSS_LAST_SEEN_BOOTTIME,
intbss->ts_boottime))
goto nla_put_failure;

switch (rdev->wiphy.signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
Expand Down
58 changes: 30 additions & 28 deletions net/wireless/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
found->pub.signal = tmp->pub.signal;
found->pub.capability = tmp->pub.capability;
found->ts = tmp->ts;
found->ts_boottime = tmp->ts_boottime;
} else {
struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden;
Expand Down Expand Up @@ -938,14 +939,13 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
}

/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss*
cfg80211_inform_bss_width(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
struct cfg80211_bss *
cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
gfp_t gfp)
{
struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel;
Expand All @@ -957,19 +957,21 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
return NULL;

if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
(signal < 0 || signal > 100)))
(data->signal < 0 || data->signal > 100)))
return NULL;

channel = cfg80211_get_bss_channel(wiphy, ie, ielen, rx_channel);
channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan);
if (!channel)
return NULL;

memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
tmp.pub.channel = channel;
tmp.pub.scan_width = scan_width;
tmp.pub.signal = signal;
tmp.pub.scan_width = data->scan_width;
tmp.pub.signal = data->signal;
tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability;
tmp.ts_boottime = data->boottime_ns;

/*
* If we do not know here whether the IEs are from a Beacon or Probe
* Response frame, we need to pick one of the options and only use it
Expand Down Expand Up @@ -999,7 +1001,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
}
rcu_assign_pointer(tmp.pub.ies, ies);

signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
wiphy->max_adj_channel_rssi_comp;
res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
if (!res)
Expand All @@ -1019,15 +1021,15 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
EXPORT_SYMBOL(cfg80211_inform_bss_width);
EXPORT_SYMBOL(cfg80211_inform_bss_data);

/* Returned bss is reference counted and must be cleaned up appropriately. */
/* cfg80211_inform_bss_width_frame helper */
struct cfg80211_bss *
cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)

{
struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
Expand All @@ -1040,8 +1042,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable));

trace_cfg80211_inform_bss_width_frame(wiphy, rx_channel, scan_width, mgmt,
len, signal);
trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);

if (WARN_ON(!mgmt))
return NULL;
Expand All @@ -1050,14 +1051,14 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
return NULL;

if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
(signal < 0 || signal > 100)))
(data->signal < 0 || data->signal > 100)))
return NULL;

if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
return NULL;

channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
ielen, rx_channel);
ielen, data->chan);
if (!channel)
return NULL;

Expand All @@ -1077,12 +1078,13 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,

memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel;
tmp.pub.scan_width = scan_width;
tmp.pub.signal = signal;
tmp.pub.scan_width = data->scan_width;
tmp.pub.signal = data->signal;
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
tmp.ts_boottime = data->boottime_ns;

signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
wiphy->max_adj_channel_rssi_comp;
res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
if (!res)
Expand All @@ -1102,7 +1104,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);

void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
Expand Down
22 changes: 11 additions & 11 deletions net/wireless/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -2670,30 +2670,30 @@ TRACE_EVENT(cfg80211_get_bss,
__entry->privacy)
);

TRACE_EVENT(cfg80211_inform_bss_width_frame,
TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal),
TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
TRACE_EVENT(cfg80211_inform_bss_frame,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_inform_bss *data,
struct ieee80211_mgmt *mgmt, size_t len),
TP_ARGS(wiphy, data, mgmt, len),
TP_STRUCT__entry(
WIPHY_ENTRY
CHAN_ENTRY
__field(enum nl80211_bss_scan_width, scan_width)
__dynamic_array(u8, mgmt, len)
__field(s32, signal)
__field(u64, ts_boottime)
),
TP_fast_assign(
WIPHY_ASSIGN;
CHAN_ASSIGN(channel);
__entry->scan_width = scan_width;
CHAN_ASSIGN(data->chan);
__entry->scan_width = data->scan_width;
if (mgmt)
memcpy(__get_dynamic_array(mgmt), mgmt, len);
__entry->signal = signal;
__entry->signal = data->signal;
__entry->ts_boottime = data->boottime_ns;
),
TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d, tsb:%llu",
WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
__entry->signal)
__entry->signal, (unsigned long long)__entry->ts_boottime)
);

DECLARE_EVENT_CLASS(cfg80211_bss_evt,
Expand Down

0 comments on commit 6e19bc4

Please sign in to comment.