Author: adrian
Date: Fri Jan 13 07:02:04 2017
New Revision: 312015
URL: https://svnweb.freebsd.org/changeset/base/312015

Log:
  [net80211] Initial VHT node upgrade/downgrade support and initial IE parsing.
  
  This is the bulk of the magic to start enabling VHT channel negotiation.
  It is absolutely, positively not yet even a complete VHT wave-1 
implementation.
  
  * parse IEs in scan, assoc req/resp, probe req/resp;
  * break apart the channel upgrade from the HT IE parsing - do it after the
    VHT IEs are parsed;
  * (dirty! sigh) add channel width decision making in ieee80211_ht.c 
htinfo_update_chw().
    This is the main bit where negotiated channel promotion through IEs occur.
  * Shoehorn in VHT node init ,teardown, rate control, etc calls like the HT
    versions;
  * Do VHT channel adjustment where appropriate
  
  Tested:
  
  * monitor mode, ath10k port
  * STA mode, ath10k port - VHT20, VHT40, VHT80 modes
  
  TODO:
  
  * IBSS;
  * hostap;
  * (ignore mesh, wds for now);
  * finish 11n state engine - channel width change, opmode notifications, SMPS, 
etc;
  * VHT basic rate negotiation and acceptance criteria when scanning, 
associating, etc;
  * VHT control/management frame handling (group managment and operating mode 
being
    the two big ones);
  * Verify TX/RX VHT rate negotiation is actually working correctly.
  
  Whilst here, add some comments about seqno allocation and locking.  To achieve
  the full VHT rates I need to push seqno allocation into the drivers and
  finally remove the IEEE80211_TX_LOCK() I added years ago to fix issues. :/

Modified:
  head/sys/net80211/ieee80211_adhoc.c
  head/sys/net80211/ieee80211_hostap.c
  head/sys/net80211/ieee80211_ht.c
  head/sys/net80211/ieee80211_ht.h
  head/sys/net80211/ieee80211_input.c
  head/sys/net80211/ieee80211_node.c
  head/sys/net80211/ieee80211_output.c
  head/sys/net80211/ieee80211_scan_sta.c
  head/sys/net80211/ieee80211_sta.c

Modified: head/sys/net80211/ieee80211_adhoc.c
==============================================================================
--- head/sys/net80211/ieee80211_adhoc.c Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_adhoc.c Fri Jan 13 07:02:04 2017        
(r312015)
@@ -822,10 +822,14 @@ adhoc_recv_mgmt(struct ieee80211_node *n
 #if 0
                        if (scan.htcap != NULL && scan.htinfo != NULL &&
                            (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
-                               if (ieee80211_ht_updateparams(ni,
+                               ieee80211_ht_updateparams(ni,
+                                   scan.htcap, scan.htinfo));
+                               if (ieee80211_ht_updateparams_final(ni,
                                    scan.htcap, scan.htinfo))
                                        ht_state_change = 1;
                        }
+
+                       /* XXX same for VHT? */
 #endif
                        if (ni != NULL) {
                                IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);

Modified: head/sys/net80211/ieee80211_hostap.c
==============================================================================
--- head/sys/net80211/ieee80211_hostap.c        Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_hostap.c        Fri Jan 13 07:02:04 2017        
(r312015)
@@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
 #include <net80211/ieee80211_superg.h>
 #endif
 #include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_vht.h>
 
 #define        IEEE80211_RATE2MBS(r)   (((r) & IEEE80211_RATE_VAL) / 2)
 
@@ -1745,6 +1746,7 @@ hostap_recv_mgmt(struct ieee80211_node *
        struct ieee80211_frame *wh;
        uint8_t *frm, *efrm, *sfrm;
        uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap;
+       uint8_t *vhtcap, *vhtinfo;
        int reassoc, resp;
        uint8_t rate;
 
@@ -2042,6 +2044,7 @@ hostap_recv_mgmt(struct ieee80211_node *
                if (reassoc)
                        frm += 6;       /* ignore current AP info */
                ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
+               vhtcap = vhtinfo = NULL;
                sfrm = frm;
                while (efrm - frm > 1) {
                        IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
@@ -2061,6 +2064,12 @@ hostap_recv_mgmt(struct ieee80211_node *
                        case IEEE80211_ELEMID_HTCAP:
                                htcap = frm;
                                break;
+                       case IEEE80211_ELEMID_VHT_CAP:
+                               vhtcap = frm;
+                               break;
+                       case IEEE80211_ELEMID_VHT_OPMODE:
+                               vhtinfo = frm;
+                               break;
                        case IEEE80211_ELEMID_VENDOR:
                                if (iswpaoui(frm))
                                        wpa = frm;
@@ -2135,10 +2144,22 @@ hostap_recv_mgmt(struct ieee80211_node *
                        vap->iv_stats.is_rx_assoc_norate++;
                        return;
                }
+
                /*
                 * Do HT rate set handling and setup HT node state.
                 */
                ni->ni_chan = vap->iv_bss->ni_chan;
+
+               /* VHT */
+               if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
+                       /* XXX TODO; see below */
+                       printf("%s: VHT TODO!\n", __func__);
+                       ieee80211_vht_node_init(ni);
+                       ieee80211_vht_update_cap(ni, vhtcap, vhtinfo);
+               } else if (ni->ni_flags & IEEE80211_NODE_VHT)
+                       ieee80211_vht_node_cleanup(ni);
+
+               /* HT */
                if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) {
                        rate = ieee80211_setup_htrates(ni, htcap,
                                IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO |
@@ -2153,6 +2174,12 @@ hostap_recv_mgmt(struct ieee80211_node *
                        ieee80211_ht_updatehtcap(ni, htcap);
                } else if (ni->ni_flags & IEEE80211_NODE_HT)
                        ieee80211_ht_node_cleanup(ni);
+
+               /* Finally - this will use HT/VHT info to change node channel */
+               if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) {
+                       ieee80211_ht_updatehtcap_final(ni);
+               }
+
 #ifdef IEEE80211_SUPPORT_SUPERG
                /* Always do ff node cleanup; for A-MSDU */
                ieee80211_ff_node_cleanup(ni);

Modified: head/sys/net80211/ieee80211_ht.c
==============================================================================
--- head/sys/net80211/ieee80211_ht.c    Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_ht.c    Fri Jan 13 07:02:04 2017        
(r312015)
@@ -1490,52 +1490,117 @@ ieee80211_parse_htinfo(struct ieee80211_
 }
 
 /*
- * Handle 11n channel switch.  Use the received HT ie's to
- * identify the right channel to use.  If we cannot locate it
- * in the channel table then fallback to legacy operation.
+ * Handle 11n/11ac channel switch.
+ *
+ * Use the received HT/VHT ie's to identify the right channel to use.
+ * If we cannot locate it in the channel table then fallback to
+ * legacy operation.
+ *
  * Note that we use this information to identify the node's
  * channel only; the caller is responsible for insuring any
  * required channel change is done (e.g. in sta mode when
  * parsing the contents of a beacon frame).
  */
 static int
-htinfo_update_chw(struct ieee80211_node *ni, int htflags)
+htinfo_update_chw(struct ieee80211_node *ni, int htflags, int vhtflags)
 {
        struct ieee80211com *ic = ni->ni_ic;
        struct ieee80211_channel *c;
        int chanflags;
        int ret = 0;
 
-       chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
-       if (chanflags != ni->ni_chan->ic_flags) {
-               /* XXX not right for ht40- */
-               c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
-               if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
-                       /*
-                        * No HT40 channel entry in our table; fall back
-                        * to HT20 operation.  This should not happen.
-                        */
-                       c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
+       /*
+        * First step - do HT/VHT only channel lookup based on operating mode
+        * flags.  This involves masking out the VHT flags as well.
+        * Otherwise we end up doing the full channel walk each time
+        * we trigger this, which is expensive.
+        */
+       chanflags = (ni->ni_chan->ic_flags &~
+           (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | htflags | vhtflags;
+
+       if (chanflags == ni->ni_chan->ic_flags)
+               goto done;
+
+       /*
+        * If HT /or/ VHT flags have changed then check both.
+        * We need to start by picking a HT channel anyway.
+        */
+
+       c = NULL;
+       chanflags = (ni->ni_chan->ic_flags &~
+           (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | htflags;
+       /* XXX not right for ht40- */
+       c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
+       if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
+               /*
+                * No HT40 channel entry in our table; fall back
+                * to HT20 operation.  This should not happen.
+                */
+               c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
 #if 0
-                       IEEE80211_NOTE(ni->ni_vap,
-                           IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
-                           "no HT40 channel (freq %u), falling back to HT20",
-                           ni->ni_chan->ic_freq);
+               IEEE80211_NOTE(ni->ni_vap,
+                   IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+                   "no HT40 channel (freq %u), falling back to HT20",
+                   ni->ni_chan->ic_freq);
 #endif
-                       /* XXX stat */
-               }
-               if (c != NULL && c != ni->ni_chan) {
-                       IEEE80211_NOTE(ni->ni_vap,
-                           IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
-                           "switch station to HT%d channel %u/0x%x",
-                           IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
-                           c->ic_freq, c->ic_flags);
-                       ni->ni_chan = c;
-                       ret = 1;
-               }
-               /* NB: caller responsible for forcing any channel change */
+               /* XXX stat */
        }
-       /* update node's tx channel width */
+
+       /* Nothing found - leave it alone; move onto VHT */
+       if (c == NULL)
+               c = ni->ni_chan;
+
+       /*
+        * If it's non-HT, then bail out now.
+        */
+       if (! IEEE80211_IS_CHAN_HT(c)) {
+               IEEE80211_NOTE(ni->ni_vap,
+                   IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+                   "not HT; skipping VHT check (%u/0x%x)",
+                   c->ic_freq, c->ic_flags);
+               goto done;
+       }
+
+       /*
+        * Next step - look at the current VHT flags and determine
+        * if we need to upgrade.  Mask out the VHT and HT flags since
+        * the vhtflags field will already have the correct HT
+        * flags to use.
+        */
+       if (IEEE80211_CONF_VHT(ic) && ni->ni_vhtcap != 0 && vhtflags != 0) {
+               chanflags = (c->ic_flags
+                   &~ (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT))
+                   | vhtflags;
+               IEEE80211_NOTE(ni->ni_vap,
+                   IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+                   ni,
+                   "%s: VHT; chanwidth=0x%02x; vhtflags=0x%08x",
+                   __func__, ni->ni_vht_chanwidth, vhtflags);
+
+               IEEE80211_NOTE(ni->ni_vap,
+                   IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+                   ni,
+                   "%s: VHT; trying lookup for %d/0x%08x",
+                   __func__, c->ic_freq, chanflags);
+               c = ieee80211_find_channel(ic, c->ic_freq, chanflags);
+       }
+
+       /* Finally, if it's changed */
+       if (c != NULL && c != ni->ni_chan) {
+               IEEE80211_NOTE(ni->ni_vap,
+                   IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+                   "switch station to %s%d channel %u/0x%x",
+                   IEEE80211_IS_CHAN_VHT(c) ? "VHT" : "HT",
+                   IEEE80211_IS_CHAN_VHT80(c) ? 80 :
+                     (IEEE80211_IS_CHAN_HT40(c) ? 40 : 20),
+                   c->ic_freq, c->ic_flags);
+               ni->ni_chan = c;
+               ret = 1;
+       }
+       /* NB: caller responsible for forcing any channel change */
+
+done:
+       /* update node's (11n) tx channel width */
        ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20;
        return (ret);
 }
@@ -1587,15 +1652,18 @@ htcap_update_shortgi(struct ieee80211_no
 /*
  * Parse and update HT-related state extracted from
  * the HT cap and info ie's.
+ *
+ * This is called from the STA management path and
+ * the ieee80211_node_join() path.  It will take into
+ * account the IEs discovered during scanning and
+ * adjust things accordingly.
  */
-int
+void
 ieee80211_ht_updateparams(struct ieee80211_node *ni,
        const uint8_t *htcapie, const uint8_t *htinfoie)
 {
        struct ieee80211vap *vap = ni->ni_vap;
        const struct ieee80211_ie_htinfo *htinfo;
-       int htflags;
-       int ret = 0;
 
        ieee80211_parse_htcap(ni, htcapie);
        if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS)
@@ -1607,8 +1675,115 @@ ieee80211_ht_updateparams(struct ieee802
        htinfo = (const struct ieee80211_ie_htinfo *) htinfoie;
        htinfo_parse(ni, htinfo);
 
+       /*
+        * Defer the node channel change; we need to now
+        * update VHT parameters before we do it.
+        */
+
+       if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) &&
+           (vap->iv_flags_ht & IEEE80211_FHT_RIFS))
+               ni->ni_flags |= IEEE80211_NODE_RIFS;
+       else
+               ni->ni_flags &= ~IEEE80211_NODE_RIFS;
+}
+
+static uint32_t
+ieee80211_vht_get_vhtflags(struct ieee80211_node *ni, uint32_t htflags)
+{
+       struct ieee80211vap *vap = ni->ni_vap;
+       uint32_t vhtflags = 0;
+
+       vhtflags = 0;
+       if (ni->ni_flags & IEEE80211_NODE_VHT && vap->iv_flags_vht & 
IEEE80211_FVHT_VHT) {
+               if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ) &&
+                   /* XXX 2 means "160MHz and 80+80MHz", 1 means "160MHz" */
+                   (MS(vap->iv_vhtcaps,
+                    IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) >= 1) &&
+                   (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT160)) {
+                       vhtflags = IEEE80211_CHAN_VHT160;
+                       /* Mirror the HT40 flags */
+                       if (htflags == IEEE80211_CHAN_HT40U) {
+                               vhtflags |= IEEE80211_CHAN_HT40U;
+                       } else if (htflags == IEEE80211_CHAN_HT40D) {
+                               vhtflags |= IEEE80211_CHAN_HT40D;
+                       }
+               } else if ((ni->ni_vht_chanwidth == 
IEEE80211_VHT_CHANWIDTH_80P80MHZ) &&
+                   /* XXX 2 means "160MHz and 80+80MHz" */
+                   (MS(vap->iv_vhtcaps,
+                    IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) == 2) &&
+                   (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80P80)) {
+                       vhtflags = IEEE80211_CHAN_VHT80_80;
+                       /* Mirror the HT40 flags */
+                       if (htflags == IEEE80211_CHAN_HT40U) {
+                               vhtflags |= IEEE80211_CHAN_HT40U;
+                       } else if (htflags == IEEE80211_CHAN_HT40D) {
+                               vhtflags |= IEEE80211_CHAN_HT40D;
+                       }
+               } else if ((ni->ni_vht_chanwidth == 
IEEE80211_VHT_CHANWIDTH_80MHZ) &&
+                   (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80)) {
+                       vhtflags = IEEE80211_CHAN_VHT80;
+                       /* Mirror the HT40 flags */
+                       if (htflags == IEEE80211_CHAN_HT40U) {
+                               vhtflags |= IEEE80211_CHAN_HT40U;
+                       } else if (htflags == IEEE80211_CHAN_HT40D) {
+                               vhtflags |= IEEE80211_CHAN_HT40D;
+                       }
+               } else if (ni->ni_vht_chanwidth == 
IEEE80211_VHT_CHANWIDTH_USE_HT) {
+                       /* Mirror the HT40 flags */
+                       /*
+                        * XXX TODO: if ht40 is disabled, but vht40 isn't
+                        * disabled then this logic will get very, very sad.
+                        * It's quite possible the only sane thing to do is
+                        * to not have vht40 as an option, and just obey
+                        * 'ht40' as that flag.
+                        */
+                       if ((htflags == IEEE80211_CHAN_HT40U) &&
+                           (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT40)) {
+                               vhtflags = IEEE80211_CHAN_VHT40U
+                                   | IEEE80211_CHAN_HT40U;
+                       } else if (htflags == IEEE80211_CHAN_HT40D &&
+                           (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT40)) {
+                               vhtflags = IEEE80211_CHAN_VHT40D
+                                   | IEEE80211_CHAN_HT40D;
+                       } else if (htflags == IEEE80211_CHAN_HT20) {
+                               vhtflags = IEEE80211_CHAN_VHT20
+                                   | IEEE80211_CHAN_HT20;
+                       }
+               } else {
+                       vhtflags = IEEE80211_CHAN_VHT20;
+               }
+       }
+       return (vhtflags);
+}
+
+/*
+ * Final part of updating the HT parameters.
+ *
+ * This is called from the STA management path and
+ * the ieee80211_node_join() path.  It will take into
+ * account the IEs discovered during scanning and
+ * adjust things accordingly.
+ *
+ * This is done after a call to ieee80211_ht_updateparams()
+ * because it (and the upcoming VHT version of updateparams)
+ * needs to ensure everything is parsed before htinfo_update_chw()
+ * is called - which will change the channel config for the
+ * node for us.
+ */
+int
+ieee80211_ht_updateparams_final(struct ieee80211_node *ni,
+       const uint8_t *htcapie, const uint8_t *htinfoie)
+{
+       struct ieee80211vap *vap = ni->ni_vap;
+       const struct ieee80211_ie_htinfo *htinfo;
+       int htflags, vhtflags;
+       int ret = 0;
+
+       htinfo = (const struct ieee80211_ie_htinfo *) htinfoie;
+
        htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ?
            IEEE80211_CHAN_HT20 : 0;
+
        /* NB: honor operating mode constraint */
        if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
            (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) {
@@ -1617,14 +1792,16 @@ ieee80211_ht_updateparams(struct ieee802
                else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
                        htflags = IEEE80211_CHAN_HT40D;
        }
-       if (htinfo_update_chw(ni, htflags))
-               ret = 1;
 
-       if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) &&
-           (vap->iv_flags_ht & IEEE80211_FHT_RIFS))
-               ni->ni_flags |= IEEE80211_NODE_RIFS;
-       else
-               ni->ni_flags &= ~IEEE80211_NODE_RIFS;
+       /*
+        * VHT flags - do much the same; check whether VHT is available
+        * and if so, what our ideal channel use would be based on our
+        * capabilities and the (pre-parsed) VHT info IE.
+        */
+       vhtflags = ieee80211_vht_get_vhtflags(ni, htflags);
+
+       if (htinfo_update_chw(ni, htflags, vhtflags))
+               ret = 1;
 
        return (ret);
 }
@@ -1632,17 +1809,31 @@ ieee80211_ht_updateparams(struct ieee802
 /*
  * Parse and update HT-related state extracted from the HT cap ie
  * for a station joining an HT BSS.
+ *
+ * This is called from the hostap path for each station.
  */
 void
 ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie)
 {
        struct ieee80211vap *vap = ni->ni_vap;
-       int htflags;
 
        ieee80211_parse_htcap(ni, htcapie);
        if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS)
                htcap_update_mimo_ps(ni);
        htcap_update_shortgi(ni);
+}
+
+/*
+ * Called once HT and VHT capabilities are parsed in hostap mode -
+ * this will adjust the channel configuration of the given node
+ * based on the configuration and capabilities.
+ */
+void
+ieee80211_ht_updatehtcap_final(struct ieee80211_node *ni)
+{
+       struct ieee80211vap *vap = ni->ni_vap;
+       int htflags;
+       int vhtflags;
 
        /* NB: honor operating mode constraint */
        /* XXX 40 MHz intolerant */
@@ -1655,7 +1846,14 @@ ieee80211_ht_updatehtcap(struct ieee8021
                else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan))
                        htflags = IEEE80211_CHAN_HT40D;
        }
-       (void) htinfo_update_chw(ni, htflags);
+       /*
+        * VHT flags - do much the same; check whether VHT is available
+        * and if so, what our ideal channel use would be based on our
+        * capabilities and the (pre-parsed) VHT info IE.
+        */
+       vhtflags = ieee80211_vht_get_vhtflags(ni, htflags);
+
+       (void) htinfo_update_chw(ni, htflags, vhtflags);
 }
 
 /*
@@ -2135,6 +2333,7 @@ ht_recv_action_ht_txchwidth(struct ieee8
            "%s: HT txchwidth, width %d%s",
            __func__, chw, ni->ni_chw != chw ? "*" : "");
        if (chw != ni->ni_chw) {
+               /* XXX does this need to change the ht40 station count? */
                ni->ni_chw = chw;
                /* XXX notify on change */
        }
@@ -2229,6 +2428,10 @@ ieee80211_ampdu_request(struct ieee80211
 
        dialogtoken = (tokens+1) % 63;          /* XXX */
        tid = tap->txa_tid;
+
+       /*
+        * XXX TODO: This is racy with any other parallel TX going on. :(
+        */
        tap->txa_start = ni->ni_txseqs[tid];
 
        args[0] = dialogtoken;

Modified: head/sys/net80211/ieee80211_ht.h
==============================================================================
--- head/sys/net80211/ieee80211_ht.h    Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_ht.h    Fri Jan 13 07:02:04 2017        
(r312015)
@@ -201,9 +201,12 @@ void       ieee80211_htprot_update(struct ieee
 void   ieee80211_ht_timeout(struct ieee80211com *);
 void   ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
 void   ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
-int    ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *,
+void   ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *,
                const uint8_t *);
+int    ieee80211_ht_updateparams_final(struct ieee80211_node *,
+           const uint8_t *, const uint8_t *);
 void   ieee80211_ht_updatehtcap(struct ieee80211_node *, const uint8_t *);
+void   ieee80211_ht_updatehtcap_final(struct ieee80211_node *);
 int    ieee80211_ampdu_request(struct ieee80211_node *,
                struct ieee80211_tx_ampdu *);
 void   ieee80211_ampdu_stop(struct ieee80211_node *,

Modified: head/sys/net80211/ieee80211_input.c
==============================================================================
--- head/sys/net80211/ieee80211_input.c Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_input.c Fri Jan 13 07:02:04 2017        
(r312015)
@@ -494,6 +494,8 @@ ieee80211_parse_beacon(struct ieee80211_
        scan->status = 0;
        /*
         * beacon/probe response frame format
+        *
+        * XXX Update from 802.11-2012 - eg where HT is
         *      [8] time stamp
         *      [2] beacon interval
         *      [2] capability information
@@ -508,6 +510,8 @@ ieee80211_parse_beacon(struct ieee80211_
         *      [tlv] WPA or RSN
         *      [tlv] HT capabilities
         *      [tlv] HT information
+        *      [tlv] VHT capabilities
+        *      [tlv] VHT information
         *      [tlv] Atheros capabilities
         *      [tlv] Mesh ID
         *      [tlv] Mesh Configuration
@@ -585,6 +589,12 @@ ieee80211_parse_beacon(struct ieee80211_
                case IEEE80211_ELEMID_HTCAP:
                        scan->htcap = frm;
                        break;
+               case IEEE80211_ELEMID_VHT_CAP:
+                       scan->vhtcap = frm;
+                       break;
+               case IEEE80211_ELEMID_VHT_OPMODE:
+                       scan->vhtopmode = frm;
+                       break;
                case IEEE80211_ELEMID_RSN:
                        scan->rsn = frm;
                        break;
@@ -718,6 +728,19 @@ ieee80211_parse_beacon(struct ieee80211_
                         sizeof(struct ieee80211_ie_htinfo)-2,
                     scan->htinfo = NULL);
        }
+
+       /* Process VHT IEs */
+       if (scan->vhtcap != NULL) {
+               IEEE80211_VERIFY_LENGTH(scan->vhtcap[1],
+                   sizeof(struct ieee80211_ie_vhtcap) - 2,
+                   scan->vhtcap = NULL);
+       }
+       if (scan->vhtopmode != NULL) {
+               IEEE80211_VERIFY_LENGTH(scan->vhtopmode[1],
+                   sizeof(struct ieee80211_ie_vht_operation) - 2,
+                   scan->vhtopmode = NULL);
+       }
+
        return scan->status;
 }
 
@@ -838,6 +861,9 @@ ieee80211_parse_action(struct ieee80211_
                }
                break;
 #endif
+       case IEEE80211_ACTION_CAT_VHT:
+               printf("%s: TODO: VHT handling!\n", __func__);
+               break;
        }
        return 0;
 }

Modified: head/sys/net80211/ieee80211_node.c
==============================================================================
--- head/sys/net80211/ieee80211_node.c  Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_node.c  Fri Jan 13 07:02:04 2017        
(r312015)
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
 #include <net80211/ieee80211_wds.h>
 #include <net80211/ieee80211_mesh.h>
 #include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_vht.h>
 
 #include <net/bpf.h>
 
@@ -412,7 +413,11 @@ ieee80211_create_ibss(struct ieee80211va
        /* XXX TODO: other bits and pieces - eg fast-frames? */
 
        /* If we're an 11n channel then initialise the 11n bits */
-       if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
+       if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
+               /* XXX what else? */
+               ieee80211_ht_node_init(ni);
+               ieee80211_vht_node_init(ni);
+       } else if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
                /* XXX what else? */
                ieee80211_ht_node_init(ni);
        }
@@ -708,9 +713,42 @@ gethtadjustflags(struct ieee80211com *ic
 }
 
 /*
+ * Calculate VHT channel promotion flags for all vaps.
+ * This assumes ni_chan have been setup for each vap.
+ */
+static int
+getvhtadjustflags(struct ieee80211com *ic)
+{
+       struct ieee80211vap *vap;
+       int flags;
+
+       flags = 0;
+       /* XXX locking */
+       TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+               if (vap->iv_state < IEEE80211_S_RUN)
+                       continue;
+               switch (vap->iv_opmode) {
+               case IEEE80211_M_WDS:
+               case IEEE80211_M_STA:
+               case IEEE80211_M_AHDEMO:
+               case IEEE80211_M_HOSTAP:
+               case IEEE80211_M_IBSS:
+               case IEEE80211_M_MBSS:
+                       flags |= ieee80211_vhtchanflags(vap->iv_bss->ni_chan);
+                       break;
+               default:
+                       break;
+               }
+       }
+       return flags;
+}
+
+/*
  * Check if the current channel needs to change based on whether
  * any vap's are using HT20/HT40.  This is used to sync the state
  * of ic_curchan after a channel width change on a running vap.
+ *
+ * Same applies for VHT.
  */
 void
 ieee80211_sync_curchan(struct ieee80211com *ic)
@@ -718,6 +756,8 @@ ieee80211_sync_curchan(struct ieee80211c
        struct ieee80211_channel *c;
 
        c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, 
gethtadjustflags(ic));
+       c = ieee80211_vht_adjust_channel(ic, c, getvhtadjustflags(ic));
+
        if (c != ic->ic_curchan) {
                ic->ic_curchan = c;
                ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
@@ -743,10 +783,23 @@ ieee80211_setupcurchan(struct ieee80211c
                 * set of running vap's.  This assumes we are called
                 * after ni_chan is setup for each vap.
                 */
+               /* XXX VHT? */
                /* NB: this assumes IEEE80211_FHT_USEHT40 > IEEE80211_FHT_HT */
                if (flags > ieee80211_htchanflags(c))
                        c = ieee80211_ht_adjust_channel(ic, c, flags);
        }
+
+       /*
+        * VHT promotion - this will at least promote to VHT20/40
+        * based on what HT has done; it may further promote the
+        * channel to VHT80 or above.
+        */
+       if (ic->ic_vhtcaps != 0) {
+               int flags = getvhtadjustflags(ic);
+               if (flags > ieee80211_vhtchanflags(c))
+                       c = ieee80211_vht_adjust_channel(ic, c, flags);
+       }
+
        ic->ic_bsschan = ic->ic_curchan = c;
        ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
        ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
@@ -849,6 +902,7 @@ ieee80211_sta_join(struct ieee80211vap *
 {
        struct ieee80211com *ic = vap->iv_ic;
        struct ieee80211_node *ni;
+       int do_ht = 0;
 
        ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr);
        if (ni == NULL) {
@@ -896,9 +950,13 @@ ieee80211_sta_join(struct ieee80211vap *
                if (ni->ni_ies.tdma_ie != NULL)
                        ieee80211_parse_tdma(ni, ni->ni_ies.tdma_ie);
 #endif
+               if (ni->ni_ies.vhtcap_ie != NULL)
+                       ieee80211_parse_vhtcap(ni, ni->ni_ies.vhtcap_ie);
+               if (ni->ni_ies.vhtopmode_ie != NULL)
+                       ieee80211_parse_vhtopmode(ni, ni->ni_ies.vhtopmode_ie);
 
-               /* XXX parse VHT IEs */
                /* XXX parse BSSLOAD IE */
+               /* XXX parse TXPWRENV IE */
                /* XXX parse APCHANREP IE */
        }
 
@@ -926,10 +984,43 @@ ieee80211_sta_join(struct ieee80211vap *
                ieee80211_ht_updateparams(ni,
                    ni->ni_ies.htcap_ie,
                    ni->ni_ies.htinfo_ie);
+               do_ht = 1;
+       }
+
+       /*
+        * Setup VHT state for this node if it's available.
+        * Same as the above.
+        *
+        * For now, don't allow 2GHz VHT operation.
+        */
+       if (ni->ni_ies.vhtopmode_ie != NULL &&
+           ni->ni_ies.vhtcap_ie != NULL &&
+           vap->iv_flags_vht & IEEE80211_FVHT_VHT) {
+               if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
+                       printf("%s: BSS %6D: 2GHz channel, VHT info; 
ignoring\n",
+                           __func__,
+                           ni->ni_macaddr,
+                           ":");
+               } else {
+                       ieee80211_vht_node_init(ni);
+                       ieee80211_vht_updateparams(ni,
+                           ni->ni_ies.vhtcap_ie,
+                           ni->ni_ies.vhtopmode_ie);
+                       ieee80211_setup_vht_rates(ni, ni->ni_ies.vhtcap_ie,
+                           ni->ni_ies.vhtopmode_ie);
+                       do_ht = 1;
+               }
+       }
+
+       /* Finally do the node channel change */
+       if (do_ht) {
+               ieee80211_ht_updateparams_final(ni, ni->ni_ies.htcap_ie,
+                   ni->ni_ies.htinfo_ie);
                ieee80211_setup_htrates(ni, ni->ni_ies.htcap_ie,
                    IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
                ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie);
        }
+
        /* XXX else check for ath FF? */
        /* XXX QoS? Difficult given that WME config is specific to a master */
 
@@ -1102,8 +1193,10 @@ node_cleanup(struct ieee80211_node *ni)
                    "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
        }
        /*
-        * Cleanup any HT-related state.
+        * Cleanup any VHT and HT-related state.
         */
+       if (ni->ni_flags & IEEE80211_NODE_VHT)
+               ieee80211_vht_node_cleanup(ni);
        if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_ht_node_cleanup(ni);
 #ifdef IEEE80211_SUPPORT_SUPERG
@@ -1423,6 +1516,7 @@ ieee80211_node_create_wds(struct ieee802
                if (vap->iv_flags & IEEE80211_F_FF)
                        ni->ni_flags |= IEEE80211_NODE_FF;
 #endif
+               /* XXX VHT */
                if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
                    (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
                        /*
@@ -1431,6 +1525,9 @@ ieee80211_node_create_wds(struct ieee802
                         * ni_chan will be adjusted to an HT channel.
                         */
                        ieee80211_ht_wds_init(ni);
+                       if (vap->iv_flags_vht & IEEE80211_FVHT_VHT) {
+                               printf("%s: TODO: vht_wds_init\n", __func__);
+                       }
                } else {
                        struct ieee80211_channel *c = ni->ni_chan;
                        /*
@@ -1638,7 +1735,7 @@ ieee80211_init_neighbor(struct ieee80211
        const struct ieee80211_frame *wh,
        const struct ieee80211_scanparams *sp)
 {
-       int do_ht_setup = 0;
+       int do_ht_setup = 0, do_vht_setup = 0;
 
        ni->ni_esslen = sp->ssid[1];
        memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
@@ -1670,11 +1767,23 @@ ieee80211_init_neighbor(struct ieee80211
                if (ni->ni_ies.htinfo_ie != NULL)
                        ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie);
 
+               if (ni->ni_ies.vhtcap_ie != NULL)
+                       ieee80211_parse_vhtcap(ni, ni->ni_ies.vhtcap_ie);
+               if (ni->ni_ies.vhtopmode_ie != NULL)
+                       ieee80211_parse_vhtopmode(ni, ni->ni_ies.vhtopmode_ie);
+
                if ((ni->ni_ies.htcap_ie != NULL) &&
                    (ni->ni_ies.htinfo_ie != NULL) &&
                    (ni->ni_vap->iv_flags_ht & IEEE80211_FHT_HT)) {
                        do_ht_setup = 1;
                }
+
+               if ((ni->ni_ies.vhtcap_ie != NULL) &&
+                   (ni->ni_ies.vhtopmode_ie != NULL) &&
+                   (ni->ni_vap->iv_flags_vht & IEEE80211_FVHT_VHT)) {
+                       do_vht_setup = 1;
+               }
+
        }
 
        /* NB: must be after ni_chan is setup */
@@ -1692,15 +1801,40 @@ ieee80211_init_neighbor(struct ieee80211
                ieee80211_ht_updateparams(ni,
                    ni->ni_ies.htcap_ie,
                    ni->ni_ies.htinfo_ie);
+
+               if (do_vht_setup) {
+                       if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
+                               printf("%s: BSS %6D: 2GHz channel, VHT info; 
ignoring\n",
+                                   __func__,
+                                   ni->ni_macaddr,
+                                   ":");
+                       } else {
+                               ieee80211_vht_node_init(ni);
+                               ieee80211_vht_updateparams(ni,
+                                   ni->ni_ies.vhtcap_ie,
+                                   ni->ni_ies.vhtopmode_ie);
+                               ieee80211_setup_vht_rates(ni,
+                                   ni->ni_ies.vhtcap_ie,
+                                   ni->ni_ies.vhtopmode_ie);
+                       }
+               }
+
+               /*
+                * Finally do the channel upgrade/change based
+                * on the HT/VHT configuration.
+                */
+               ieee80211_ht_updateparams_final(ni, ni->ni_ies.htcap_ie,
+                   ni->ni_ies.htinfo_ie);
                ieee80211_setup_htrates(ni,
                    ni->ni_ies.htcap_ie,
                    IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
                ieee80211_setup_basic_htrates(ni,
                    ni->ni_ies.htinfo_ie);
+
                ieee80211_node_setuptxparms(ni);
                ieee80211_ratectl_node_init(ni);
 
-               /* Reassociate; we're now 11n */
+               /* Reassociate; we're now 11n/11ac */
                /*
                 * XXX TODO: this is the wrong thing to do -
                 * we're calling it with isnew=1 so the ath(4)
@@ -2365,6 +2499,7 @@ ieee80211_node_timeout(void *arg)
                IEEE80211_LOCK(ic);
                ieee80211_erp_timeout(ic);
                ieee80211_ht_timeout(ic);
+               ieee80211_vht_timeout(ic);
                IEEE80211_UNLOCK(ic);
        }
        callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
@@ -2462,8 +2597,12 @@ ieee80211_dump_node(struct ieee80211_nod
        printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n",
                ni->ni_htcap, ni->ni_htparam,
                ni->ni_htctlchan, ni->ni_ht2ndchan);
-       printf("\thtopmode %x htstbc %x chw %u\n",
+       printf("\thtopmode %x htstbc %x htchw %u\n",
                ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw);
+       printf("\tvhtcap %x freq1 %d freq2 %d vhtbasicmcs %x\n",
+               ni->ni_vhtcap, (int) ni->ni_vht_chan1, (int) ni->ni_vht_chan2,
+               (int) ni->ni_vht_basicmcs);
+       /* XXX VHT state */
 }
 
 void
@@ -2594,6 +2733,8 @@ ieee80211_node_join(struct ieee80211_nod
 
                if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
                        ieee80211_ht_node_join(ni);
+               if (IEEE80211_IS_CHAN_VHT(ic->ic_bsschan))
+                       ieee80211_vht_node_join(ni);
                if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
                    IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
                        ieee80211_node_join_11g(ni);
@@ -2603,6 +2744,9 @@ ieee80211_node_join(struct ieee80211_nod
        } else
                newassoc = 0;
 
+       /*
+        * XXX VHT - should log VHT channel width, etc
+        */
        IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
            "station associated at aid %d: %s preamble, %s slot 
time%s%s%s%s%s%s%s%s",
            IEEE80211_NODE_AID(ni),
@@ -2610,6 +2754,7 @@ ieee80211_node_join(struct ieee80211_nod
            ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long",
            ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "",
            ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+           /* XXX update for VHT string */
            ni->ni_flags & IEEE80211_NODE_HT ?
                (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "",
            ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
@@ -2774,6 +2919,8 @@ ieee80211_node_leave(struct ieee80211_no
        vap->iv_sta_assoc--;
        ic->ic_sta_assoc--;
 
+       if (IEEE80211_IS_CHAN_VHT(ic->ic_bsschan))
+               ieee80211_vht_node_leave(ni);
        if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
                ieee80211_ht_node_leave(ni);
        if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&

Modified: head/sys/net80211/ieee80211_output.c
==============================================================================
--- head/sys/net80211/ieee80211_output.c        Fri Jan 13 06:53:56 2017        
(r312014)
+++ head/sys/net80211/ieee80211_output.c        Fri Jan 13 07:02:04 2017        
(r312015)
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
 #endif
 #include <net80211/ieee80211_wds.h>
 #include <net80211/ieee80211_mesh.h>
+#include <net80211/ieee80211_vht.h>
 
 #if defined(INET) || defined(INET6)
 #include <netinet/in.h> 
@@ -764,6 +765,16 @@ ieee80211_send_setup(
        }
        *(uint16_t *)&wh->i_dur[0] = 0;
 
+       /*
+        * XXX TODO: this is what the TX lock is for.
+        * Here we're incrementing sequence numbers, and they
+        * need to be in lock-step with what the driver is doing
+        * both in TX ordering and crypto encap (IV increment.)
+        *
+        * If the driver does seqno itself, then we can skip
+        * assigning sequence numbers here, and we can avoid
+        * requiring the TX lock.
+        */
        tap = &ni->ni_tx_ampdu[tid];
        if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap))
                m->m_flags |= M_AMPDU_MPDU;
@@ -1542,6 +1553,11 @@ ieee80211_encap(struct ieee80211vap *vap
                if (is_amsdu)
                        qos[0] |= IEEE80211_QOS_AMSDU;
 
+               /*
+                * XXX TODO TX lock is needed for atomic updates of sequence
+                * numbers.  If the driver does it, then don't do it here;
+                * and we don't need the TX lock held.
+                */
                if ((m->m_flags & M_AMPDU_MPDU) == 0) {
                        /*
                         * NB: don't assign a sequence # to potential
@@ -1561,6 +1577,11 @@ ieee80211_encap(struct ieee80211vap *vap
                        M_SEQNO_SET(m, seqno);
                }
        } else {
+               /*
+                * XXX TODO TX lock is needed for atomic updates of sequence
+                * numbers.  If the driver does it, then don't do it here;
+                * and we don't need the TX lock held.
+                */
                seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
                *(uint16_t *)wh->i_seq =
                    htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
@@ -2065,6 +2086,7 @@ ieee80211_add_qos(uint8_t *frm, const st
  * Send a probe request frame with the specified ssid
  * and any optional information element data.
  */
+/* XXX VHT? */
 int
 ieee80211_send_probereq(struct ieee80211_node *ni,
        const uint8_t sa[IEEE80211_ADDR_LEN],
@@ -2111,6 +2133,7 @@ ieee80211_send_probereq(struct ieee80211
         *      [tlv] RSN (optional)
         *      [tlv] extended supported rates
         *      [tlv] HT cap (optional)
+        *      [tlv] VHT cap (optional)
         *      [tlv] WPA (optional)
         *      [tlv] user-specified ie's
         */
@@ -2119,7 +2142,8 @@ ieee80211_send_probereq(struct ieee80211
                 2 + IEEE80211_NWID_LEN
               + 2 + IEEE80211_RATE_SIZE
               + sizeof(struct ieee80211_ie_htcap)
-              + sizeof(struct ieee80211_ie_htinfo)
+              + sizeof(struct ieee80211_ie_vhtcap)
+              + sizeof(struct ieee80211_ie_htinfo)     /* XXX not needed? */
               + sizeof(struct ieee80211_ie_wpa)
               + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
               + sizeof(struct ieee80211_ie_wpa)
@@ -2159,6 +2183,21 @@ ieee80211_send_probereq(struct ieee80211
                frm = ieee80211_add_htcap_ch(frm, vap, c);
        }
 
+       /*
+        * XXX TODO: need to figure out what/how to update the
+        * VHT channel.
+        */
+#if 0
+       (vap->iv_flags_vht & IEEE80211_FVHT_VHT) {
+               struct ieee80211_channel *c;
+
+               c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+                   vap->iv_flags_ht);
+               c = ieee80211_vht_adjust_channel(ic, c, vap->iv_flags_vht);
+               frm = ieee80211_add_vhtcap_ch(frm, vap, c);
+       }
+#endif
+
        frm = ieee80211_add_wpa(frm, vap);
        if (vap->iv_appie_probereq != NULL)
                frm = add_appie(frm, vap->iv_appie_probereq);
@@ -2357,6 +2396,7 @@ ieee80211_send_mgmt(struct ieee80211_nod
 
        case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
        case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+               /* XXX VHT? */
                /*
                 * asreq frame format
                 *      [2] capability information
@@ -2368,6 +2408,7 @@ ieee80211_send_mgmt(struct ieee80211_nod
                 *      [4] power capability (optional)
                 *      [28] supported channels (optional)
                 *      [tlv] HT capabilities
+                *      [tlv] VHT capabilities
                 *      [tlv] WME (optional)
                 *      [tlv] Vendor OUI HT capabilities (optional)
                 *      [tlv] Atheros capabilities (if negotiated)
@@ -2385,6 +2426,7 @@ ieee80211_send_mgmt(struct ieee80211_nod
                       + 2 + 26
                       + sizeof(struct ieee80211_wme_info)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to