[RFC] Try multiple bandwidths when checking usable channels.
From: Rostislav Lisovy
Date: Fri Jun 27 2014 - 12:25:21 EST
Current code checks if the 20MHz bandwidth is allowed for
particular channel -- if it is not, the channel is disabled.
This disables usage of 5/10 MHz channels.
The new approach is that there are multiple checks for one channel --
one for each bandwidth: 5, 10, 20, 40, 80, 160 MHz (when we hit a
bandwidth that is not allowed, greater bandwidths are automaticly
disabled as well). This prevents the following scenario to happen:
The 5 MHz bandwidth channel at the very end of the band is
successfully checked which is followed by setting flags
IEEE80211_CHAN_NO_* according to the maximum bandwidth allowed by the
particular regulatory rule (which may be greater than the 5 MHz).
When someone will try to use the channel with the maximum bandwidth
allowed (e.g. 20 MHz), the resulting channel will not be in the range
of the band anymore.
Signed-off-by: Rostislav Lisovy <rostislav.lisovy@xxxxxxxxxxx>
---
include/net/cfg80211.h | 8 +-
net/wireless/reg.c | 202 +++++++++++++++++++++++++++++++------------------
2 files changed, 137 insertions(+), 73 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index e46c437..919f759 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3679,6 +3679,8 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
* freq_reg_info - get regulatory information for the given frequency
* @wiphy: the wiphy for which we want to process this rule for
* @center_freq: Frequency in KHz for which we want regulatory information for
+ * @desired_bw_khz: The bandwidth of the channel in KHz we want regulatory
+ * information for
*
* Use this function to get the regulatory rule for a specific frequency on
* a given wireless device. If the device has a specific regulatory domain
@@ -3692,9 +3694,13 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
* have a regulatory rule for a frequency range in the center_freq's band.
* See freq_in_rule_band() for our current definition of a band -- this is
* purely subjective and right now it's 802.11 specific.
+ * -EINVAL either if the channel does not fit to any of the belonging
+ * regulatory rules OR when it would possibly fit but the requested channel
+ * bandwidth is greater than the one allowed by the regulatory rule.
*/
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
- u32 center_freq);
+ u32 center_freq,
+ u32 desired_bw_khz);
/**
* reg_initiator_name - map regulatory request initiator enum to name
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 1afdf45..7a14b94 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -910,7 +910,7 @@ static u32 map_regdom_flags(u32 rd_flags)
}
static const struct ieee80211_reg_rule *
-freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
+freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz,
const struct ieee80211_regdomain *regd)
{
int i;
@@ -920,6 +920,9 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
if (!regd)
return ERR_PTR(-EINVAL);
+ if (!desired_bw_khz)
+ desired_bw_khz = MHZ_TO_KHZ(20);
+
for (i = 0; i < regd->n_reg_rules; i++) {
const struct ieee80211_reg_rule *rr;
const struct ieee80211_freq_range *fr = NULL;
@@ -935,10 +938,28 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq);
- bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
+ bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz);
+
+ /*
+ * Even if the 'center_freq' and 'bw' do fit
+ * we need to check if the required bandwidth makes
+ * sense according to the maximum allowed bandwidth
+ */
+ if (band_rule_found && bw_fits) {
+ u32 max_bandwidth_khz;
- if (band_rule_found && bw_fits)
- return rr;
+ /* Check if auto calculation requested */
+ if (rr->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth_khz =
+ reg_get_max_bandwidth(regd, rr);
+ else
+ max_bandwidth_khz = fr->max_bandwidth_khz;
+
+ if (max_bandwidth_khz > desired_bw_khz)
+ return rr;
+ else
+ return ERR_PTR(-EINVAL);
+ }
}
if (!band_rule_found)
@@ -948,13 +969,14 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
}
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
- u32 center_freq)
+ u32 center_freq,
+ u32 desired_bw_khz)
{
const struct ieee80211_regdomain *regd;
regd = reg_get_regdomain(wiphy);
- return freq_reg_info_regd(wiphy, center_freq, regd);
+ return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, regd);
}
EXPORT_SYMBOL(freq_reg_info);
@@ -1019,11 +1041,6 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
}
#endif
-/*
- * Note that right now we assume the desired channel bandwidth
- * is always 20 MHz for each individual channel (HT40 uses 20 MHz
- * per channel, the primary and the extension channel).
- */
static void handle_channel(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator,
struct ieee80211_channel *chan)
@@ -1035,41 +1052,75 @@ static void handle_channel(struct wiphy *wiphy,
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
const struct ieee80211_regdomain *regd;
- u32 max_bandwidth_khz;
+ bool check_greater_bw = 1;
+ int bw;
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
flags = chan->orig_flags;
- reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
- if (IS_ERR(reg_rule)) {
- /*
- * We will disable all channels that do not match our
- * received regulatory rule unless the hint is coming
- * from a Country IE and the Country IE had no information
- * about a band. The IEEE 802.11 spec allows for an AP
- * to send only a subset of the regulatory rules allowed,
- * so an AP in the US that only supports 2.4 GHz may only send
- * a country IE with information for the 2.4 GHz band
- * while 5 GHz is still supported.
- */
- if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- PTR_ERR(reg_rule) == -ERANGE)
- return;
+ /* Check for 5, 10, 20, 40, 80, 160 bandwidths */
+ for (bw = 5; bw < 160 && check_greater_bw; bw *= 2) {
+ reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(bw));
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
- request_wiphy && request_wiphy == wiphy &&
- request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
- REG_DBG_PRINT("Disabling freq %d MHz for good\n",
- chan->center_freq);
- chan->orig_flags |= IEEE80211_CHAN_DISABLED;
- chan->flags = chan->orig_flags;
- } else {
- REG_DBG_PRINT("Disabling freq %d MHz\n",
- chan->center_freq);
- chan->flags |= IEEE80211_CHAN_DISABLED;
+ if (IS_ERR(reg_rule) && (PTR_ERR(reg_rule) == -EINVAL)) {
+ /*
+ * Set BW limiting flags for any channel but the
+ * 5MHz one -- if the 5MHz BW does not fit, the whole
+ * channel is disabled
+ */
+ switch (bw) {
+ case 5:
+ break;
+ case 10:
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ case 20:
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ case 40:
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ case 80:
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ case 160:
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ check_greater_bw = 0;
+ }
+
+ if (!check_greater_bw)
+ break;
+ }
+
+ if (IS_ERR(reg_rule)) {
+
+ /*
+ * We will disable all channels that do not match our
+ * received regulatory rule unless the hint is coming
+ * from a Country IE and the Country IE had no information
+ * about a band. The IEEE 802.11 spec allows for an AP
+ * to send only a subset of the regulatory rules allowed,
+ * so an AP in the US that only supports 2.4 GHz may only send
+ * a country IE with information for the 2.4 GHz band
+ * while 5 GHz is still supported.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ PTR_ERR(reg_rule) == -ERANGE)
+ return;
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ REG_DBG_PRINT("Disabling freq %d MHz for good\n",
+ chan->center_freq);
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ } else {
+ REG_DBG_PRINT("Disabling freq %d MHz\n",
+ chan->center_freq);
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ }
+
+ return;
}
- return;
}
regd = reg_get_regdomain(wiphy);
@@ -1078,18 +1129,6 @@ static void handle_channel(struct wiphy *wiphy,
power_rule = ®_rule->power_rule;
freq_range = ®_rule->freq_range;
- max_bandwidth_khz = freq_range->max_bandwidth_khz;
- /* Check if auto calculation requested */
- if (reg_rule->flags & NL80211_RRF_AUTO_BW)
- max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags = IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
-
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
@@ -1495,17 +1534,48 @@ static void handle_channel_custom(struct wiphy *wiphy,
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
- u32 max_bandwidth_khz;
+ bool check_greater_bw = 1;
+ int bw;
- reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
- regd);
+ /* Check for 5, 10, 20, 40, 80, 160 bandwidths */
+ for (bw = 5; bw < 160 && check_greater_bw; bw *= 2) {
+ reg_rule = freq_reg_info_regd(wiphy,
+ MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(bw), regd);
- if (IS_ERR(reg_rule)) {
- REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
- chan->center_freq);
- chan->orig_flags |= IEEE80211_CHAN_DISABLED;
- chan->flags = chan->orig_flags;
- return;
+ if (IS_ERR(reg_rule) && (PTR_ERR(reg_rule) == -EINVAL)) {
+ /*
+ * Set BW limiting flags for any channel but the
+ * 5MHz one -- if the 5MHz BW does not fit, the whole
+ * channel is disabled
+ */
+ switch (bw) {
+ case 5:
+ break;
+ case 10:
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ case 20:
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ case 40:
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ case 80:
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ case 160:
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ check_greater_bw = 0;
+ }
+
+ if (!check_greater_bw)
+ break;
+ }
+
+ if (IS_ERR(reg_rule)) {
+ REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
+ chan->center_freq);
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ return;
+ }
}
chan_reg_rule_print_dbg(regd, chan, reg_rule);
@@ -1513,18 +1583,6 @@ static void handle_channel_custom(struct wiphy *wiphy,
power_rule = ®_rule->power_rule;
freq_range = ®_rule->freq_range;
- max_bandwidth_khz = freq_range->max_bandwidth_khz;
- /* Check if auto calculation requested */
- if (reg_rule->flags & NL80211_RRF_AUTO_BW)
- max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags = IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
-
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
chan->max_reg_power = chan->max_power =
--
2.0.0.rc4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/