cfg80211: skip disabled channels on channel survey
[pandora-kernel.git] / net / wireless / nl80211.c
index 2222ce0..70cbc8c 100644 (file)
@@ -3294,8 +3294,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        struct cfg80211_scan_request *request;
-       struct cfg80211_ssid *ssid;
-       struct ieee80211_channel *channel;
        struct nlattr *attr;
        struct wiphy *wiphy;
        int err, tmp, n_ssids = 0, n_channels, i;
@@ -3342,8 +3340,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        request = kzalloc(sizeof(*request)
-                       + sizeof(*ssid) * n_ssids
-                       + sizeof(channel) * n_channels
+                       + sizeof(*request->ssids) * n_ssids
+                       + sizeof(*request->channels) * n_channels
                        + ie_len, GFP_KERNEL);
        if (!request)
                return -ENOMEM;
@@ -3408,12 +3406,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        i = 0;
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
+                       request->ssids[i].ssid_len = nla_len(attr);
                        if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
                                goto out_free;
                        }
                        memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
-                       request->ssids[i].ssid_len = nla_len(attr);
                        i++;
                }
        }
@@ -3449,8 +3447,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        struct cfg80211_sched_scan_request *request;
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
-       struct cfg80211_ssid *ssid;
-       struct ieee80211_channel *channel;
        struct nlattr *attr;
        struct wiphy *wiphy;
        int err, tmp, n_ssids = 0, n_channels, i;
@@ -3507,8 +3503,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                return -EINVAL;
 
        request = kzalloc(sizeof(*request)
-                       + sizeof(*ssid) * n_ssids
-                       + sizeof(channel) * n_channels
+                       + sizeof(*request->ssids) * n_ssids
+                       + sizeof(*request->channels) * n_channels
                        + ie_len, GFP_KERNEL);
        if (!request)
                return -ENOMEM;
@@ -3576,6 +3572,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp) {
+                       request->ssids[i].ssid_len = nla_len(attr);
                        if (request->ssids[i].ssid_len >
                            IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
@@ -3583,7 +3580,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                        }
                        memcpy(request->ssids[i].ssid, nla_data(attr),
                               nla_len(attr));
-                       request->ssids[i].ssid_len = nla_len(attr);
                        i++;
                }
        }
@@ -3758,10 +3754,6 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
        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)
@@ -3824,6 +3816,8 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        }
 
        while (1) {
+               struct ieee80211_channel *chan;
+
                res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
                                            &survey);
                if (res == -ENOENT)
@@ -3831,6 +3825,19 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                if (res)
                        goto out_err;
 
+               /* Survey without a channel doesn't make sense */
+               if (!survey.channel) {
+                       res = -EINVAL;
+                       goto out;
+               }
+
+               chan = ieee80211_get_channel(&dev->wiphy,
+                                            survey.channel->center_freq);
+               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+                       survey_idx++;
+                       continue;
+               }
+
                if (nl80211_send_survey(skb,
                                NETLINK_CB(cb->skb).pid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -4365,6 +4372,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static int nl80211_testmode_dump(struct sk_buff *skb,
+                                struct netlink_callback *cb)
+{
+       struct cfg80211_registered_device *dev;
+       int err;
+       long phy_idx;
+       void *data = NULL;
+       int data_len = 0;
+
+       if (cb->args[0]) {
+               /*
+                * 0 is a valid index, but not valid for args[0],
+                * so we need to offset by 1.
+                */
+               phy_idx = cb->args[0] - 1;
+       } else {
+               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                                 nl80211_fam.attrbuf, nl80211_fam.maxattr,
+                                 nl80211_policy);
+               if (err)
+                       return err;
+               if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
+                       return -EINVAL;
+               phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
+               if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
+                       cb->args[1] =
+                               (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
+       }
+
+       if (cb->args[1]) {
+               data = nla_data((void *)cb->args[1]);
+               data_len = nla_len((void *)cb->args[1]);
+       }
+
+       mutex_lock(&cfg80211_mutex);
+       dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
+       if (!dev) {
+               mutex_unlock(&cfg80211_mutex);
+               return -ENOENT;
+       }
+       cfg80211_lock_rdev(dev);
+       mutex_unlock(&cfg80211_mutex);
+
+       if (!dev->ops->testmode_dump) {
+               err = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       while (1) {
+               void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
+                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                          NL80211_CMD_TESTMODE);
+               struct nlattr *tmdata;
+
+               if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
+                       genlmsg_cancel(skb, hdr);
+                       break;
+               }
+
+               tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
+               if (!tmdata) {
+                       genlmsg_cancel(skb, hdr);
+                       break;
+               }
+               err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
+                                             data, data_len);
+               nla_nest_end(skb, tmdata);
+
+               if (err == -ENOBUFS || err == -ENOENT) {
+                       genlmsg_cancel(skb, hdr);
+                       break;
+               } else if (err) {
+                       genlmsg_cancel(skb, hdr);
+                       goto out_err;
+               }
+
+               genlmsg_end(skb, hdr);
+       }
+
+       err = skb->len;
+       /* see above */
+       cb->args[0] = phy_idx + 1;
+ out_err:
+       cfg80211_unlock_rdev(dev);
+       return err;
+}
+
 static struct sk_buff *
 __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
                              int approxlen, u32 pid, u32 seq, gfp_t gfp)
@@ -5662,6 +5756,7 @@ static struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_TESTMODE,
                .doit = nl80211_testmode_do,
+               .dumpit = nl80211_testmode_dump,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WIPHY |