From patchwork Fri Jun 27 16:24:54 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rostislav Lisovy X-Patchwork-Id: 4436511 Return-Path: X-Original-To: patchwork-linux-wireless@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 777EB9F319 for ; Fri, 27 Jun 2014 16:25:29 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 3FE2420384 for ; Fri, 27 Jun 2014 16:25:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6AAEA2038D for ; Fri, 27 Jun 2014 16:25:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751534AbaF0QZM (ORCPT ); Fri, 27 Jun 2014 12:25:12 -0400 Received: from mail-we0-f181.google.com ([74.125.82.181]:42826 "EHLO mail-we0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751314AbaF0QZK (ORCPT ); Fri, 27 Jun 2014 12:25:10 -0400 Received: by mail-we0-f181.google.com with SMTP id q59so5451773wes.26 for ; Fri, 27 Jun 2014 09:25:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=rYtyHu4MPpenil4KYiP3mIiioTfv25Az+vIfsf8Wdfo=; b=wdlQ8H76Ek04Cw5Z1k+67fVDVULI3sKKsPL45JgW42J3K19Ct+d2SwcySjxJSWlwlN rMxOPXWQ2PJznHyRn30aR3qONGYBShNP+GAlq6e94+P1P7HHcDzFZKWzYxYRUWljwUgv KqGyDtgkhrDh86bI6rG5wsDRz+ci/83i7ImpQi6Z5ssTC2LhUvS0B7H0X+ft8dthhjhm /igRyAVUv0u7a7WmF4eUHuhSmgOZuBDsKla+30/UReT0AdcLYOSgoGgocu9STNoLOk+s O1ku3lbSzbUrTZ4CPQp55FUwpmJhPC1s6d+I5Nvkc0SrSVhKhUXHErozcDiZwnhm4sqw 5D3Q== X-Received: by 10.194.23.135 with SMTP id m7mr27043135wjf.2.1403886308424; Fri, 27 Jun 2014 09:25:08 -0700 (PDT) Received: from c2c-vostro1.felk.cvut.cz ([147.32.86.112]) by mx.google.com with ESMTPSA id hs8sm79513291wib.10.2014.06.27.09.25.07 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 27 Jun 2014 09:25:07 -0700 (PDT) From: Rostislav Lisovy X-Google-Original-From: Rostislav Lisovy To: Johannes Berg , "John W. Linville" , linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Michal Sojka , s.sander@nordsys.de, jan-niklas.meier@volkswagen.de, Rostislav Lisovy Subject: [RFC] Try multiple bandwidths when checking usable channels. Date: Fri, 27 Jun 2014 18:24:54 +0200 Message-Id: <1403886294-713-2-git-send-email-rostislav.lisovy@fel.cvut.cz> X-Mailer: git-send-email 2.0.0.rc4 In-Reply-To: <1403886294-713-1-git-send-email-rostislav.lisovy@fel.cvut.cz> References: <1403886294-713-1-git-send-email-rostislav.lisovy@fel.cvut.cz> Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 =