2010-10-29 12:07:24

by Juuso Oikarinen

[permalink] [raw]
Subject: [RFC PATCH] cfg80211: Reorder scan channels at 5 channel intervals

From: Juuso Oikarinen <[email protected]>

This patch reorders scan lists so that channels are placed 5 apart, where
possible. With this patch, scanning 13 channels on the 2.4GHz band is be
performed in the following order: 1, 6, 11, 5, 10, 4, 9, 3, 8, 13, 2, 7.

Each band is ordered separately, so that the benefits of the changed order is
not lost when the underlying drivers with hw-scan bundle each band into groups
for scanning.

The reasoning for this is the reduction of probe-response noise coming from
AP's on overlapping channels responding to the scan probe-requests. Essentially
this should slightly improve the number of scan results received for a scan.

Signed-off-by: Juuso Oikarinen <[email protected]>
---
net/wireless/core.h | 2 +
net/wireless/nl80211.c | 27 +++++---------
net/wireless/scan.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+), 17 deletions(-)

diff --git a/net/wireless/core.h b/net/wireless/core.h
index 6583cca..ca6464f 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -397,6 +397,8 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
enum nl80211_channel_type channel_type);

u16 cfg80211_calculate_bitrate(struct rate_info *rate);
+bool cfg80211_reorder_scan_req(struct cfg80211_scan_request *req);
+

#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c506241..980b15d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2819,26 +2819,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
return r;
}

-static int validate_scan_freqs(struct nlattr *freqs)
+static int count_scan_freqs(struct nlattr *freqs)
{
- struct nlattr *attr1, *attr2;
- int n_channels = 0, tmp1, tmp2;
+ struct nlattr *attr1;
+ int n_channels = 0, tmp1;

nla_for_each_nested(attr1, freqs, tmp1) {
n_channels++;
- /*
- * Some hardware has a limited channel list for
- * scanning, and it is pretty much nonsensical
- * to scan for a channel twice, so disallow that
- * and don't require drivers to check that the
- * channel list they get isn't longer than what
- * they can scan, as long as they can scan all
- * the channels they registered at once.
- */
- nla_for_each_nested(attr2, freqs, tmp2)
- if (attr1 != attr2 &&
- nla_get_u32(attr1) == nla_get_u32(attr2))
- return 0;
}

return n_channels;
@@ -2869,7 +2856,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return -EBUSY;

if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
- n_channels = validate_scan_freqs(
+ n_channels = count_scan_freqs(
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
if (!n_channels)
return -EINVAL;
@@ -2960,6 +2947,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)

request->n_channels = i;

+ /* reorder channels in the request and check for duplicates */
+ if (!cfg80211_reorder_scan_req(request)) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
i = 0;
if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 503ebb8..63f66c5 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -661,6 +661,86 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
}
EXPORT_SYMBOL(cfg80211_unlink_bss);

+#define CFG80211_SCAN_CHAN_INTERVAL 5
+
+bool cfg80211_reorder_scan_req(struct cfg80211_scan_request *req)
+{
+ struct ieee80211_channel *tmp;
+ int i, j;
+ int diff, min_diff, min_i, max_diff, max_i;
+
+
+ /* sort and validate channels */
+ for (i = 0; i < req->n_channels; i++) {
+ for (j = i + 1; j < req->n_channels; j++) {
+ /*
+ * Some hardware has a limited channel list for
+ * scanning, and it is pretty much nonsensical
+ * to scan for a channel twice, so disallow that
+ * and don't require drivers to check that the
+ * channel list they get isn't longer than what
+ * they can scan, as long as they can scan all
+ * the channels they registered at once.
+ */
+ if (req->channels[i]->center_freq ==
+ req->channels[j]->center_freq)
+ return false;
+
+ if (req->channels[i]->center_freq >
+ req->channels[j]->center_freq) {
+ tmp = req->channels[i];
+ req->channels[i] = req->channels[j];
+ req->channels[j] = tmp;
+ }
+ }
+ }
+
+ /*
+ * Reorder consequtive channels apart from each other
+ * for improved scan results - ordering in such a way that
+ * different bands remain grouped.
+ */
+ for (i = 1; i < req->n_channels; i++) {
+ min_i = i;
+ min_diff = -1;
+ max_i = i;
+ max_diff = -1;
+ for (j = i; j < req->n_channels; j++) {
+ int ch1 = ieee80211_frequency_to_channel(req->channels[i-1]->center_freq);
+ int ch2 = ieee80211_frequency_to_channel(req->channels[j]->center_freq);
+ diff = abs(ch1 - ch2);
+
+ if (req->channels[i-1]->band ==
+ req->channels[j]->band) {
+ if (diff == CFG80211_SCAN_CHAN_INTERVAL) {
+ min_i = j;
+ break;
+ }
+ if (diff > max_diff) {
+ max_diff = diff;
+ max_i = j;
+ }
+ if (diff >= CFG80211_SCAN_CHAN_INTERVAL &&
+ (min_diff == -1 || diff < min_diff)) {
+ min_diff = diff;
+ min_i = j;
+ }
+ }
+ }
+ if (min_i != i) {
+ tmp = req->channels[i];
+ req->channels[i] = req->channels[min_i];
+ req->channels[min_i] = tmp;
+ } else if (max_i != i) {
+ tmp = req->channels[i];
+ req->channels[i] = req->channels[max_i];
+ req->channels[max_i] = tmp;
+ }
+ }
+
+ return true;
+}
+
#ifdef CONFIG_CFG80211_WEXT
int cfg80211_wext_siwscan(struct net_device *dev,
struct iw_request_info *info,
@@ -759,6 +839,12 @@ int cfg80211_wext_siwscan(struct net_device *dev,
/* Set real number of channels specified in creq->channels[] */
creq->n_channels = i;

+ /* reorder channels in the request and check for duplicates */
+ if (!cfg80211_reorder_scan_req(creq)) {
+ err = -EINVAL;
+ goto out;
+ }
+
/* translate "Scan for SSID" request */
if (wreq) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
--
1.7.1