Handling of 80 MHz, 160 MHz channel bandwidths for VHT (11ac) Regulatory
and setting channel flags for allowed bandwidths.
Signed-off-by: Mahesh Palivela <maheshp@xxxxxxxxxxx>
---
include/net/cfg80211.h | 64 +++++++++++++++--
net/wireless/reg.c | 183
+++++++++++++++++++++++++++++++++++++++++++++++-
net/wireless/reg.h | 5 ++
3 files changed, 244 insertions(+), 8 deletions(-)
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);
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html