cfg80211: return channel noise via survey API
authorHolger Schurig <holgerschurig@gmail.com>
Wed, 11 Nov 2009 11:25:40 +0000 (12:25 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 13 Nov 2009 22:43:58 +0000 (17:43 -0500)
This patch implements the NL80211_CMD_GET_SURVEY command and an get_survey()
ops that a driver can implement. The goal of this command is to allow a
drivers to report channel survey data (e.g. channel noise, channel
occupation).

For now, only the mechanism to report back channel noise has been
implemented.

In future, there will either be a survey-trigger command --- or the existing
scan-trigger command will be enhanced. This will allow user-space to
request survey for arbitrary channels.

Note: any driver that cannot report channel noise should not report
any value at all, e.g. made-up -92 dBm.

Signed-off-by: Holger Schurig <holgerschurig@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/nl80211.h
include/net/cfg80211.h
net/wireless/nl80211.c

index d2f276de9abe81d06080a501bfe6e962113625db..45db17f81aa39f6e4b0e5700c912a21023ce622b 100644 (file)
  * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
  *     partial scan results may be available
  *
+ * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ *      or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ *     NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+ *
  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
  *     has been changed and provides details of the request information
  *     that caused the change such as who initiated the regulatory request
@@ -341,6 +346,9 @@ enum nl80211_commands {
 
        NL80211_CMD_SET_WIPHY_NETNS,
 
+       NL80211_CMD_GET_SURVEY,
+       NL80211_CMD_NEW_SURVEY_RESULTS,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -586,6 +594,10 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface
  *
+ * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of
+ *      the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
+ *      containing info as possible, see &enum survey_info.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -718,6 +730,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_4ADDR,
 
+       NL80211_ATTR_SURVEY_INFO,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -1120,6 +1134,26 @@ enum nl80211_reg_rule_flags {
        NL80211_RRF_NO_IBSS             = 1<<8,
 };
 
+/**
+ * enum nl80211_survey_info - survey information
+ *
+ * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
+ * when getting information about a survey.
+ *
+ * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
+ * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ */
+enum nl80211_survey_info {
+       __NL80211_SURVEY_INFO_INVALID,
+       NL80211_SURVEY_INFO_FREQUENCY,
+       NL80211_SURVEY_INFO_NOISE,
+
+       /* keep last */
+       __NL80211_SURVEY_INFO_AFTER_LAST,
+       NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+};
+
 /**
  * enum nl80211_mntr_flags - monitor configuration flags
  *
index 0e4c51fc63e5e8b032060e0b26a3f829b66b04a1..21710fc17eafe513ea2be0a9625df3ff24373baa 100644 (file)
@@ -234,6 +234,35 @@ struct key_params {
        u32 cipher;
 };
 
+/**
+ * enum survey_info_flags - survey information flags
+ *
+ * Used by the driver to indicate which info in &struct survey_info
+ * it has filled in during the get_survey().
+ */
+enum survey_info_flags {
+       SURVEY_INFO_NOISE_DBM = 1<<0,
+};
+
+/**
+ * struct survey_info - channel survey response
+ *
+ * Used by dump_survey() to report back per-channel survey information.
+ *
+ * @channel: the channel this survey record reports, mandatory
+ * @filled: bitflag of flags from &enum survey_info_flags
+ * @noise: channel noise in dBm. This and all following fields are
+ *     optional
+ *
+ * This structure can later be expanded with things like
+ * channel duty cycle etc.
+ */
+struct survey_info {
+       struct ieee80211_channel *channel;
+       u32 filled;
+       s8 noise;
+};
+
 /**
  * struct beacon_parameters - beacon parameters
  *
@@ -944,6 +973,8 @@ struct cfg80211_bitrate_mask {
  * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
  *     functions to adjust rfkill hw state
  *
+ * @dump_survey: get site survey information.
+ *
  * @testmode_cmd: run a test mode command
  */
 struct cfg80211_ops {
@@ -1063,6 +1094,9 @@ struct cfg80211_ops {
                                    const u8 *peer,
                                    const struct cfg80211_bitrate_mask *mask);
 
+       int     (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev,
+                       int idx, struct survey_info *info);
+
        /* some temporary stuff to finish wext */
        int     (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
                                  bool enabled, int timeout);
index 4dc139cdba5cba1086b989e44489164efef06ccf..5c8b3bfada4b8813bbe21c8b29e5c5d19ccc6c52 100644 (file)
@@ -3245,6 +3245,106 @@ static int nl80211_dump_scan(struct sk_buff *skb,
        return err;
 }
 
+static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
+                               int flags, struct net_device *dev,
+                               struct survey_info *survey)
+{
+       void *hdr;
+       struct nlattr *infoattr;
+
+       /* Survey without a channel doesn't make sense */
+       if (!survey->channel)
+               return -EINVAL;
+
+       hdr = nl80211hdr_put(msg, pid, seq, flags,
+                            NL80211_CMD_NEW_SURVEY_RESULTS);
+       if (!hdr)
+               return -ENOMEM;
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+       infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
+       if (!infoattr)
+               goto nla_put_failure;
+
+       NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
+                   survey->channel->center_freq);
+       if (survey->filled & SURVEY_INFO_NOISE_DBM)
+               NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
+                           survey->noise);
+
+       nla_nest_end(msg, infoattr);
+
+       return genlmsg_end(msg, hdr);
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nl80211_dump_survey(struct sk_buff *skb,
+                       struct netlink_callback *cb)
+{
+       struct survey_info survey;
+       struct cfg80211_registered_device *dev;
+       struct net_device *netdev;
+       int ifidx = cb->args[0];
+       int survey_idx = cb->args[1];
+       int res;
+
+       if (!ifidx)
+               ifidx = nl80211_get_ifidx(cb);
+       if (ifidx < 0)
+               return ifidx;
+       cb->args[0] = ifidx;
+
+       rtnl_lock();
+
+       netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
+       if (!netdev) {
+               res = -ENODEV;
+               goto out_rtnl;
+       }
+
+       dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
+       if (IS_ERR(dev)) {
+               res = PTR_ERR(dev);
+               goto out_rtnl;
+       }
+
+       if (!dev->ops->dump_survey) {
+               res = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       while (1) {
+               res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
+                                           &survey);
+               if (res == -ENOENT)
+                       break;
+               if (res)
+                       goto out_err;
+
+               if (nl80211_send_survey(skb,
+                               NETLINK_CB(cb->skb).pid,
+                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                               netdev,
+                               &survey) < 0)
+                       goto out;
+               survey_idx++;
+       }
+
+ out:
+       cb->args[1] = survey_idx;
+       res = skb->len;
+ out_err:
+       cfg80211_unlock_rdev(dev);
+ out_rtnl:
+       rtnl_unlock();
+
+       return res;
+}
+
 static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
 {
        return auth_type <= NL80211_AUTHTYPE_MAX;
@@ -4322,6 +4422,11 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
+       {
+               .cmd = NL80211_CMD_GET_SURVEY,
+               .policy = nl80211_policy,
+               .dumpit = nl80211_dump_survey,
+       },
 };
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
        .name = "mlme",