Author: adrian
Date: Fri Jan 13 06:53:56 2017
New Revision: 312014
URL: https://svnweb.freebsd.org/changeset/base/312014

Log:
  [net80211] initial, somewhat incomplete VHT channel setup code and attach 
path.
  
  This sets up:
  
  * vht capabilities in vaps;
  * calls vht_announce to announce VHT capabilities if any;
  * sets up vht20, vht40 and vht80 channels, assuming the regulatory code
    does the right thing with 80MHz available ranges;
  * adds support to the ieee80211_add_channel_list_5ghz() code to populate
    VHT channels, as this is the API my ath10k driver is using;
  * add support for the freq1/freq2 field population and lookup that
    VHT channels require.
  
  The VHT80 code assumes that the regulatory domain already has limited VHT80
  bands to, well, 80MHz wide chunks.

Modified:
  head/sys/net80211/ieee80211.c

Modified: head/sys/net80211/ieee80211.c
==============================================================================
--- head/sys/net80211/ieee80211.c       Fri Jan 13 06:22:49 2017        
(r312013)
+++ head/sys/net80211/ieee80211.c       Fri Jan 13 06:53:56 2017        
(r312014)
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
 #include <net80211/ieee80211_superg.h>
 #endif
 #include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_vht.h>
 
 #include <net/bpf.h>
 
@@ -119,6 +120,8 @@ static const struct ieee80211_rateset ie
        { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
 #undef B
 
+static int set_vht_extchan(struct ieee80211_channel *c);
+
 /*
  * Fill in 802.11 available channel set, mark
  * all available channels as active, and pick
@@ -150,10 +153,23 @@ ieee80211_chan_init(struct ieee80211com 
                 */
                if (c->ic_ieee == 0)
                        c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
+
+               /*
+                * Setup the HT40/VHT40 upper/lower bits.
+                * The VHT80 math is done elsewhere.
+                */
                if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
                        c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
                            (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
                            c->ic_flags);
+
+               /* Update VHT math */
+               /*
+                * XXX VHT again, note that this assumes VHT80 channels
+                * are legit already
+                */
+               set_vht_extchan(c);
+
                /* default max tx power to max regulatory */
                if (c->ic_maxpower == 0)
                        c->ic_maxpower = 2*c->ic_maxregpower;
@@ -343,6 +359,7 @@ ieee80211_ifattach(struct ieee80211com *
        ieee80211_superg_attach(ic);
 #endif
        ieee80211_ht_attach(ic);
+       ieee80211_vht_attach(ic);
        ieee80211_scan_attach(ic);
        ieee80211_regdomain_attach(ic);
        ieee80211_dfs_attach(ic);
@@ -386,6 +403,7 @@ ieee80211_ifdetach(struct ieee80211com *
 #ifdef IEEE80211_SUPPORT_SUPERG
        ieee80211_superg_detach(ic);
 #endif
+       ieee80211_vht_detach(ic);
        ieee80211_ht_detach(ic);
        /* NB: must be called before ieee80211_node_detach */
        ieee80211_proto_detach(ic);
@@ -515,8 +533,15 @@ ieee80211_vap_setup(struct ieee80211com 
        vap->iv_flags_ext = ic->ic_flags_ext;
        vap->iv_flags_ven = ic->ic_flags_ven;
        vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE;
+
+       /* 11n capabilities - XXX methodize */
        vap->iv_htcaps = ic->ic_htcaps;
        vap->iv_htextcaps = ic->ic_htextcaps;
+
+       /* 11ac capabilities - XXX methodize */
+       vap->iv_vhtcaps = ic->ic_vhtcaps;
+       vap->iv_vhtextcaps = ic->ic_vhtextcaps;
+
        vap->iv_opmode = opmode;
        vap->iv_caps |= ieee80211_opcap[opmode];
        IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr);
@@ -601,6 +626,7 @@ ieee80211_vap_setup(struct ieee80211com 
        ieee80211_superg_vattach(vap);
 #endif
        ieee80211_ht_vattach(vap);
+       ieee80211_vht_vattach(vap);
        ieee80211_scan_vattach(vap);
        ieee80211_regdomain_vattach(vap);
        ieee80211_radiotap_vattach(vap);
@@ -737,6 +763,7 @@ ieee80211_vap_detach(struct ieee80211vap
 #ifdef IEEE80211_SUPPORT_SUPERG
        ieee80211_superg_vdetach(vap);
 #endif
+       ieee80211_vht_vdetach(vap);
        ieee80211_ht_vdetach(vap);
        /* NB: must be before ieee80211_node_vdetach */
        ieee80211_proto_vdetach(vap);
@@ -1081,6 +1108,110 @@ set_extchan(struct ieee80211_channel *c)
                c->ic_extieee = 0;
 }
 
+/*
+ * Populate the freq1/freq2 fields as appropriate for VHT channels.
+ *
+ * This for now uses a hard-coded list of 80MHz wide channels.
+ *
+ * For HT20/HT40, freq1 just is the centre frequency of the 40MHz
+ * wide channel we've already decided upon.
+ *
+ * For VHT80 and VHT160, there are only a small number of fixed
+ * 80/160MHz wide channels, so we just use those.
+ *
+ * This is all likely very very wrong - both the regulatory code
+ * and this code needs to ensure that all four channels are
+ * available and valid before the VHT80 (and eight for VHT160) channel
+ * is created.
+ */
+
+struct vht_chan_range {
+       uint16_t freq_start;
+       uint16_t freq_end;
+};
+
+struct vht_chan_range vht80_chan_ranges[] = {
+       { 5170, 5250 },
+       { 5250, 5330 },
+       { 5490, 5570 },
+       { 5570, 5650 },
+       { 5650, 5730 },
+       { 5735, 5815 },
+       { 0, 0, }
+};
+
+static int
+set_vht_extchan(struct ieee80211_channel *c)
+{
+       int i;
+
+       if (! IEEE80211_IS_CHAN_VHT(c)) {
+               return (0);
+       }
+
+       if (IEEE80211_IS_CHAN_VHT20(c)) {
+               c->ic_vht_ch_freq1 = c->ic_ieee;
+               return (1);
+       }
+
+       if (IEEE80211_IS_CHAN_VHT40(c)) {
+               if (IEEE80211_IS_CHAN_HT40U(c))
+                       c->ic_vht_ch_freq1 = c->ic_ieee + 2;
+               else if (IEEE80211_IS_CHAN_HT40D(c))
+                       c->ic_vht_ch_freq1 = c->ic_ieee - 2;
+               else
+                       return (0);
+               return (1);
+       }
+
+       if (IEEE80211_IS_CHAN_VHT80(c)) {
+               for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) {
+                       if (c->ic_freq >= vht80_chan_ranges[i].freq_start &&
+                           c->ic_freq < vht80_chan_ranges[i].freq_end) {
+                               int midpoint;
+
+                               midpoint = vht80_chan_ranges[i].freq_start + 40;
+                               c->ic_vht_ch_freq1 =
+                                   ieee80211_mhz2ieee(midpoint, c->ic_flags);
+                               c->ic_vht_ch_freq2 = 0;
+#if 0
+                               printf("%s: %d, freq=%d, midpoint=%d, freq1=%d, 
freq2=%d\n",
+                                   __func__, c->ic_ieee, c->ic_freq, midpoint,
+                                   c->ic_vht_ch_freq1, c->ic_vht_ch_freq2);
+#endif
+                               return (1);
+                       }
+               }
+               return (0);
+       }
+
+       printf("%s: unknown VHT channel type (ieee=%d, flags=0x%08x)\n",
+           __func__,
+           c->ic_ieee,
+           c->ic_flags);
+
+       return (0);
+}
+
+/*
+ * Return whether the current channel could possibly be a part of
+ * a VHT80 channel.
+ *
+ * This doesn't check that the whole range is in the allowed list
+ * according to regulatory.
+ */
+static int
+is_vht80_valid_freq(uint16_t freq)
+{
+       int i;
+       for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) {
+               if (freq >= vht80_chan_ranges[i].freq_start &&
+                   freq < vht80_chan_ranges[i].freq_end)
+                       return (1);
+       }
+       return (0);
+}
+
 static int
 addchan(struct ieee80211_channel chans[], int maxchans, int *nchans,
     uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags)
@@ -1090,13 +1221,25 @@ addchan(struct ieee80211_channel chans[]
        if (*nchans >= maxchans)
                return (ENOBUFS);
 
+#if 0
+       printf("%s: %d: ieee=%d, freq=%d, flags=0x%08x\n",
+           __func__,
+           *nchans,
+           ieee,
+           freq,
+           flags);
+#endif
+
        c = &chans[(*nchans)++];
        c->ic_ieee = ieee;
        c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags);
        c->ic_maxregpower = maxregpower;
        c->ic_maxpower = 2 * maxregpower;
        c->ic_flags = flags;
+       c->ic_vht_ch_freq1 = 0;
+       c->ic_vht_ch_freq2 = 0;
        set_extchan(c);
+       set_vht_extchan(c);
 
        return (0);
 }
@@ -1112,14 +1255,27 @@ copychan_prev(struct ieee80211_channel c
        if (*nchans >= maxchans)
                return (ENOBUFS);
 
+#if 0
+       printf("%s: %d: flags=0x%08x\n",
+           __func__,
+           *nchans,
+           flags);
+#endif
+
        c = &chans[(*nchans)++];
        c[0] = c[-1];
        c->ic_flags = flags;
+       c->ic_vht_ch_freq1 = 0;
+       c->ic_vht_ch_freq2 = 0;
        set_extchan(c);
+       set_vht_extchan(c);
 
        return (0);
 }
 
+/*
+ * XXX VHT-2GHz
+ */
 static void
 getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40)
 {
@@ -1140,35 +1296,73 @@ getflags_2ghz(const uint8_t bands[], uin
 }
 
 static void
-getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40)
+getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40, int vht80)
 {
        int nmodes;
 
+       /*
+        * the addchan_list function seems to expect the flags array to
+        * be in channel width order, so the VHT bits are interspersed
+        * as appropriate to maintain said order.
+        *
+        * It also assumes HT40U is before HT40D.
+        */
        nmodes = 0;
+
+       /* 20MHz */
        if (isset(bands, IEEE80211_MODE_11A))
                flags[nmodes++] = IEEE80211_CHAN_A;
        if (isset(bands, IEEE80211_MODE_11NA))
                flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20;
+       if (isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+               flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 |
+                   IEEE80211_CHAN_VHT20;
+
+       /* 40MHz */
        if (ht40) {
                flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U;
+       }
+       if (ht40 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+               flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U
+                   | IEEE80211_CHAN_VHT40U;
+       }
+       if (ht40) {
                flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D;
        }
+       if (ht40 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+               flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D
+                   | IEEE80211_CHAN_VHT40D;
+       }
+
+       /* 80MHz */
+       if (vht80 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+               flags[nmodes++] = IEEE80211_CHAN_A |
+                   IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT80;
+               flags[nmodes++] = IEEE80211_CHAN_A |
+                   IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT80;
+               }
+       }
+
+       /* XXX VHT80+80 */
+       /* XXX VHT160 */
        flags[nmodes] = 0;
 }
 
 static void
-getflags(const uint8_t bands[], uint32_t flags[], int ht40)
+getflags(const uint8_t bands[], uint32_t flags[], int ht40, int vht80)
 {
 
        flags[0] = 0;
        if (isset(bands, IEEE80211_MODE_11A) ||
-           isset(bands, IEEE80211_MODE_11NA)) {
+           isset(bands, IEEE80211_MODE_11NA) ||
+           isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
                if (isset(bands, IEEE80211_MODE_11B) ||
                    isset(bands, IEEE80211_MODE_11G) ||
-                   isset(bands, IEEE80211_MODE_11NG))
+                   isset(bands, IEEE80211_MODE_11NG) ||
+                   isset(bands, IEEE80211_MODE_VHT_2GHZ))
                        return;
 
-               getflags_5ghz(bands, flags, ht40);
+               getflags_5ghz(bands, flags, ht40, vht80);
        } else
                getflags_2ghz(bands, flags, ht40);
 }
@@ -1176,6 +1370,7 @@ getflags(const uint8_t bands[], uint32_t
 /*
  * Add one 20 MHz channel into specified channel list.
  */
+/* XXX VHT */
 int
 ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans,
     int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower,
@@ -1184,7 +1379,7 @@ ieee80211_add_channel(struct ieee80211_c
        uint32_t flags[IEEE80211_MODE_MAX];
        int i, error;
 
-       getflags(bands, flags, 0);
+       getflags(bands, flags, 0, 0);
        KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
 
        error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower,
@@ -1218,6 +1413,7 @@ findchannel(struct ieee80211_channel cha
 /*
  * Add 40 MHz channel pair into specified channel list.
  */
+/* XXX VHT */
 int
 ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans,
     int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags)
@@ -1275,11 +1471,17 @@ ieee80211_get_channel_center_freq(const 
  * For 80+80MHz channels this will be the centre of the primary
  * 80MHz channel; the secondary 80MHz channel will be center_freq2().
  */
-
 uint32_t
 ieee80211_get_channel_center_freq1(const struct ieee80211_channel *c)
 {
 
+       /*
+        * VHT - use the pre-calculated centre frequency
+        * of the given channel.
+        */
+       if (IEEE80211_IS_CHAN_VHT(c))
+               return (ieee80211_ieee2mhz(c->ic_vht_ch_freq1, c->ic_flags));
+
        if (IEEE80211_IS_CHAN_HT40U(c)) {
                return (c->ic_freq + 10);
        }
@@ -1291,12 +1493,15 @@ ieee80211_get_channel_center_freq1(const
 }
 
 /*
- * For now, no 80+80 support; this is zero.
+ * For now, no 80+80 support; it will likely always return 0.
  */
 uint32_t
 ieee80211_get_channel_center_freq2(const struct ieee80211_channel *c)
 {
 
+       if (IEEE80211_IS_CHAN_VHT(c) && (c->ic_vht_ch_freq2 != 0))
+               return (ieee80211_ieee2mhz(c->ic_vht_ch_freq2, c->ic_flags));
+
        return (0);
 }
 
@@ -1310,16 +1515,70 @@ add_chanlist(struct ieee80211_channel ch
 {
        uint16_t freq;
        int i, j, error;
+       int is_vht;
 
        for (i = 0; i < nieee; i++) {
                freq = ieee80211_ieee2mhz(ieee[i], flags[0]);
                for (j = 0; flags[j] != 0; j++) {
+                       /*
+                        * Notes:
+                        * + HT40 and VHT40 channels occur together, so
+                        *   we need to be careful that we actually allow that.
+                        * + VHT80, VHT160 will coexist with HT40/VHT40, so
+                        *   make sure it's not skipped because of the overlap
+                        *   check used for (V)HT40.
+                        */
+                       is_vht = !! (flags[j] & IEEE80211_CHAN_VHT);
+
+                       /*
+                        * Test for VHT80.
+                        * XXX This is all very broken right now.
+                        * What we /should/ do is:
+                        *
+                        * + check that the frequency is in the list of
+                        *   allowed VHT80 ranges; and
+                        * + the other 3 channels in the list are actually
+                        *   also available.
+                        */
+                       if (is_vht && flags[j] & IEEE80211_CHAN_VHT80)
+                               if (! is_vht80_valid_freq(freq))
+                                       continue;
+
+                       /*
+                        * Test for (V)HT40.
+                        *
+                        * This is also a fall through from VHT80; as we only
+                        * allow a VHT80 channel if the VHT40 combination is
+                        * also valid.  If the VHT40 form is not valid then
+                        * we certainly can't do VHT80..
+                        */
                        if (flags[j] & IEEE80211_CHAN_HT40D)
+                               /*
+                                * Can't have a "lower" channel if we are the
+                                * first channel.
+                                *
+                                * Can't have a "lower" channel if it's below/
+                                * within 20MHz of the first channel.
+                                *
+                                * Can't have a "lower" channel if the channel
+                                * below it is not 20MHz away.
+                                */
                                if (i == 0 || ieee[i] < ieee[0] + 4 ||
                                    freq - 20 !=
                                    ieee80211_ieee2mhz(ieee[i] - 4, flags[j]))
                                        continue;
                        if (flags[j] & IEEE80211_CHAN_HT40U)
+                               /*
+                                * Can't have an "upper" channel if we are
+                                * the last channel.
+                                *
+                                * Can't have an "upper" channel be above the
+                                * last channel in the list.
+                                *
+                                * Can't have an "upper" channel if the next
+                                * channel according to the math isn't 20MHz
+                                * away.  (Likely for channel 13/14.)
+                                */
                                if (i == nieee - 1 ||
                                    ieee[i] + 4 > ieee[nieee - 1] ||
                                    freq + 20 !=
@@ -1348,6 +1607,7 @@ ieee80211_add_channel_list_2ghz(struct i
 {
        uint32_t flags[IEEE80211_MODE_MAX];
 
+       /* XXX no VHT for now */
        getflags_2ghz(bands, flags, ht40);
        KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
 
@@ -1360,8 +1620,15 @@ ieee80211_add_channel_list_5ghz(struct i
     int ht40)
 {
        uint32_t flags[IEEE80211_MODE_MAX];
+       int vht80 = 0;
+
+       /*
+        * For now, assume VHT == VHT80 support as a minimum.
+        */
+       if (isset(bands, IEEE80211_MODE_VHT_5GHZ))
+               vht80 = 1;
 
-       getflags_5ghz(bands, flags, ht40);
+       getflags_5ghz(bands, flags, ht40, vht80);
        KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
 
        return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags));
@@ -1662,6 +1929,7 @@ ieee80211_announce(struct ieee80211com *
                printf("\n");
        }
        ieee80211_ht_announce(ic);
+       ieee80211_vht_announce(ic);
 }
 
 void
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to