nl80211: allow BSS data to include CLOCK_BOOTTIME timestamp
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>
This commit is contained in:
parent
93f0490e5d
commit
6e19bc4b70
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -3975,11 +3992,26 @@ 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,
|
||||
|
@ -3987,9 +4019,13 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
|||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4006,11 +4042,10 @@ 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)
|
||||
|
@ -4018,7 +4053,6 @@ enum cfg80211_bss_frame_type {
|
|||
* @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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue