nl80211: support setting S1G channels
authorThomas Pedersen <thomas@adapt-ip.com>
Tue, 8 Sep 2020 19:03:06 +0000 (12:03 -0700)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 18 Sep 2020 10:31:17 +0000 (12:31 +0200)
S1G channels have a single width defined per frequency, so
derive it from the channel flags with
ieee80211_s1g_channel_width().

Also support setting an S1G channel where control frequency may
differ from operating, and add some basic validation to
ensure the control channel is with the operating.

Signed-off-by: Thomas Pedersen <thomas@adapt-ip.com>
Link: https://lore.kernel.org/r/20200908190323.15814-6-thomas@adapt-ip.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
net/wireless/chan.c
net/wireless/util.c

index 2a75617..44db9f8 100644 (file)
@@ -5294,6 +5294,16 @@ ieee80211_channel_to_khz(const struct ieee80211_channel *chan)
        return MHZ_TO_KHZ(chan->center_freq) + chan->freq_offset;
 }
 
+/**
+ * ieee80211_s1g_channel_width - get allowed channel width from @chan
+ *
+ * Only allowed for band NL80211_BAND_S1GHZ
+ * @chan: channel
+ * Return: The allowed channel width for this center_freq
+ */
+enum nl80211_chan_width
+ieee80211_s1g_channel_width(const struct ieee80211_channel *chan);
+
 /**
  * ieee80211_channel_to_freq_khz - convert channel number to frequency
  * @chan: channel number
index 6a6f2f2..96e24ee 100644 (file)
@@ -141,9 +141,62 @@ static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
        return true;
 }
 
+static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
+{
+       int mhz;
+
+       switch (chan_width) {
+       case NL80211_CHAN_WIDTH_1:
+               mhz = 1;
+               break;
+       case NL80211_CHAN_WIDTH_2:
+               mhz = 2;
+               break;
+       case NL80211_CHAN_WIDTH_4:
+               mhz = 4;
+               break;
+       case NL80211_CHAN_WIDTH_8:
+               mhz = 8;
+               break;
+       case NL80211_CHAN_WIDTH_16:
+               mhz = 16;
+               break;
+       case NL80211_CHAN_WIDTH_5:
+               mhz = 5;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               mhz = 10;
+               break;
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               mhz = 20;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               mhz = 40;
+               break;
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_80:
+               mhz = 80;
+               break;
+       case NL80211_CHAN_WIDTH_160:
+               mhz = 160;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -1;
+       }
+       return mhz;
+}
+
+static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
+{
+       return nl80211_chan_width_to_mhz(c->width);
+}
+
 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
 {
-       u32 control_freq;
+       u32 control_freq, oper_freq;
+       int oper_width, control_width;
 
        if (!chandef->chan)
                return false;
@@ -155,10 +208,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
 
        switch (chandef->width) {
        case NL80211_CHAN_WIDTH_1:
-       case NL80211_CHAN_WIDTH_2:
-       case NL80211_CHAN_WIDTH_4:
-       case NL80211_CHAN_WIDTH_8:
-       case NL80211_CHAN_WIDTH_16:
        case NL80211_CHAN_WIDTH_5:
        case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20:
@@ -169,6 +218,30 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
                if (chandef->center_freq2)
                        return false;
                break;
+       case NL80211_CHAN_WIDTH_2:
+       case NL80211_CHAN_WIDTH_4:
+       case NL80211_CHAN_WIDTH_8:
+       case NL80211_CHAN_WIDTH_16:
+               control_freq = ieee80211_channel_to_khz(chandef->chan);
+               oper_freq = ieee80211_chandef_to_khz(chandef);
+               control_width = nl80211_chan_width_to_mhz(
+                                       ieee80211_s1g_channel_width(
+                                                               chandef->chan));
+               oper_width = cfg80211_chandef_get_width(chandef);
+
+               if (oper_width < 0 || control_width < 0)
+                       return false;
+               if (chandef->center_freq2)
+                       return false;
+
+               if (control_freq + MHZ_TO_KHZ(control_width) / 2 >
+                   oper_freq + MHZ_TO_KHZ(oper_width) / 2)
+                       return false;
+
+               if (control_freq - MHZ_TO_KHZ(control_width) / 2 <
+                   oper_freq - MHZ_TO_KHZ(oper_width) / 2)
+                       return false;
+               break;
        case NL80211_CHAN_WIDTH_40:
                if (chandef->center_freq1 != control_freq + 10 &&
                    chandef->center_freq1 != control_freq - 10)
@@ -264,53 +337,6 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
        }
 }
 
-static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
-{
-       int width;
-
-       switch (c->width) {
-       case NL80211_CHAN_WIDTH_1:
-               width = 1;
-               break;
-       case NL80211_CHAN_WIDTH_2:
-               width = 2;
-               break;
-       case NL80211_CHAN_WIDTH_4:
-               width = 4;
-               break;
-       case NL80211_CHAN_WIDTH_8:
-               width = 8;
-               break;
-       case NL80211_CHAN_WIDTH_16:
-               width = 16;
-               break;
-       case NL80211_CHAN_WIDTH_5:
-               width = 5;
-               break;
-       case NL80211_CHAN_WIDTH_10:
-               width = 10;
-               break;
-       case NL80211_CHAN_WIDTH_20:
-       case NL80211_CHAN_WIDTH_20_NOHT:
-               width = 20;
-               break;
-       case NL80211_CHAN_WIDTH_40:
-               width = 40;
-               break;
-       case NL80211_CHAN_WIDTH_80P80:
-       case NL80211_CHAN_WIDTH_80:
-               width = 80;
-               break;
-       case NL80211_CHAN_WIDTH_160:
-               width = 160;
-               break;
-       default:
-               WARN_ON_ONCE(1);
-               return -1;
-       }
-       return width;
-}
-
 const struct cfg80211_chan_def *
 cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
                            const struct cfg80211_chan_def *c2)
index 49e7c0c..ac2bb1a 100644 (file)
@@ -111,6 +111,33 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 }
 EXPORT_SYMBOL(ieee80211_channel_to_freq_khz);
 
+enum nl80211_chan_width
+ieee80211_s1g_channel_width(const struct ieee80211_channel *chan)
+{
+       if (WARN_ON(!chan || chan->band != NL80211_BAND_S1GHZ))
+               return NL80211_CHAN_WIDTH_20_NOHT;
+
+       /*S1G defines a single allowed channel width per channel.
+        * Extract that width here.
+        */
+       if (chan->flags & IEEE80211_CHAN_1MHZ)
+               return NL80211_CHAN_WIDTH_1;
+       else if (chan->flags & IEEE80211_CHAN_2MHZ)
+               return NL80211_CHAN_WIDTH_2;
+       else if (chan->flags & IEEE80211_CHAN_4MHZ)
+               return NL80211_CHAN_WIDTH_4;
+       else if (chan->flags & IEEE80211_CHAN_8MHZ)
+               return NL80211_CHAN_WIDTH_8;
+       else if (chan->flags & IEEE80211_CHAN_16MHZ)
+               return NL80211_CHAN_WIDTH_16;
+
+       pr_err("unknown channel width for channel at %dKHz?\n",
+              ieee80211_channel_to_khz(chan));
+
+       return NL80211_CHAN_WIDTH_1;
+}
+EXPORT_SYMBOL(ieee80211_s1g_channel_width);
+
 int ieee80211_freq_khz_to_channel(u32 freq)
 {
        /* TODO: just handle MHz for now */