[2/4] hostapd: add supported channel BW checking infrastructure

Message ID 20180301114929.420-3-dlebed@quantenna.com
State New
Headers show
Series
  • Implement supported channel BW checking
Related show

Commit Message

Dmitrii Lebed March 1, 2018, 11:49 a.m.
From: Dmitry Lebed <dlebed@quantenna.com>

Additional checks added to common code to verify supported
BW options for each channel using nl80211-provided info.
No support of additional modes added, just additional checks.
Such checks are needed because driver/HW can declare more
strict limitations than declared in 802.11 spec, without
this patch hostapd may select unsupported channel and will
fail because linux kernel does check channel BW limitations.

Signed-off-by: Dmitry Lebed <dlebed@quantenna.com>
---
 src/ap/hw_features.c            | 45 ++++++++++---------
 src/common/hw_features_common.c | 99 +++++++++++++++++++++++++++++++++--------
 src/common/hw_features_common.h | 11 ++++-
 3 files changed, 114 insertions(+), 41 deletions(-)

--
2.16.1



This email, including its contents and any attachment(s), may contain confidential information of Quantenna Communications, Inc. and is solely for the intended recipient(s). If you may have received this in error, please contact the sender and permanently delete this email, its contents and any attachment(s).

Patch

diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 84e74eef0..cea75b85e 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -229,9 +229,6 @@  static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
        int pri_chan, sec_chan;

-       if (!iface->conf->secondary_channel)
-               return 1; /* HT40 not used */
-
        pri_chan = iface->conf->channel;
        sec_chan = pri_chan + iface->conf->secondary_channel * 4;

@@ -692,31 +689,30 @@  int hostapd_check_ht_capab(struct hostapd_iface *iface)
        return 0;
 }

-
 static int hostapd_is_usable_chan(struct hostapd_iface *iface,
                                  int channel, int primary)
 {
-       int i;
        struct hostapd_channel_data *chan;

        if (!iface->current_mode)
                return 0;

-       for (i = 0; i < iface->current_mode->num_channels; i++) {
-               chan = &iface->current_mode->channels[i];
-               if (chan->chan != channel)
-                       continue;
+       chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+       if (!chan)
+               return 0;

-               if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
-                       return 1;
+       if (primary && chan_pri_allowed(chan))
+               return 1;

-               wpa_printf(MSG_DEBUG,
-                          "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
-                          primary ? "" : "Configured HT40 secondary ",
-                          i, chan->chan, chan->flag,
-                          chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
-                          chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
-       }
+       if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+               return 1;
+
+       wpa_printf(MSG_DEBUG,
+                  "%schannel (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
+                  primary ? "" : "Configured HT40 secondary ",
+                  chan->chan, chan->flag,
+                  chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+                  chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");

        wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
                   channel, primary ? "primary" : "secondary");
@@ -727,6 +723,13 @@  static int hostapd_is_usable_chan(struct hostapd_iface *iface,
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
        int secondary_chan;
+       struct hostapd_channel_data *pri_chan;
+
+       pri_chan = hw_get_channel_chan(iface->current_mode,
+                                      iface->conf->channel, NULL);
+
+       if (!pri_chan)
+               return 0;

        if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
                return 0;
@@ -741,13 +744,15 @@  static int hostapd_is_usable_chans(struct hostapd_iface *iface)

        /* Both HT40+ and HT40- are set, pick a valid secondary channel */
        secondary_chan = iface->conf->channel + 4;
-       if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+       if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+           (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
                iface->conf->secondary_channel = 1;
                return 1;
        }

        secondary_chan = iface->conf->channel - 4;
-       if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+       if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+           (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
                iface->conf->secondary_channel = -1;
                return 1;
        }
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index db4037927..903c17742 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -84,35 +84,45 @@  int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
 }


-int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
-                             int sec_chan)
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan_num,
+                             int sec_chan_num)
 {
-       int ok, j, first;
+       int ok, first;
        int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
                          149, 157, 165, 184, 192 };
        size_t k;
+       struct hostapd_channel_data *pri_chan, *sec_chan;
+       const int ht_plus = pri_chan_num < sec_chan_num ? 1 : 0;

-       if (pri_chan == sec_chan || !sec_chan)
-               return 1; /* HT40 not used */
+       pri_chan = hw_get_channel_chan(mode, pri_chan_num, NULL);
+       if (!pri_chan)
+               return 0;
+
+       if (pri_chan_num == sec_chan_num || !sec_chan_num) {
+               if (chan_pri_allowed(pri_chan)) {
+                       return 1; /* HT40 not used */
+               } else {
+                       wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary",
+                                  pri_chan_num);
+                       return 0;
+               }
+       }
+
+       sec_chan = hw_get_channel_chan(mode, sec_chan_num, NULL);
+       if (!sec_chan)
+               return 0;

        wpa_printf(MSG_DEBUG,
                   "HT40: control channel: %d  secondary channel: %d",
-                  pri_chan, sec_chan);
+                  pri_chan_num, sec_chan_num);

        /* Verify that HT40 secondary channel is an allowed 20 MHz
         * channel */
-       ok = 0;
-       for (j = 0; j < mode->num_channels; j++) {
-               struct hostapd_channel_data *chan = &mode->channels[j];
-               if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-                   chan->chan == sec_chan) {
-                       ok = 1;
-                       break;
-               }
-       }
-       if (!ok) {
+       if ((sec_chan->flag & HOSTAPD_CHAN_DISABLED) ||
+           (ht_plus && !(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
+           (!ht_plus && !(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
                wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
-                          sec_chan);
+                          sec_chan_num);
                return 0;
        }

@@ -125,7 +135,7 @@  int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
        if (mode->mode != HOSTAPD_MODE_IEEE80211A)
                return 1;

-       first = pri_chan < sec_chan ? pri_chan : sec_chan;
+       first = pri_chan_num < sec_chan_num ? pri_chan_num : sec_chan_num;

        ok = 0;
        for (k = 0; k < ARRAY_SIZE(allowed); k++) {
@@ -136,7 +146,7 @@  int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
        }
        if (!ok) {
                wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
-                          pri_chan, sec_chan);
+                          pri_chan_num, sec_chan_num);
                return 0;
        }

@@ -553,3 +563,54 @@  int ieee80211ac_cap_check(u32 hw, u32 conf)
 }

 #endif /* CONFIG_IEEE80211AC */
+
+u32 num_chan_to_bw(int num_chans)
+{
+       switch (num_chans) {
+       case 2:
+       case 4:
+       case 8:
+               return num_chans * 20;
+       default:
+               return 20;
+       }
+}
+
+int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
+                   int ht40_plus, int pri)
+{
+       u32 bw_mask;
+
+       switch (bw) {
+       case 20:
+               bw_mask = HOSTAPD_CHAN_WIDTH_20;
+               break;
+       case 40:
+               /* HT 40 MHz support declared only for primary channel,
+                * just skip 40 MHz secondary checking */
+               if (pri && ht40_plus)
+                       bw_mask = HOSTAPD_CHAN_WIDTH_40P;
+               else if (pri && !ht40_plus)
+                       bw_mask = HOSTAPD_CHAN_WIDTH_40M;
+               else
+                       bw_mask = 0;
+               break;
+       case 80:
+               bw_mask = HOSTAPD_CHAN_WIDTH_80;
+               break;
+       case 160:
+               bw_mask = HOSTAPD_CHAN_WIDTH_160;
+               break;
+       default:
+               bw_mask = 0;
+               break;
+       }
+
+       return (chan->allowed_bw & bw_mask) == bw_mask;
+}
+
+int chan_pri_allowed(const struct hostapd_channel_data *chan)
+{
+       return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+               (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
+}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 9cddbd50e..b2ae50488 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -20,8 +20,8 @@  struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
 int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
 int hw_get_chan(struct hostapd_hw_modes *mode, int freq);

-int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
-                             int sec_chan);
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan_num,
+                             int sec_chan_num);
 void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
 int check_40mhz_5g(struct hostapd_hw_modes *mode,
                   struct wpa_scan_results *scan_res, int pri_chan,
@@ -39,4 +39,11 @@  void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
                      int disabled);
 int ieee80211ac_cap_check(u32 hw, u32 conf);

+u32 num_chan_to_bw(int num_chans);
+/* check if BW is applicable for channel */
+int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
+                   int ht40_plus, int pri);
+/* check if channel is allowed to be used as primary */
+int chan_pri_allowed(const struct hostapd_channel_data *chan);
+
 #endif /* HW_FEATURES_COMMON_H */