From patchwork Tue Aug 14 15:55:26 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mahesh Palivela X-Patchwork-Id: 1321511 Return-Path: X-Original-To: patchwork-linux-wireless@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 19B4240211 for ; Tue, 14 Aug 2012 15:55:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755033Ab2HNPz2 (ORCPT ); Tue, 14 Aug 2012 11:55:28 -0400 Received: from hub022-nj-1.exch022.serverdata.net ([206.225.164.184]:40816 "EHLO HUB022-nj-1.exch022.serverdata.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753653Ab2HNPz1 convert rfc822-to-8bit (ORCPT ); Tue, 14 Aug 2012 11:55:27 -0400 Received: from MBX022-E1-NJ-10.exch022.domain.local ([10.240.6.62]) by HUB022-NJ-1.exch022.domain.local ([10.240.6.30]) with mapi id 14.02.0309.002; Tue, 14 Aug 2012 08:55:27 -0700 From: Mahesh Palivela To: "linux-wireless@vger.kernel.org" CC: "linville@tuxdriver.com" , "johannes@sipsolutions.net" Subject: [PATCH] cfg80211: VHT (11ac) Regulatory change Thread-Topic: [PATCH] cfg80211: VHT (11ac) Regulatory change Thread-Index: Ac16NTdWCOYDbKGERv6Vluf8FyUqDQ== Date: Tue, 14 Aug 2012 15:55:26 +0000 Message-ID: <952C5D5D0470AE4FB7D8A75C6ADC71CA0FCDD559@mbx022-e1-nj-10.exch022.domain.local> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [49.206.130.53] MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Handling of 80 MHz, 160 MHz channel bandwidths for VHT (11ac) Regulatory and setting channel flags for allowed bandwidths. Signed-off-by: Mahesh Palivela --- Sending patch second time as Stanislaw Gruszka complained its malformed. include/net/cfg80211.h | 64 +++++++++++++++-- net/wireless/reg.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++- net/wireless/reg.h | 5 ++ 3 files changed, 244 insertions(+), 8 deletions(-) Thanks, Mahesh-- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 493fa0c..552c9ed 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -96,19 +96,71 @@ enum ieee80211_band { * is not permitted. * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel * is not permitted. + * @IEEE80211_CHAN_NO_VHT80_80PLUS: 3 extension channels above this channel + * are not permitted. + * @IEEE80211_CHAN_NO_VHT80_40MINUS_60PLUS: 1 extension channel below this chan + * 2 ext chans above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT80_60MINUS_40PLUS: 2 extension channels below this chan + * 1 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT80_80MINUS: 3 extension channels below this channel + * are not permitted. + * @IEEE80211_CHAN_NO_VHT160_160PLUS: 7 extension channels above this channel + * are not permitted. + * @IEEE80211_CHAN_NO_VHT160_40MINUS_140PLUS: 1 extension channel below this chan + * 6 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT160_60MINUS_120PLUS: 2 extension channels below this chan + * 5 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT160_80MINUS_100PLUS: 3 extension channels below this chan + * 4 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT160_100MINUS_80PLUS: 4 extension channels below this chan + * 3 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT160_120MINUS_60PLUS: 5 extension channels below this chan + * 2 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT160_140MINUS_40PLUS: 6 extension channel below this chan + * 1 ext chan above this chan are not permitted. + * @IEEE80211_CHAN_NO_VHT160_160MINUS: 7 extension channels below this channel + * are not permitted. */ enum ieee80211_channel_flags { - IEEE80211_CHAN_DISABLED = 1<<0, - IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, - IEEE80211_CHAN_NO_IBSS = 1<<2, - IEEE80211_CHAN_RADAR = 1<<3, - IEEE80211_CHAN_NO_HT40PLUS = 1<<4, - IEEE80211_CHAN_NO_HT40MINUS = 1<<5, + IEEE80211_CHAN_DISABLED = 1<<0, + IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, + IEEE80211_CHAN_NO_IBSS = 1<<2, + IEEE80211_CHAN_RADAR = 1<<3, + IEEE80211_CHAN_NO_HT40PLUS = 1<<4, + IEEE80211_CHAN_NO_HT40MINUS = 1<<5, + IEEE80211_CHAN_NO_VHT80_80PLUS = 1<<6, + IEEE80211_CHAN_NO_VHT80_60PLUS_40MINUS = 1<<7, + IEEE80211_CHAN_NO_VHT80_40PLUS_60MINUS = 1<<8, + IEEE80211_CHAN_NO_VHT80_80MINUS = 1<<9, + IEEE80211_CHAN_NO_VHT160_160PLUS = 1<<10, + IEEE80211_CHAN_NO_VHT160_140PLUS_40MINUS = 1<<11, + IEEE80211_CHAN_NO_VHT160_120PLUS_60MINUS = 1<<12, + IEEE80211_CHAN_NO_VHT160_100PLUS_80MINUS = 1<<13, + IEEE80211_CHAN_NO_VHT160_80PLUS_100MINUS = 1<<14, + IEEE80211_CHAN_NO_VHT160_60PLUS_120MINUS = 1<<15, + IEEE80211_CHAN_NO_VHT160_40PLUS_140MINUS = 1<<16, + IEEE80211_CHAN_NO_VHT160_160MINUS = 1<<17, }; #define IEEE80211_CHAN_NO_HT40 \ (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) +#define IEEE80211_CHAN_NO_VHT80 \ + (IEEE80211_CHAN_NO_VHT80_80PLUS | \ + IEEE80211_CHAN_NO_VHT80_60PLUS_40MINUS | \ + IEEE80211_CHAN_NO_VHT80_40PLUS_60MINUS | \ + IEEE80211_CHAN_NO_VHT80_80MINUS) + +#define IEEE80211_CHAN_NO_VHT160 \ + (IEEE80211_CHAN_NO_VHT160_160PLUS | \ + IEEE80211_CHAN_NO_VHT160_140PLUS_40MINUS | \ + IEEE80211_CHAN_NO_VHT160_120PLUS_60MINUS | \ + IEEE80211_CHAN_NO_VHT160_100PLUS_80MINUS | \ + IEEE80211_CHAN_NO_VHT160_80PLUS_100MINUS | \ + IEEE80211_CHAN_NO_VHT160_60PLUS_120MINUS | \ + IEEE80211_CHAN_NO_VHT160_40PLUS_140MINUS | \ + IEEE80211_CHAN_NO_VHT160_160MINUS) + /** * struct ieee80211_channel - channel definition * diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2303ee7..4bb2641 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -877,7 +877,16 @@ static void handle_channel(struct wiphy *wiphy, freq_range = ®_rule->freq_range; if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags = IEEE80211_CHAN_NO_HT40; + bw_flags = IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_VHT80 | + IEEE80211_CHAN_NO_VHT160; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) + bw_flags = IEEE80211_CHAN_NO_VHT80 | + IEEE80211_CHAN_NO_VHT160; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) + bw_flags = IEEE80211_CHAN_NO_VHT160; if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && @@ -1124,6 +1133,166 @@ static void reg_process_beacons(struct wiphy *wiphy) wiphy_update_beacon_reg(wiphy); } +static bool is_vht_not_allowed(struct ieee80211_channel *chan, u32 vht_chbw) +{ + u32 flags; + if (!chan) + return true; + if (chan->flags & IEEE80211_CHAN_DISABLED) + return true; + /* This would happen when regulatory rules disallow 80/160 completely */ + + if (vht_chbw == VHT_CHBW_80) + flags = IEEE80211_CHAN_NO_VHT80; + else if (vht_chbw == VHT_CHBW_160) + flags = IEEE80211_CHAN_NO_VHT160; + else + return true; + + if (flags == (chan->flags & (flags))) + return true; + return false; +} + +static void reg_process_vht_flags_channel(struct wiphy *wiphy, + unsigned int chan_idx, + u32 vht_chbw) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channel; + struct ieee80211_channel *ext_channels[EXT_CHANS_MAX] = + {NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL}; + unsigned int i,j; + int ext_chan_offset; + u32 vht80_flags[VHT_CHBW_80/CHWIDTH_5G] = { + IEEE80211_CHAN_NO_VHT80_80MINUS, + IEEE80211_CHAN_NO_VHT80_40PLUS_60MINUS, + IEEE80211_CHAN_NO_VHT80_60PLUS_40MINUS, + IEEE80211_CHAN_NO_VHT80_80PLUS + }; + u32 vht160_flags[VHT_CHBW_160/CHWIDTH_5G] = { + IEEE80211_CHAN_NO_VHT160_160MINUS, + IEEE80211_CHAN_NO_VHT160_40PLUS_140MINUS, + IEEE80211_CHAN_NO_VHT160_60PLUS_120MINUS, + IEEE80211_CHAN_NO_VHT160_80PLUS_100MINUS, + IEEE80211_CHAN_NO_VHT160_100PLUS_80MINUS, + IEEE80211_CHAN_NO_VHT160_120PLUS_60MINUS, + IEEE80211_CHAN_NO_VHT160_140PLUS_40MINUS, + IEEE80211_CHAN_NO_VHT160_160PLUS + }; + assert_cfg80211_lock(); + + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + BUG_ON(chan_idx >= sband->n_channels); + BUG_ON((vht_chbw != VHT_CHBW_80) && (vht_chbw != VHT_CHBW_160)); + channel = &sband->channels[chan_idx]; + + if (is_vht_not_allowed(channel, vht_chbw)) { + if (vht_chbw == VHT_CHBW_80) + channel->flags |= IEEE80211_CHAN_NO_VHT80; + else if (vht_chbw == VHT_CHBW_160) + channel->flags |= IEEE80211_CHAN_NO_VHT160; + return; + } + + /* + * We need to ensure the multiple extension channels exist + * to be able to use 80 or 160 MHz bw, this finds them (or not) + * + * Start with extreme below ext channel. + * extreme below for 80 MHz BW will be 80-, 160 MHz BW will be 160- + * extreme above for 80 MHz BW will be 80+, 160 MHz BW will be 160+ + */ + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *c = &sband->channels[i]; + + /* + * start with either 80- or 160- + * ext_channels[] array stores all possible ext channels + * for a given VHT channel and channel bandwidth + */ + ext_chan_offset = (CHWIDTH_5G - vht_chbw); + for (j=0; ext_chan_offset <= (vht_chbw - CHWIDTH_5G); + ext_chan_offset += CHWIDTH_5G, j++) { + if (ext_chan_offset == 0) + continue; + if(c->center_freq == + channel->center_freq + ext_chan_offset) + ext_channels[j] = c; + } + } + + /* + * Now that we have found all possible extension channels for + * a given channel and BW, set the channel flags if valid ext channels + * exists. + * + * For 80 Mhz BW, valid ext channels are + * 80- 60- 40- 40+ 60+ 80+ + * + * For 160 MHz BW, valid ext channels are + * 160- 140- 120- 100- 80- 60- 40- 40+ 60+ 80+ 100+ 120+ 140+ 160+. + */ + for (j=0; j < (vht_chbw/CHWIDTH_5G); j++) { + if (vht_chbw == VHT_CHBW_80) { + /* + * For 80 MHz channel bandwidth, ext_channels + * 80-, 60- 40- i.e ext_channels[0], ext_channels[1], + * ext_channels[2] should exist. Next 60-, 40-, 40+ + * should exist and so on. + */ + if (is_vht_not_allowed(ext_channels[j], vht_chbw) && + is_vht_not_allowed(ext_channels[j+1], vht_chbw) && + is_vht_not_allowed(ext_channels[j+2], vht_chbw)) + channel->flags |= vht80_flags[j]; + else + channel->flags &= vht80_flags[j]; + } + + if (vht_chbw == VHT_CHBW_160) { + /* + * For 160 MHz channel bandwidth, ext_channels + * 160-, 140-, 120-, 100-, 80-, 60- 40- means + * ext_channels[0], ext_channels[1], ext_channels[3] + * ext_channels[2], ext_channels[4], ext_channels[5] + * should exist. Next 140-, 120-, 100-, 80-, 60-, + * 40-, 40+ should exist and so on. + */ + if (is_vht_not_allowed(ext_channels[j], vht_chbw) && + is_vht_not_allowed(ext_channels[j+1], vht_chbw) && + is_vht_not_allowed(ext_channels[j+2], vht_chbw) && + is_vht_not_allowed(ext_channels[j+3], vht_chbw) && + is_vht_not_allowed(ext_channels[j+4], vht_chbw) && + is_vht_not_allowed(ext_channels[j+5], vht_chbw)) + channel->flags |= vht160_flags[j]; + else + channel->flags &= vht160_flags[j]; + } + } +} + +static void reg_process_vht_flags(struct wiphy *wiphy) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + if(!wiphy->bands[IEEE80211_BAND_5GHZ]) { + /* 5GHz band is not supported, which is + * mandatory for VHT. so simply return */ + return; + } + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + reg_process_vht_flags_channel(wiphy, i, VHT_CHBW_80); + reg_process_vht_flags_channel(wiphy, i, VHT_CHBW_160); + } +} + static bool is_ht40_not_allowed(struct ieee80211_channel *chan) { if (!chan) @@ -1230,6 +1399,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); + reg_process_vht_flags(wiphy); if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } @@ -1296,7 +1466,16 @@ static void handle_channel_custom(struct wiphy *wiphy, freq_range = ®_rule->freq_range; if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags = IEEE80211_CHAN_NO_HT40; + bw_flags = IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_VHT80 | + IEEE80211_CHAN_NO_VHT160; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) + bw_flags = IEEE80211_CHAN_NO_VHT80 | + IEEE80211_CHAN_NO_VHT160; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) + bw_flags = IEEE80211_CHAN_NO_VHT160; chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f023c8a..f2ba79a 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -16,6 +16,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define VHT_CHBW_80 80 +#define VHT_CHBW_160 160 +#define CHWIDTH_5G 20 +#define EXT_CHANS_MAX (2*VHT_CHBW_160/CHWIDTH_5G)-2 + extern const struct ieee80211_regdomain *cfg80211_regdomain; bool is_world_regdom(const char *alpha2);