209 lines
6.2 KiB
Diff
209 lines
6.2 KiB
Diff
Subject: cfg80211/nl80211: implement station attribute retrieval
|
|
|
|
After a station is added to the kernel's structures, userspace
|
|
has to be able to retrieve statistics about that station, especially
|
|
whether the station was idle and how much bytes were transferred
|
|
to and from it. This adds the necessary code to nl80211.
|
|
|
|
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
|
|
|
|
---
|
|
include/linux/nl80211.h | 28 ++++++++++++++++
|
|
include/net/cfg80211.h | 35 ++++++++++++++++++++
|
|
net/wireless/nl80211.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++-
|
|
3 files changed, 144 insertions(+), 1 deletion(-)
|
|
|
|
--- everything.orig/include/linux/nl80211.h 2007-11-08 17:15:15.961529840 +0100
|
|
+++ everything/include/linux/nl80211.h 2007-11-08 17:17:00.891547364 +0100
|
|
@@ -157,6 +157,9 @@ enum nl80211_commands {
|
|
* restriction (at most %NL80211_MAX_SUPP_RATES).
|
|
* @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
|
|
* to, or the AP interface the station was originally added to to.
|
|
+ * @NL80211_ATTR_STA_STATS: statistics for a station, part of station info
|
|
+ * given for %NL80211_CMD_GET_STATION, nested attribute containing
|
|
+ * info as possible, see &enum nl80211_sta_stats.
|
|
*
|
|
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
|
* @__NL80211_ATTR_AFTER_LAST: internal use
|
|
@@ -190,6 +193,7 @@ enum nl80211_attrs {
|
|
NL80211_ATTR_STA_LISTEN_INTERVAL,
|
|
NL80211_ATTR_STA_SUPPORTED_RATES,
|
|
NL80211_ATTR_STA_VLAN,
|
|
+ NL80211_ATTR_STA_STATS,
|
|
|
|
/* add attributes here, update the policy in nl80211.c */
|
|
|
|
@@ -252,4 +256,28 @@ enum nl80211_sta_flags {
|
|
NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
|
|
};
|
|
|
|
+/**
|
|
+ * enum nl80211_sta_stats - station statistics
|
|
+ *
|
|
+ * These attribute types are used with %NL80211_ATTR_STA_STATS
|
|
+ * when getting information about a station.
|
|
+ *
|
|
+ * @__NL80211_STA_STAT_INVALID: attribute number 0 is reserved
|
|
+ * @NL80211_STA_STAT_INACTIVE_TIME: time since last activity (u32, msecs)
|
|
+ * @NL80211_STA_STAT_RX_BYTES: total received bytes (u32, from this station)
|
|
+ * @NL80211_STA_STAT_TX_BYTES: total transmitted bytes (u32, to this station)
|
|
+ * @__NL80211_STA_STAT_AFTER_LAST: internal
|
|
+ * @NL80211_STA_STAT_MAX: highest possible station stats attribute
|
|
+ */
|
|
+enum nl80211_sta_stats {
|
|
+ __NL80211_STA_STAT_INVALID,
|
|
+ NL80211_STA_STAT_INACTIVE_TIME,
|
|
+ NL80211_STA_STAT_RX_BYTES,
|
|
+ NL80211_STA_STAT_TX_BYTES,
|
|
+
|
|
+ /* keep last */
|
|
+ __NL80211_STA_STAT_AFTER_LAST,
|
|
+ NL80211_STA_STAT_MAX = __NL80211_STA_STAT_AFTER_LAST - 1
|
|
+};
|
|
+
|
|
#endif /* __LINUX_NL80211_H */
|
|
--- everything.orig/include/net/cfg80211.h 2007-11-08 17:15:15.971532444 +0100
|
|
+++ everything/include/net/cfg80211.h 2007-11-08 17:17:00.891547364 +0100
|
|
@@ -130,6 +130,39 @@ struct station_parameters {
|
|
u8 supported_rates_len;
|
|
};
|
|
|
|
+/**
|
|
+ * enum station_stats_flags - station statistics flags
|
|
+ *
|
|
+ * Used by the driver to indicate which info in &struct station_stats
|
|
+ * it has filled in during get_station().
|
|
+ *
|
|
+ * @STATION_STAT_INACTIVE_TIME: @inactive_time filled
|
|
+ * @STATION_STAT_RX_BYTES: @rx_bytes filled
|
|
+ * @STATION_STAT_TX_BYTES: @tx_bytes filled
|
|
+ */
|
|
+enum station_stats_flags {
|
|
+ STATION_STAT_INACTIVE_TIME = 1<<0,
|
|
+ STATION_STAT_RX_BYTES = 1<<1,
|
|
+ STATION_STAT_TX_BYTES = 1<<2,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct station_stats - station statistics
|
|
+ *
|
|
+ * Station information filled by driver for get_station().
|
|
+ *
|
|
+ * @filled: bitflag of flags from &enum station_stats_flags
|
|
+ * @inactive_time: time since last station activity (tx/rx) in milliseconds
|
|
+ * @rx_bytes: bytes received from this station
|
|
+ * @tx_bytes: bytes transmitted to this station
|
|
+ */
|
|
+struct station_stats {
|
|
+ u32 filled;
|
|
+ u32 inactive_time;
|
|
+ u32 rx_bytes;
|
|
+ u32 tx_bytes;
|
|
+};
|
|
+
|
|
/* from net/wireless.h */
|
|
struct wiphy;
|
|
|
|
@@ -209,6 +242,8 @@ struct cfg80211_ops {
|
|
u8 *mac);
|
|
int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
|
|
u8 *mac, struct station_parameters *params);
|
|
+ int (*get_station)(struct wiphy *wiphy, struct net_device *dev,
|
|
+ u8 *mac, struct station_stats *stats);
|
|
};
|
|
|
|
#endif /* __NET_CFG80211_H */
|
|
--- everything.orig/net/wireless/nl80211.c 2007-11-08 17:15:15.981533909 +0100
|
|
+++ everything/net/wireless/nl80211.c 2007-11-08 17:17:00.901534235 +0100
|
|
@@ -751,9 +751,89 @@ static int parse_station_flags(struct nl
|
|
return 0;
|
|
}
|
|
|
|
+static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
|
+ int flags, struct net_device *dev,
|
|
+ u8 *mac_addr, struct station_stats *stats)
|
|
+{
|
|
+ void *hdr;
|
|
+ struct nlattr *statsattr;
|
|
+
|
|
+ hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
+ if (!hdr)
|
|
+ return -1;
|
|
+
|
|
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
|
|
+
|
|
+ statsattr = nla_nest_start(msg, NL80211_ATTR_STA_STATS);
|
|
+ if (!statsattr)
|
|
+ goto nla_put_failure;
|
|
+ if (stats->filled & STATION_STAT_INACTIVE_TIME)
|
|
+ NLA_PUT_U32(msg, NL80211_STA_STAT_INACTIVE_TIME,
|
|
+ stats->inactive_time);
|
|
+ if (stats->filled & STATION_STAT_RX_BYTES)
|
|
+ NLA_PUT_U32(msg, NL80211_STA_STAT_RX_BYTES,
|
|
+ stats->rx_bytes);
|
|
+ if (stats->filled & STATION_STAT_TX_BYTES)
|
|
+ NLA_PUT_U32(msg, NL80211_STA_STAT_TX_BYTES,
|
|
+ stats->tx_bytes);
|
|
+
|
|
+ nla_nest_end(msg, statsattr);
|
|
+
|
|
+ return genlmsg_end(msg, hdr);
|
|
+
|
|
+ nla_put_failure:
|
|
+ return genlmsg_cancel(msg, hdr);
|
|
+}
|
|
+
|
|
+
|
|
static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
- return -EOPNOTSUPP;
|
|
+ struct cfg80211_registered_device *drv;
|
|
+ int err;
|
|
+ struct net_device *dev;
|
|
+ struct station_stats stats;
|
|
+ struct sk_buff *msg;
|
|
+ u8 *mac_addr = NULL;
|
|
+
|
|
+ memset(&stats, 0, sizeof(stats));
|
|
+
|
|
+ if (!info->attrs[NL80211_ATTR_MAC])
|
|
+ return -EINVAL;
|
|
+
|
|
+ mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
+
|
|
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (!drv->ops->get_station) {
|
|
+ err = -EOPNOTSUPP;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ rtnl_lock();
|
|
+ err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &stats);
|
|
+ rtnl_unlock();
|
|
+
|
|
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
+ if (!msg)
|
|
+ goto out;
|
|
+
|
|
+ if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
|
|
+ dev, mac_addr, &stats) < 0)
|
|
+ goto out_free;
|
|
+
|
|
+ err = genlmsg_unicast(msg, info->snd_pid);
|
|
+ goto out;
|
|
+
|
|
+ out_free:
|
|
+ nlmsg_free(msg);
|
|
+
|
|
+ out:
|
|
+ cfg80211_put_dev(drv);
|
|
+ dev_put(dev);
|
|
+ return err;
|
|
}
|
|
|
|
/*
|