This patch adds initial 11ac support to the iwx(4) driver.
This means that 80MHz channels can be used. No other 11ac features
are enabled yet.

This is not yet a patch which could be committed. Apart from debug
prints which need to go, there is a known issue found by dv@ where
this patch causes a firmware error, sysassert 0x20101A25. The reason
for this is not known.
It would help to get more testing to see if more clues can be found
based on where this error pops up. I cannot reproduce the error myself.

When sending feedback, please be clear about which iwx(4) device and
which access point has been tested. Thanks!

The patch works for me on AX200 and AX201 with a pepwave AC one mini AP,
although throughput is not much different to 11n 40MHz with this AP.

diff refs/heads/master refs/heads/11ac
blob - 57bdcce64458e7f9d5802ce4247d5651f9183200
blob + a56c59f82c854c282a61c302162df4eae3c27fb8
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -304,6 +304,7 @@ int iwx_schedule_session_protection(struct iwx_softc *
            uint32_t);
 void   iwx_init_channel_map(struct iwx_softc *, uint16_t *, uint32_t *, int);
 void   iwx_setup_ht_rates(struct iwx_softc *);
+void   iwx_setup_vht_rates(struct iwx_softc *);
 int    iwx_mimo_enabled(struct iwx_softc *);
 void   iwx_mac_ctxt_task(void *);
 void   iwx_phy_ctxt_task(void *);
@@ -363,12 +364,13 @@ void      iwx_clear_oactive(struct iwx_softc *, struct 
iwx_
 void   iwx_rx_bmiss(struct iwx_softc *, struct iwx_rx_packet *,
            struct iwx_rx_data *);
 int    iwx_binding_cmd(struct iwx_softc *, struct iwx_node *, uint32_t);
+uint8_t        iwx_get_vht_ctrl_pos(struct ieee80211com *, struct 
ieee80211_channel *);
 int    iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *, struct iwx_phy_ctxt *, 
uint8_t,
-           uint8_t, uint32_t, uint8_t);
+           uint8_t, uint32_t, uint8_t, uint8_t);
 int    iwx_phy_ctxt_cmd_v3(struct iwx_softc *, struct iwx_phy_ctxt *, uint8_t,
-           uint8_t, uint32_t, uint8_t);
+           uint8_t, uint32_t, uint8_t, uint8_t);
 int    iwx_phy_ctxt_cmd(struct iwx_softc *, struct iwx_phy_ctxt *, uint8_t,
-           uint8_t, uint32_t, uint32_t, uint8_t);
+           uint8_t, uint32_t, uint32_t, uint8_t, uint8_t);
 int    iwx_send_cmd(struct iwx_softc *, struct iwx_host_cmd *);
 int    iwx_send_cmd_pdu(struct iwx_softc *, uint32_t, uint32_t, uint16_t,
            const void *);
@@ -430,10 +432,12 @@ int       iwx_scan_abort(struct iwx_softc *);
 int    iwx_enable_mgmt_queue(struct iwx_softc *);
 int    iwx_rs_rval2idx(uint8_t);
 uint16_t iwx_rs_ht_rates(struct iwx_softc *, struct ieee80211_node *, int);
+uint16_t iwx_rs_vht_rates(struct iwx_softc *, struct ieee80211_node *, int);
 int    iwx_rs_init(struct iwx_softc *, struct iwx_node *);
 int    iwx_enable_data_tx_queues(struct iwx_softc *);
 int    iwx_phy_ctxt_update(struct iwx_softc *, struct iwx_phy_ctxt *,
-           struct ieee80211_channel *, uint8_t, uint8_t, uint32_t, uint8_t);
+           struct ieee80211_channel *, uint8_t, uint8_t, uint32_t, uint8_t,
+           uint8_t);
 int    iwx_auth(struct iwx_softc *);
 int    iwx_deauth(struct iwx_softc *);
 int    iwx_run(struct iwx_softc *);
@@ -2812,6 +2816,15 @@ iwx_init_channel_map(struct iwx_softc *sc, uint16_t *c
                        if (ch_flags & IWX_NVM_CHANNEL_40MHZ)
                                channel->ic_flags |= IEEE80211_CHAN_40MHZ;
                }
+
+               if (is_5ghz && data->sku_cap_11ac_enable) {
+                       channel->ic_flags |= IEEE80211_CHAN_VHT;
+                       if (ch_flags & IWX_NVM_CHANNEL_80MHZ) {
+                               printf("%s: channel %d 80MHz ok\n", __func__, 
hw_value);
+                               channel->ic_xflags |= IEEE80211_CHANX_80MHZ;
+                       } else
+                               printf("%s: channel %d 80MHz NOT ok\n", 
__func__, hw_value);
+               }
        }
 }
 
@@ -2846,6 +2859,34 @@ iwx_setup_ht_rates(struct iwx_softc *sc)
 }
 
 void
+iwx_setup_vht_rates(struct iwx_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint8_t rx_ant = iwx_fw_valid_rx_ant(sc);
+       int n;
+
+       ic->ic_vht_rxmcs = (IEEE80211_VHT_MCS_0_9 <<
+           IEEE80211_VHT_MCS_FOR_SS_SHIFT(1));
+
+       if (iwx_mimo_enabled(sc) &&
+           ((rx_ant & IWX_ANT_AB) == IWX_ANT_AB ||
+           (rx_ant & IWX_ANT_BC) == IWX_ANT_BC)) {
+               ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_0_9 <<
+                   IEEE80211_VHT_MCS_FOR_SS_SHIFT(2));
+       } else {
+               ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+                   IEEE80211_VHT_MCS_FOR_SS_SHIFT(2));
+       }
+
+       for (n = 3; n <= IEEE80211_VHT_NUM_SS; n++) {
+               ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+                   IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+       }
+
+       ic->ic_vht_txmcs = ic->ic_vht_rxmcs;
+}
+
+void
 iwx_init_reorder_buffer(struct iwx_reorder_buffer *reorder_buf,
     uint16_t ssn, uint16_t buf_size)
 {
@@ -3147,7 +3188,7 @@ iwx_phy_ctxt_task(void *arg)
        struct ieee80211com *ic = &sc->sc_ic;
        struct iwx_node *in = (void *)ic->ic_bss;
        struct ieee80211_node *ni = &in->in_ni;
-       uint8_t chains, sco;
+       uint8_t chains, sco, vht_chan_width;
        int err, s = splnet();
 
        if ((sc->sc_flags & IWX_FLAG_SHUTDOWN) ||
@@ -3159,13 +3200,23 @@ iwx_phy_ctxt_task(void *arg)
        }
 
        chains = iwx_mimo_enabled(sc) ? 2 : 1;
-       if (ieee80211_node_supports_ht_chan40(ni))
+       if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+           IEEE80211_CHAN_40MHZ_ALLOWED(ni->ni_chan) &&
+           ieee80211_node_supports_ht_chan40(ni))
                sco = (ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK);
        else
                sco = IEEE80211_HTOP0_SCO_SCN;
-       if (in->in_phyctxt->sco != sco) {
+       if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+           IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+           ieee80211_node_supports_vht_chan80(ni))
+               vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
+       else
+               vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
+       if (in->in_phyctxt->sco != sco ||
+           in->in_phyctxt->vht_chan_width != vht_chan_width) {
                err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
-                   in->in_phyctxt->channel, chains, chains, 0, sco);
+                   in->in_phyctxt->channel, chains, chains, 0, sco,
+                   vht_chan_width);
                if (err)
                        printf("%s: failed to update PHY\n", DEVNAME(sc));
        }
@@ -3981,8 +4032,13 @@ iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int
                tap->wr_chan_freq =
                    htole16(ic->ic_channels[chanidx].ic_freq);
                chan_flags = ic->ic_channels[chanidx].ic_flags;
-               if (ic->ic_curmode != IEEE80211_MODE_11N)
+               if (ic->ic_curmode != IEEE80211_MODE_11N &&
+                   ic->ic_curmode != IEEE80211_MODE_11AC) {
                        chan_flags &= ~IEEE80211_CHAN_HT;
+                       chan_flags &= ~IEEE80211_CHAN_40MHZ;
+               }
+               if (ic->ic_curmode != IEEE80211_MODE_11AC)
+                       chan_flags &= ~IEEE80211_CHAN_VHT;
                tap->wr_chan_flags = htole16(chan_flags);
                tap->wr_dbm_antsignal = (int8_t)rxi->rxi_rssi;
                tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
@@ -4806,9 +4862,42 @@ iwx_binding_cmd(struct iwx_softc *sc, struct iwx_node 
        return err;
 }
 
+uint8_t
+iwx_get_vht_ctrl_pos(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+       int center_idx = ic->ic_bss->ni_vht_chan_center_freq_idx0;
+       int primary_idx = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+       /*
+        * The FW is expected to check the control channel position only
+        * when in HT/VHT and the channel width is not 20MHz. Return
+        * this value as the default one:
+        */
+       uint8_t pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
+
+       switch (primary_idx - center_idx) {
+       case -6:
+               pos = IWX_PHY_VHT_CTRL_POS_2_BELOW;
+               break;
+       case -2:
+               pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
+               break;
+       case 2:
+               pos = IWX_PHY_VHT_CTRL_POS_1_ABOVE;
+               break;
+       case 6:
+               pos = IWX_PHY_VHT_CTRL_POS_2_ABOVE;
+               break;
+       default:
+               break;
+       }
+
+       return pos;
+}
+
 int
 iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
-    uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t 
sco)
+    uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t 
sco,
+    uint8_t vht_chan_width)
 {
        struct ieee80211com *ic = &sc->sc_ic;
        struct iwx_phy_context_cmd_uhb cmd;
@@ -4829,7 +4918,17 @@ iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *sc, struct i
        cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
            IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
        cmd.ci.channel = htole32(ieee80211_chan2ieee(ic, chan));
-       if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
+       if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
+               uint8_t ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+               if (ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_BELOW ||
+                   ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_ABOVE) {
+                       cmd.ci.ctrl_pos = ctrl_pos;
+                       cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
+               } else {
+                       cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20;
+                       cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
+               }
+       } else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
                if (sco == IEEE80211_HTOP0_SCO_SCA) {
                        /* secondary chan above -> control chan below */
                        cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
@@ -4860,7 +4959,8 @@ iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *sc, struct i
 
 int
 iwx_phy_ctxt_cmd_v3(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
-    uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t 
sco)
+    uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t 
sco,
+    uint8_t vht_chan_width)
 {
        struct ieee80211com *ic = &sc->sc_ic;
        struct iwx_phy_context_cmd cmd;
@@ -4881,7 +4981,17 @@ iwx_phy_ctxt_cmd_v3(struct iwx_softc *sc, struct iwx_p
        cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
            IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
        cmd.ci.channel = ieee80211_chan2ieee(ic, chan);
-       if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
+       if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
+               uint8_t ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+               if (ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_BELOW ||
+                   ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_ABOVE) {
+                       cmd.ci.ctrl_pos = ctrl_pos;
+                       cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
+               } else {
+                       cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20;
+                       cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
+               }
+       } else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
                if (sco == IEEE80211_HTOP0_SCO_SCA) {
                        /* secondary chan above -> control chan below */
                        cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
@@ -4913,7 +5023,7 @@ iwx_phy_ctxt_cmd_v3(struct iwx_softc *sc, struct iwx_p
 int
 iwx_phy_ctxt_cmd(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
     uint8_t chains_static, uint8_t chains_dynamic, uint32_t action,
-    uint32_t apply_time, uint8_t sco)
+    uint32_t apply_time, uint8_t sco, uint8_t vht_chan_width)
 {
        int cmdver;
 
@@ -4933,11 +5043,11 @@ iwx_phy_ctxt_cmd(struct iwx_softc *sc, struct iwx_phy_
         */
        if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS)) {
                return iwx_phy_ctxt_cmd_uhb_v3(sc, ctxt, chains_static,
-                   chains_dynamic, action, sco);
+                   chains_dynamic, action, sco, vht_chan_width);
        }
 
        return iwx_phy_ctxt_cmd_v3(sc, ctxt, chains_static, chains_dynamic,
-           action, sco);
+           action, sco, vht_chan_width);
 }
 
 int
@@ -5258,13 +5368,23 @@ iwx_tx_fill_cmd(struct iwx_softc *sc, struct iwx_node 
        if ((ni->ni_flags & IEEE80211_NODE_HT) &&
            type == IEEE80211_FC0_TYPE_DATA &&
            rinfo->ht_plcp != IWX_RATE_HT_SISO_MCS_INV_PLCP) {
-               uint8_t sco;
-               if (ieee80211_node_supports_ht_chan40(ni))
+               uint8_t sco = IEEE80211_HTOP0_SCO_SCN;
+               uint8_t vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
+               if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+                   IEEE80211_CHAN_80MHZ_ALLOWED(ni->ni_chan) &&
+                   ieee80211_node_supports_vht_chan80(ni))
+                       vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
+               else if (IEEE80211_CHAN_40MHZ_ALLOWED(ni->ni_chan) &&
+                   ieee80211_node_supports_ht_chan40(ni))
                        sco = (ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK);
-               else
-                       sco = IEEE80211_HTOP0_SCO_SCN;
                rate_flags |= IWX_RATE_MCS_HT_MSK; 
-               if ((sco == IEEE80211_HTOP0_SCO_SCA || 
+               if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80 &&
+                   in->in_phyctxt != NULL &&
+                   in->in_phyctxt->vht_chan_width == vht_chan_width) {
+                       rate_flags |= IWX_RATE_MCS_CHAN_WIDTH_80;
+                       if (ieee80211_node_supports_vht_sgi80(ni))
+                               rate_flags |= IWX_RATE_MCS_SGI_MSK;
+               } else if ((sco == IEEE80211_HTOP0_SCO_SCA || 
                    sco == IEEE80211_HTOP0_SCO_SCB) &&
                    in->in_phyctxt != NULL && in->in_phyctxt->sco == sco) {
                        rate_flags |= IWX_RATE_MCS_CHAN_WIDTH_40;
@@ -5376,8 +5496,13 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ie
                tap->wt_flags = 0;
                tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
                chan_flags = ni->ni_chan->ic_flags;
-               if (ic->ic_curmode != IEEE80211_MODE_11N)
+               if (ic->ic_curmode != IEEE80211_MODE_11N &&
+                   ic->ic_curmode != IEEE80211_MODE_11AC) {
                        chan_flags &= ~IEEE80211_CHAN_HT;
+                       chan_flags &= ~IEEE80211_CHAN_40MHZ;
+               }
+               if (ic->ic_curmode != IEEE80211_MODE_11AC)
+                       chan_flags &= ~IEEE80211_CHAN_VHT;
                tap->wt_chan_flags = htole16(chan_flags);
                if ((ni->ni_flags & IEEE80211_NODE_HT) &&
                    !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
@@ -5797,7 +5922,9 @@ iwx_add_sta_cmd(struct iwx_softc *sc, struct iwx_node 
 {
        struct iwx_add_sta_cmd add_sta_cmd;
        int err;
-       uint32_t status;
+       uint32_t status, aggsize;
+       const uint32_t max_aggsize = (IWX_STA_FLG_MAX_AGG_SIZE_64K >>
+                   IWX_STA_FLG_MAX_AGG_SIZE_SHIFT);
        struct ieee80211com *ic = &sc->sc_ic;
 
        if (!update && (sc->sc_flags & IWX_FLAG_STA_ACTIVE))
@@ -5832,24 +5959,52 @@ iwx_add_sta_cmd(struct iwx_softc *sc, struct iwx_node 
                    IWX_STA_FLG_AGG_MPDU_DENS_MSK);
 
                if (iwx_mimo_enabled(sc)) {
-                       if (in->in_ni.ni_rxmcs[1] != 0) {
-                               add_sta_cmd.station_flags |=
-                                   htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
+                       if (in->in_ni.ni_flags & IEEE80211_NODE_VHT) {
+                               uint16_t rx_mcs = (in->in_ni.ni_vht_rxmcs &
+                                   IEEE80211_VHT_MCS_FOR_SS_MASK(2)) >>
+                                   IEEE80211_VHT_MCS_FOR_SS_SHIFT(2);
+                               if (rx_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP) {
+                                       add_sta_cmd.station_flags |=
+                                           htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
+                               }
+                       } else {
+                               if (in->in_ni.ni_rxmcs[1] != 0) {
+                                       add_sta_cmd.station_flags |=
+                                           htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
+                               }
+                               if (in->in_ni.ni_rxmcs[2] != 0) {
+                                       add_sta_cmd.station_flags |=
+                                           htole32(IWX_STA_FLG_MIMO_EN_MIMO3);
+                               }
                        }
-                       if (in->in_ni.ni_rxmcs[2] != 0) {
-                               add_sta_cmd.station_flags |=
-                                   htole32(IWX_STA_FLG_MIMO_EN_MIMO3);
-                       }
                }
 
-               if (ieee80211_node_supports_ht_chan40(&in->in_ni)) {
+               if (IEEE80211_CHAN_40MHZ_ALLOWED(in->in_ni.ni_chan) &&
+                   ieee80211_node_supports_ht_chan40(&in->in_ni)) {
                        add_sta_cmd.station_flags |= htole32(
                            IWX_STA_FLG_FAT_EN_40MHZ);
                }
 
-               add_sta_cmd.station_flags
-                   |= htole32(IWX_STA_FLG_MAX_AGG_SIZE_64K);
-               switch (ic->ic_ampdu_params & IEEE80211_AMPDU_PARAM_SS) {
+               if (in->in_ni.ni_flags & IEEE80211_NODE_VHT) {
+                       if (IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+                           ieee80211_node_supports_vht_chan80(&in->in_ni)) {
+                               add_sta_cmd.station_flags |= htole32(
+                                   IWX_STA_FLG_FAT_EN_80MHZ);
+                       }
+                       aggsize = (in->in_ni.ni_vhtcaps &
+                           IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK) >>
+                           IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT;
+               } else {
+                       aggsize = (in->in_ni.ni_ampdu_param &
+                           IEEE80211_AMPDU_PARAM_LE);
+               }
+               if (aggsize > max_aggsize)
+                       aggsize = max_aggsize;
+               add_sta_cmd.station_flags |= htole32((aggsize <<
+                   IWX_STA_FLG_MAX_AGG_SIZE_SHIFT) &
+                   IWX_STA_FLG_MAX_AGG_SIZE_MSK);
+
+               switch (in->in_ni.ni_ampdu_param & IEEE80211_AMPDU_PARAM_SS) {
                case IEEE80211_AMPDU_PARAM_SS_2:
                        add_sta_cmd.station_flags
                            |= htole32(IWX_STA_FLG_AGG_MPDU_DENS_2US);
@@ -6072,7 +6227,15 @@ iwx_fill_probe_req(struct iwx_softc *sc, struct iwx_sc
                        return ENOBUFS;
                frm = ieee80211_add_htcaps(frm, ic);
                /* XXX add WME info? */
+               remain -= frm - pos;
        }
+
+       if (ic->ic_flags & IEEE80211_F_VHTON) {
+               if (remain < 14)
+                       return ENOBUFS;
+               frm = ieee80211_add_vhtcaps(frm, ic);
+       }
+
        preq->common_data.len = htole16(frm - pos);
 
        return 0;
@@ -6829,6 +6992,38 @@ iwx_rs_ht_rates(struct iwx_softc *sc, struct ieee80211
        return htrates;
 }
 
+uint16_t
+iwx_rs_vht_rates(struct iwx_softc *sc, struct ieee80211_node *ni, int num_ss)
+{
+       uint16_t rx_mcs;
+       int max_mcs = -1;
+
+       rx_mcs = (ni->ni_vht_rxmcs & IEEE80211_VHT_MCS_FOR_SS_MASK(num_ss)) >>
+           IEEE80211_VHT_MCS_FOR_SS_SHIFT(num_ss);
+       switch (rx_mcs) {
+       case IEEE80211_VHT_MCS_SS_NOT_SUPP:
+               break;
+       case IEEE80211_VHT_MCS_0_7:
+               max_mcs = 7;
+               break;
+       case IEEE80211_VHT_MCS_0_8:
+               max_mcs = 8;
+               break;
+       case IEEE80211_VHT_MCS_0_9:
+               /* Disable VHT MCS 9 for 20MHz-only stations. */
+               if (!ieee80211_node_supports_ht_chan40(ni))
+                       max_mcs = 8;
+               else
+                       max_mcs = 9;
+               break;
+       default:
+               /* Should not happen; Values above cover the possible range. */
+               panic("invalid VHT Rx MCS value %u", rx_mcs);
+       }
+               
+       return ((1 << (max_mcs + 1)) - 1);
+}
+
 int
 iwx_rs_init(struct iwx_softc *sc, struct iwx_node *in)
 {
@@ -6849,27 +7044,49 @@ iwx_rs_init(struct iwx_softc *sc, struct iwx_node *in)
                cfg_cmd.non_ht_rates |= (1 << idx);
        }
 
-       if (ni->ni_flags & IEEE80211_NODE_HT) {
+       if (ni->ni_flags & IEEE80211_NODE_VHT) {
+               cfg_cmd.mode = IWX_TLC_MNG_MODE_VHT;
+               cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_HT_BW_NONE_160] =
+                   htole16(iwx_rs_vht_rates(sc, ni, 1));
+               cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_HT_BW_NONE_160] =
+                   htole16(iwx_rs_vht_rates(sc, ni, 2));
+       } else if (ni->ni_flags & IEEE80211_NODE_HT) {
                cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
                cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_HT_BW_NONE_160] =
-                   iwx_rs_ht_rates(sc, ni, IEEE80211_HT_RATESET_SISO);
+                   htole16(iwx_rs_ht_rates(sc, ni,
+                   IEEE80211_HT_RATESET_SISO));
                cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_HT_BW_NONE_160] =
-                   iwx_rs_ht_rates(sc, ni, IEEE80211_HT_RATESET_MIMO2);
+                   htole16(iwx_rs_ht_rates(sc, ni,
+                   IEEE80211_HT_RATESET_MIMO2));
        } else
                cfg_cmd.mode = IWX_TLC_MNG_MODE_NON_HT;
 
        cfg_cmd.sta_id = IWX_STATION_ID;
-       if (in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCA ||
+       if (in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80)
+               cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
+       else if (in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCA ||
            in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCB)
                cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_40MHZ;
        else
                cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_20MHZ;
        cfg_cmd.chains = IWX_TLC_MNG_CHAIN_A_MSK | IWX_TLC_MNG_CHAIN_B_MSK;
-       cfg_cmd.max_mpdu_len = 3839;
-       if (ieee80211_node_supports_ht_sgi20(ni))
-               cfg_cmd.sgi_ch_width_supp = (1 << IWX_TLC_MNG_CH_WIDTH_20MHZ);
-       if (ieee80211_node_supports_ht_sgi40(ni))
-               cfg_cmd.sgi_ch_width_supp = (1 << IWX_TLC_MNG_CH_WIDTH_40MHZ);
+       if (ni->ni_flags & IEEE80211_NODE_VHT)
+               cfg_cmd.max_mpdu_len = htole16(3895);
+       else
+               cfg_cmd.max_mpdu_len = htole16(3839);
+       if (ni->ni_flags & IEEE80211_NODE_HT) {
+               if (ieee80211_node_supports_ht_sgi20(ni)) {
+                       cfg_cmd.sgi_ch_width_supp |= (1 <<
+                           IWX_TLC_MNG_CH_WIDTH_20MHZ);
+               }
+               if (ieee80211_node_supports_ht_sgi40(ni)) {
+                       cfg_cmd.sgi_ch_width_supp |= (1 <<
+                           IWX_TLC_MNG_CH_WIDTH_40MHZ);
+               }
+       }
+       if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+           ieee80211_node_supports_vht_sgi80(ni))
+               cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_80MHZ);
 
        cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
        return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, cmd_size, &cfg_cmd);
@@ -6889,7 +7106,11 @@ iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_upd
                return;
 
        rate_n_flags = le32toh(notif->rate);
-       if (rate_n_flags & IWX_RATE_MCS_HT_MSK) {
+       if (rate_n_flags & IWX_RATE_MCS_VHT_MSK) {
+               ni->ni_txmcs = (rate_n_flags & IWX_RATE_VHT_MCS_RATE_CODE_MSK);
+               ni->ni_vht_ss = ((rate_n_flags & IWX_RATE_VHT_MCS_NSS_MSK) >>
+                   IWX_RATE_VHT_MCS_NSS_POS) + 1;
+       } else if (rate_n_flags & IWX_RATE_MCS_HT_MSK) {
                ni->ni_txmcs = (rate_n_flags &
                    (IWX_RATE_HT_MCS_RATE_CODE_MSK |
                    IWX_RATE_HT_MCS_NSS_MSK));
@@ -6918,7 +7139,8 @@ iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_upd
 int
 iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_phy_ctxt *phyctxt,
     struct ieee80211_channel *chan, uint8_t chains_static,
-    uint8_t chains_dynamic, uint32_t apply_time, uint8_t sco)
+    uint8_t chains_dynamic, uint32_t apply_time, uint8_t sco,
+    uint8_t vht_chan_width)
 {
        uint16_t band_flags = (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ);
        int err;
@@ -6928,7 +7150,8 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
            (phyctxt->channel->ic_flags & band_flags) !=
            (chan->ic_flags & band_flags)) {
                err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
-                   chains_dynamic, IWX_FW_CTXT_ACTION_REMOVE, apply_time, sco);
+                   chains_dynamic, IWX_FW_CTXT_ACTION_REMOVE, apply_time, sco,
+                   vht_chan_width);
                if (err) {
                        printf("%s: could not remove PHY context "
                            "(error %d)\n", DEVNAME(sc), err);
@@ -6936,7 +7159,8 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
                }
                phyctxt->channel = chan;
                err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
-                   chains_dynamic, IWX_FW_CTXT_ACTION_ADD, apply_time, sco);
+                   chains_dynamic, IWX_FW_CTXT_ACTION_ADD, apply_time, sco,
+                   vht_chan_width);
                if (err) {
                        printf("%s: could not add PHY context "
                            "(error %d)\n", DEVNAME(sc), err);
@@ -6945,7 +7169,8 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
        } else {
                phyctxt->channel = chan;
                err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
-                   chains_dynamic, IWX_FW_CTXT_ACTION_MODIFY, apply_time, sco);
+                   chains_dynamic, IWX_FW_CTXT_ACTION_MODIFY, apply_time, sco,
+                   vht_chan_width);
                if (err) {
                        printf("%s: could not update PHY context (error %d)\n",
                            DEVNAME(sc), err);
@@ -6954,6 +7179,7 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
        }
 
        phyctxt->sco = sco;
+       phyctxt->vht_chan_width = vht_chan_width;
        return 0;
 }
 
@@ -6969,12 +7195,14 @@ iwx_auth(struct iwx_softc *sc)
 
        if (ic->ic_opmode == IEEE80211_M_MONITOR) {
                err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
-                   ic->ic_ibss_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+                   ic->ic_ibss_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+                   IEEE80211_VHTOP0_CHAN_WIDTH_HT);
                if (err)
                        return err;
        } else {
                err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
-                   in->in_ni.ni_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+                   in->in_ni.ni_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+                   IEEE80211_VHTOP0_CHAN_WIDTH_HT);
                if (err)
                        return err;
        }
@@ -7087,7 +7315,8 @@ iwx_deauth(struct iwx_softc *sc)
 
        /* Move unused PHY context to a default channel. */
        err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
-           &ic->ic_channels[1], 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+           &ic->ic_channels[1], 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+           IEEE80211_VHTOP0_CHAN_WIDTH_HT);
        if (err)
                return err;
 
@@ -7116,27 +7345,43 @@ iwx_run(struct iwx_softc *sc)
                uint8_t chains = iwx_mimo_enabled(sc) ? 2 : 1;
                err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
                    in->in_phyctxt->channel, chains, chains,
-                   0, IEEE80211_HTOP0_SCO_SCN);
+                   0, IEEE80211_HTOP0_SCO_SCN,
+                   IEEE80211_VHTOP0_CHAN_WIDTH_HT);
                if (err) {
                        printf("%s: failed to update PHY\n", DEVNAME(sc));
                        return err;
                }
        } else if (ni->ni_flags & IEEE80211_NODE_HT) {
                uint8_t chains = iwx_mimo_enabled(sc) ? 2 : 1;
-               uint8_t sco;
-               if (ieee80211_node_supports_ht_chan40(ni))
+               uint8_t sco, vht_chan_width;
+               if (IEEE80211_CHAN_40MHZ_ALLOWED(in->in_ni.ni_chan) &&
+                   ieee80211_node_supports_ht_chan40(ni))
                        sco = (ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK);
                else
                        sco = IEEE80211_HTOP0_SCO_SCN;
+               if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+                   IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+                   ieee80211_node_supports_vht_chan80(ni))
+                       vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
+               else
+                       vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
                err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
                    in->in_phyctxt->channel, chains, chains,
-                   0, sco);
+                   0, sco, vht_chan_width);
                if (err) {
                        printf("%s: failed to update PHY\n", DEVNAME(sc));
                        return err;
                }
        }
 
+       /* Update STA again to apply HT and VHT settings. */
+       err = iwx_add_sta_cmd(sc, in, 1);
+       if (err) {
+               printf("%s: could not update STA (error %d)\n",
+                   DEVNAME(sc), err);
+               return err;
+       }
+
        /* We have now been assigned an associd by the AP. */
        err = iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_MODIFY, 1);
        if (err) {
@@ -7255,7 +7500,8 @@ iwx_run_stop(struct iwx_softc *sc)
        /* Reset Tx chains in case MIMO or 40 MHz channels were enabled. */
        if (in->in_ni.ni_flags & IEEE80211_NODE_HT) {
                err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
-                  in->in_phyctxt->channel, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+                   in->in_phyctxt->channel, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+                   IEEE80211_VHTOP0_CHAN_WIDTH_HT);
                if (err) {
                        printf("%s: failed to update PHY\n", DEVNAME(sc));
                        return err;
@@ -7905,7 +8151,8 @@ iwx_init_hw(struct iwx_softc *sc)
                sc->sc_phyctxt[i].id = i;
                sc->sc_phyctxt[i].channel = &ic->ic_channels[1];
                err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[i], 1, 1,
-                   IWX_FW_CTXT_ACTION_ADD, 0, IEEE80211_HTOP0_SCO_SCN);
+                   IWX_FW_CTXT_ACTION_ADD, 0, IEEE80211_HTOP0_SCO_SCN,
+                   IEEE80211_VHTOP0_CHAN_WIDTH_HT);
                if (err) {
                        printf("%s: could not add phy context %d (error %d)\n",
                            DEVNAME(sc), i, err);
@@ -8016,6 +8263,8 @@ iwx_init(struct ifnet *ifp)
 
        if (sc->sc_nvm.sku_cap_11n_enable)
                iwx_setup_ht_rates(sc);
+       if (sc->sc_nvm.sku_cap_11ac_enable)
+               iwx_setup_vht_rates(sc);
 
        KASSERT(sc->task_refs.refs == 0);
        refcnt_init(&sc->task_refs);
@@ -9272,6 +9521,8 @@ iwx_preinit(struct iwx_softc *sc)
 
        if (sc->sc_nvm.sku_cap_11n_enable)
                iwx_setup_ht_rates(sc);
+       if (sc->sc_nvm.sku_cap_11ac_enable)
+               iwx_setup_vht_rates(sc);
 
        /* not all hardware can do 5GHz band */
        if (!sc->sc_nvm.sku_cap_band_52GHz_enable)
@@ -9498,6 +9749,13 @@ iwx_attach(struct device *parent, struct device *self,
        ic->ic_aselcaps = 0;
        ic->ic_ampdu_params = (IEEE80211_AMPDU_PARAM_SS_4 | 0x3 /* 64k */);
 
+       ic->ic_vhtcaps = IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 |
+           (IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K <<
+           IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT) |
+           (IEEE80211_VHTCAP_CHAN_WIDTH_80 <<
+            IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT) | IEEE80211_VHTCAP_SGI80 |
+           IEEE80211_VHTCAP_RX_ANT_PATTERN | IEEE80211_VHTCAP_TX_ANT_PATTERN;
+
        ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
        ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
        ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
@@ -9505,6 +9763,8 @@ iwx_attach(struct device *parent, struct device *self,
        for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
                sc->sc_phyctxt[i].id = i;
                sc->sc_phyctxt[i].sco = IEEE80211_HTOP0_SCO_SCN;
+               sc->sc_phyctxt[i].vht_chan_width =
+                   IEEE80211_VHTOP0_CHAN_WIDTH_HT;
        }
 
        /* IBSS channel undefined for now. */
blob - a855b36f5aee316aeaef1e4e43ab8fb9f128482e
blob + 64c5ec103876f14d1ac8e53f682b2c8a351ba42b
--- sys/dev/pci/if_iwxreg.h
+++ sys/dev/pci/if_iwxreg.h
@@ -6431,7 +6431,9 @@ struct iwx_umac_scan_iter_complete_notif {
 #define IWX_STA_FLG_MAX_AGG_SIZE_256K  (5 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
 #define IWX_STA_FLG_MAX_AGG_SIZE_512K  (6 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
 #define IWX_STA_FLG_MAX_AGG_SIZE_1024K (7 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
-#define IWX_STA_FLG_MAX_AGG_SIZE_MSK   (7 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
+#define IWX_STA_FLG_MAX_AGG_SIZE_2M    (8 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
+#define IWX_STA_FLG_MAX_AGG_SIZE_4M    (9 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
+#define IWX_STA_FLG_MAX_AGG_SIZE_MSK   (0xf << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
 
 #define IWX_STA_FLG_AGG_MPDU_DENS_SHIFT        23
 #define IWX_STA_FLG_AGG_MPDU_DENS_2US  (4 << IWX_STA_FLG_AGG_MPDU_DENS_SHIFT)
blob - f40dd20c64f442c8f11f9723b8ed3b2cef2f06b8
blob + 39c4c34e97ef9d27c645a73123b48fce64ba4445
--- sys/dev/pci/if_iwxvar.h
+++ sys/dev/pci/if_iwxvar.h
@@ -321,6 +321,7 @@ struct iwx_phy_ctxt {
        uint32_t ref;
        struct ieee80211_channel *channel;
        uint8_t sco; /* 40 MHz secondary channel offset */
+       uint8_t vht_chan_width;
 };
 
 struct iwx_bf_data {
blob - 83d4eac091af3240f88ad56d06a47f6373f80922
blob + 470ec987e4341275eaa2f9a9e5a0f64d0b54c6a2
--- sys/net80211/ieee80211.c
+++ sys/net80211/ieee80211.c
@@ -472,11 +472,10 @@ ieee80211_media_init(struct ifnet *ifp,
                                ADD(ic, IFM_IEEE80211_VHT_MCS0 + i,
                                    mopt | IFM_IEEE80211_MONITOR);
                }
-#if 0
                ic->ic_flags |= IEEE80211_F_VHTON; /* enable 11ac by default */
+               ic->ic_flags |= IEEE80211_F_HTON; /* 11ac implies 11n */
                if (ic->ic_caps & IEEE80211_C_QOS)
                        ic->ic_flags |= IEEE80211_F_QOS;
-#endif
        }
 
        ieee80211_media_status(ifp, &imr);
@@ -663,6 +662,7 @@ ieee80211_media_change(struct ifnet *ifp)
            (newphymode == IEEE80211_MODE_AUTO ||
            newphymode == IEEE80211_MODE_11AC)) {
                ic->ic_flags |= IEEE80211_F_VHTON;
+               ic->ic_flags |= IEEE80211_F_HTON;
                ieee80211_configure_ampdu_tx(ic, 1);
        } else if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) &&
            (newphymode == IEEE80211_MODE_AUTO ||
blob - 2d31a17c87d7496541dba5934bef696d59f8c8fc
blob + d21964d56add61ec02fbcda02e118c76fdc54f6c
--- sys/net80211/ieee80211.h
+++ sys/net80211/ieee80211.h
@@ -441,7 +441,17 @@ enum {
        IEEE80211_ELEMID_U_APSD_COEX            = 142,
        /* 143-174 reserved */
        IEEE80211_ELEMID_MCCAOP_ADVERT_OVIEW    = 174,
-       /* 175-220 reserved */
+       /* 175-190 reserved */
+       IEEE80211_ELEMID_VHTCAPS                = 191,  /* 11ac */
+       IEEE80211_ELEMID_VHTOP                  = 192,  /* 11ac */
+       IEEE80211_ELEMID_EXT_BSS_LOAD           = 193,  /* 11ac */
+       IEEE80211_ELEMID_WIDEBAND_CHNL_SWITCH   = 194,  /* 11ac */
+       IEEE80211_ELEMID_VHT_TXPOWER            = 195,  /* 11ac */
+       IEEE80211_ELEMID_CHNL_SWITCH_WRAPPER    = 196,  /* 11ac */
+       IEEE80211_ELEMID_AID                    = 197,  /* 11ac */
+       IEEE80211_ELEMID_QUIET_CHNL             = 198,  /* 11ac */
+       IEEE80211_ELEMID_OPMODE_NOTIF           = 199,  /* 11ac */
+       /* 200-220 reserved */
        IEEE80211_ELEMID_VENDOR                 = 221   /* vendor private */
        /* 222-255 reserved */
 };
@@ -712,6 +722,84 @@ enum {
 /* Bits 12-15 are reserved. */
 
 /*
+ * VHT Capabilities Info (see 802.11ac-2013 8.4.2.160.2).
+ */
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_MASK  0x00000003
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_SHIFT 0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895  0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991  1
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_MASK       0x0c
+#define IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT      2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_80         0
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160                1
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160_8080   2
+#define IEEE80211_VHTCAP_RX_LDPC               0x00000010
+#define IEEE80211_VHTCAP_SGI80                 0x00000020
+#define IEEE80211_VHTCAP_SGI160                        0x00000040
+#define IEEE80211_VHTCAP_TX_STBC               0x00000080
+#define IEEE80211_VHTCAP_RX_STBC_SS_MASK       0x00000700
+#define IEEE80211_VHTCAP_RX_STBC_SS_SHIFT      8
+#define IEEE80211_VHTCAP_SU_BEAMFORMER         0x00000800
+#define IEEE80211_VHTCAP_SU_BEAMFORMEE         0x00001000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK   0x0000e000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT  13
+#define IEEE80211_VHTCAP_NUM_STS_MASK          0x00070000
+#define IEEE80211_VHTCAP_NUM_STS_SHIFT         16
+#define IEEE80211_VHTCAP_MU_BEAMFORMER         0x00080000
+#define IEEE80211_VHTCAP_MU_BEAMFORMEE         0x00100000
+#define IEEE80211_VHTCAP_TXOP_PS               0x00200000
+#define IEEE80211_VHTCAP_HTC_VHT               0x00400000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK    0x03800000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT   23
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_8K      0
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_16K     1
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_32K     2
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K     3
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_128K    4
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_256K    5
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_512K    6
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_1024K   7
+#define IEEE80211_VHTCAP_LINK_ADAPT_MASK       0x0c000000
+#define IEEE80211_VHTCAP_LINK_ADAPT_SHIFT      26
+#define IEEE80211_VHTCAP_LINK_ADAPT_UNSOL_MFB  2
+#define IEEE80211_VHTCAP_LINK_ADAPT_MRQ_MFB    3
+#define IEEE80211_VHTCAP_RX_ANT_PATTERN                0x10000000
+#define IEEE80211_VHTCAP_TX_ANT_PATTERN                0x20000000
+
+/*
+ * VHT-MCS and NSS map (see 802.11ac-2013 8.4.2.160.3, Figure 8-401bs).
+ * Set of VHT MCS supported for a given number of spatial streams, `n'.
+ * Used by the VHT capabilities IE and by the basic VHT MSC set in
+ * the VHT operation IE.
+ */
+#define IEEE80211_VHT_MCS_FOR_SS_MASK(n)       (0x3 << (2*((n)-1)))
+#define IEEE80211_VHT_MCS_FOR_SS_SHIFT(n)      (2*((n)-1))
+#define IEEE80211_VHT_MCS_0_7          0
+#define IEEE80211_VHT_MCS_0_8          1
+#define IEEE80211_VHT_MCS_0_9          2
+#define IEEE80211_VHT_MCS_SS_NOT_SUPP  3
+
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_MASK      0x1fff
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_SHIFT     0
+
+/* The highest number of spatial streams supported by VHT. */
+#define IEEE80211_VHT_NUM_SS   8
+
+/*
+ * VHT Operation element (see 802.11ac-2013 8.4.2.161).
+ */
+/* Byte 0. */
+#define IEEE80211_VHTOP0_CHAN_WIDTH_MASK       0x03
+#define IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT      0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_HT         0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_80         1
+#define IEEE80211_VHTOP0_CHAN_WIDTH_160                2
+#define IEEE80211_VHTOP0_CHAN_WIDTH_8080       3
+/* Byte 1 contains channel center frequency index 0 for 80, 80+80, 160 MHz. */
+/* Byte 2 contains channel center frequency index 1 for 80+80 MHz only. */
+
+/*
  * EDCA Access Categories.
  */
 enum ieee80211_edca_ac {
blob - 1483da3b25a656f42c0d14d420998a0e4039a54f
blob + 8d928e16e1f5abd7e46143e116c00eab7d0e9619
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -1606,7 +1606,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
        const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
-       const u_int8_t *rsnie, *wpaie, *htcaps, *htop;
+       const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop;
        u_int16_t capinfo, bintval;
        u_int8_t chan, bchan, erp;
        int is_new;
@@ -1647,7 +1647,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
        capinfo = LE_READ_2(frm); frm += 2;
 
        ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
-       htcaps = htop = NULL;
+       htcaps = htop = vhtcaps = vhtop = NULL;
        bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
        chan = bchan;
        erp = 0;
@@ -1692,6 +1692,12 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
                case IEEE80211_ELEMID_HTOP:
                        htop = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
+               case IEEE80211_ELEMID_VHTOP:
+                       vhtop = frm;
+                       break;
                case IEEE80211_ELEMID_TIM:
                        if (frm[1] < 4) {
                                ic->ic_stats.is_rx_elem_toosmall++;
@@ -1779,6 +1785,11 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        if (htop && !ieee80211_setup_htop(ni, htop + 2, htop[1], 1))
                htop = NULL; /* invalid HTOP */
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+               if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+                       vhtop = NULL; /* invalid VHTOP */
+       }
 
        if (tim) {
                ni->ni_dtimcount = tim[2];
@@ -2021,7 +2032,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
 {
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
-       const u_int8_t *ssid, *rates, *xrates, *htcaps;
+       const u_int8_t *ssid, *rates, *xrates, *htcaps, *vhtcaps;
        u_int8_t rate;
 
        if (ic->ic_opmode == IEEE80211_M_STA ||
@@ -2032,7 +2043,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
        frm = (const u_int8_t *)&wh[1];
        efrm = mtod(m, u_int8_t *) + m->m_len;
 
-       ssid = rates = xrates = htcaps = NULL;
+       ssid = rates = xrates = htcaps = vhtcaps = NULL;
        while (frm + 2 <= efrm) {
                if (frm + 2 + frm[1] > efrm) {
                        ic->ic_stats.is_rx_elem_toosmall++;
@@ -2051,6 +2062,9 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
                case IEEE80211_ELEMID_HTCAPS:
                        htcaps = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
                }
                frm += 2 + frm[1];
        }
@@ -2101,6 +2115,10 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        else
                ieee80211_clear_htcaps(ni);
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+       else
+               ieee80211_clear_vhtcaps(ni);
        IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
 }
 #endif /* IEEE80211_STA_ONLY */
@@ -2170,7 +2188,8 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
 {
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
-       const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie, *htcaps;
+       const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie;
+       const u_int8_t *htcaps, *vhtcaps;
        u_int16_t capinfo, bintval;
        int resp, status = 0;
        struct ieee80211_rsnparams rsn;
@@ -2204,7 +2223,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
        } else
                resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
 
-       ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = NULL;
+       ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = vhtcaps = NULL;
        while (frm + 2 <= efrm) {
                if (frm + 2 + frm[1] > efrm) {
                        ic->ic_stats.is_rx_elem_toosmall++;
@@ -2228,6 +2247,9 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
                case IEEE80211_ELEMID_HTCAPS:
                        htcaps = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
                case IEEE80211_ELEMID_VENDOR:
                        if (frm[1] < 4) {
                                ic->ic_stats.is_rx_elem_toosmall++;
@@ -2479,6 +2501,10 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        else
                ieee80211_clear_htcaps(ni);
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+       else
+               ieee80211_clear_vhtcaps(ni);
  end:
        if (status != 0) {
                IEEE80211_SEND_MGMT(ic, ni, resp, status);
@@ -2507,6 +2533,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
        const struct ieee80211_frame *wh;
        const u_int8_t *frm, *efrm;
        const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
+       const u_int8_t *vhtcaps, *vhtop;
        u_int16_t capinfo, status, associd;
        u_int8_t rate;
 
@@ -2541,6 +2568,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
        associd = LE_READ_2(frm); frm += 2;
 
        rates = xrates = edcaie = wmmie = htcaps = htop = NULL;
+       vhtcaps = vhtop = NULL;
        while (frm + 2 <= efrm) {
                if (frm + 2 + frm[1] > efrm) {
                        ic->ic_stats.is_rx_elem_toosmall++;
@@ -2562,6 +2590,12 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
                case IEEE80211_ELEMID_HTOP:
                        htop = frm;
                        break;
+               case IEEE80211_ELEMID_VHTCAPS:
+                       vhtcaps = frm;
+                       break;
+               case IEEE80211_ELEMID_VHTOP:
+                       vhtop = frm;
+                       break;
                case IEEE80211_ELEMID_VENDOR:
                        if (frm[1] < 4) {
                                ic->ic_stats.is_rx_elem_toosmall++;
@@ -2609,8 +2643,17 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
                ieee80211_setup_htop(ni, htop + 2, htop[1], 0);
        ieee80211_ht_negotiate(ic, ni);
 
-       /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */
-       if (ni->ni_flags & IEEE80211_NODE_HT)
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+               ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+               if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+                       vhtop = NULL; /* invalid VHTOP */
+       }
+       ieee80211_vht_negotiate(ic, ni);
+
+       /* Hop into 11n/11ac modes after associating to a HT/VHT AP. */
+       if (ni->ni_flags & IEEE80211_NODE_VHT)
+               ieee80211_setmode(ic, IEEE80211_MODE_11AC);
+       else if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_setmode(ic, IEEE80211_MODE_11N);
        else
                ieee80211_setmode(ic, ieee80211_chan2mode(ic, ni->ni_chan));
blob - 65e93c23da2d86c0ad259f77b7c9affc85d9038b
blob + 32fc38fc29eeb977d19cd3e395f75da4f532ae67
--- sys/net80211/ieee80211_ioctl.h
+++ sys/net80211/ieee80211_ioctl.h
@@ -110,6 +110,8 @@ struct ieee80211_stats {
        u_int32_t       is_ht_rx_ba_window_gap_timeout;
        u_int32_t       is_ht_rx_ba_timeout;
        u_int32_t       is_ht_tx_ba_timeout;
+       u_int32_t       is_vht_nego_no_mandatory_mcs;
+       u_int32_t       is_vht_nego_no_basic_mcs;
 };
 
 #define        SIOCG80211STATS         _IOWR('i', 242, struct ifreq)
blob - 02c5a00fef8a8b714f214baacaeaffc2a67d348a
blob + c874c8746572eee71976e13d651d76cd8579098d
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -87,6 +87,7 @@ void ieee80211_node_join_ht(struct ieee80211com *, str
 void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_ht(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_node_leave_vht(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_node_leave_pwrsave(struct ieee80211com *,
@@ -2411,6 +2412,71 @@ ieee80211_setup_htop(struct ieee80211_node *ni, const 
 }
 
 /*
+ * Install received VHT caps information in the node's state block.
+ */
+void
+ieee80211_setup_vhtcaps(struct ieee80211_node *ni, const uint8_t *data,
+    uint8_t len)
+{
+       if (len != 12)
+               return;
+
+       ni->ni_vhtcaps = (data[0] | (data[1] << 8) | data[2] << 16 |
+           data[3] << 24);
+       ni->ni_vht_rxmcs = (data[4] | (data[5] << 8));
+       ni->ni_vht_rx_max_lgi_mbit_s = ((data[6] | (data[7] << 8)) &
+           IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+       ni->ni_vht_txmcs = (data[8] | (data[9] << 8));
+       ni->ni_vht_tx_max_lgi_mbit_s = ((data[10] | (data[11] << 8)) &
+           IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+
+       ni->ni_flags |= IEEE80211_NODE_VHTCAP;
+}
+
+/*
+ * Install received VHT op information in the node's state block.
+ */
+int
+ieee80211_setup_vhtop(struct ieee80211_node *ni, const uint8_t *data,
+    uint8_t len, int isprobe)
+{
+
+       if (len != 5)
+               return 0;
+
+       if (data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_HT &&
+           data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_80 &&
+           data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_160 &&
+           data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_8080)
+               return 0;
+
+       ni->ni_vht_chan_width = data[0];
+       ni->ni_vht_chan_center_freq_idx0 = data[1];
+       ni->ni_vht_chan_center_freq_idx1 = data[2];
+       ni->ni_vht_basic_mcs = (data[3] | data[4] << 8);
+       return 1;
+}
+
+#ifndef IEEE80211_STA_ONLY
+/* 
+ * Handle nodes switching from 11ac into legacy modes.
+ */
+void
+ieee80211_clear_vhtcaps(struct ieee80211_node *ni)
+{
+       ni->ni_vhtcaps = 0;
+       ni->ni_vht_rxmcs = 0;
+       ni->ni_vht_rx_max_lgi_mbit_s = 0;
+       ni->ni_vht_txmcs = 0;
+       ni->ni_vht_tx_max_lgi_mbit_s = 0;
+
+       ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+           IEEE80211_NODE_VHT_SGI160 | IEEE80211_NODE_VHTCAP);
+
+}
+#endif
+
+/*
  * Install received rate set information in the node's state block.
  */
 int
@@ -2772,6 +2838,15 @@ ieee80211_node_leave_ht(struct ieee80211com *ic, struc
 }
 
 /*
+ * Handle a VHT STA leaving a VHT network.
+ */
+void
+ieee80211_node_leave_vht(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+       ieee80211_clear_vhtcaps(ni);
+}
+
+/*
  * Handle a station leaving an RSN network.
  */
 void
@@ -2910,6 +2985,8 @@ ieee80211_node_leave(struct ieee80211com *ic, struct i
 
        if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_node_leave_ht(ic, ni);
+       if (ni->ni_flags & IEEE80211_NODE_VHT)
+               ieee80211_node_leave_vht(ic, ni);
 
        if (ic->ic_node_leave != NULL)
                (*ic->ic_node_leave)(ic, ni);
blob - 82cddbfb3a65784f65eaeaca87ca1efd8f619d2f
blob + e64e3ec26b20b7bc5c38cb6c07aede94cf7a6a8f
--- sys/net80211/ieee80211_node.h
+++ sys/net80211/ieee80211_node.h
@@ -352,6 +352,19 @@ struct ieee80211_node {
        uint16_t                ni_htop2;
        uint8_t                 ni_basic_mcs[howmany(128,NBBY)];
 
+       /* VHT capabilities */
+       uint32_t                ni_vhtcaps;
+       uint16_t                ni_vht_rxmcs;
+       uint16_t                ni_vht_rx_max_lgi_mbit_s;
+       uint16_t                ni_vht_txmcs;
+       uint16_t                ni_vht_tx_max_lgi_mbit_s;
+
+       /* VHT operation */
+       uint8_t                 ni_vht_chan_width;
+       uint8_t                 ni_vht_chan_center_freq_idx0;
+       uint8_t                 ni_vht_chan_center_freq_idx1;
+       uint16_t                ni_vht_basic_mcs;
+
        /* Timeout handlers which trigger Tx Block Ack negotiation. */
        struct timeout          ni_addba_req_to[IEEE80211_NUM_TID];
        int                     ni_addba_req_intval[IEEE80211_NUM_TID];
@@ -406,6 +419,9 @@ struct ieee80211_node {
 #define IEEE80211_NODE_HT_SGI40                0x8000  /* SGI on 40 MHz 
negotiated */ 
 #define IEEE80211_NODE_VHT             0x10000 /* VHT negotiated */
 #define IEEE80211_NODE_HTCAP           0x20000 /* claims to support HT */
+#define IEEE80211_NODE_VHTCAP          0x40000 /* claims to support VHT */
+#define IEEE80211_NODE_VHT_SGI80       0x80000 /* SGI on 80 MHz negotiated */ 
+#define IEEE80211_NODE_VHT_SGI160      0x100000 /* SGI on 160 MHz negotiated 
*/ 
 
        /* If not NULL, this function gets called when ni_refcnt hits zero. */
        void                    (*ni_unref_cb)(struct ieee80211com *,
@@ -500,6 +516,64 @@ ieee80211_node_supports_ht_chan40(struct ieee80211_nod
            (ni->ni_htop0 & IEEE80211_HTOP0_CHW));
 }
 
+/* 
+ * Check if the peer supports VHT.
+ * Require a VHT capabilities IE and support for VHT MCS with a single
+ * spatial stream.
+ */
+static inline int
+ieee80211_node_supports_vht(struct ieee80211_node *ni)
+{
+       uint16_t rx_mcs;
+
+       rx_mcs = (ni->ni_vht_rxmcs & IEEE80211_VHT_MCS_FOR_SS_MASK(1)) >>
+           IEEE80211_VHT_MCS_FOR_SS_SHIFT(1);
+
+       return ((ni->ni_flags & IEEE80211_NODE_VHTCAP) &&
+           rx_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 80 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi80(struct ieee80211_node *ni)
+{
+       return ieee80211_node_supports_vht(ni) &&
+           (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 160 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi160(struct ieee80211_node *ni)
+{
+       return ieee80211_node_supports_vht(ni) &&
+           (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160);
+}
+
+/* Check if the peer can receive frames sent on an 80 MHz channel. */
+static inline int
+ieee80211_node_supports_vht_chan80(struct ieee80211_node *ni)
+{
+       uint8_t cap_chan_width, op_chan_width;
+
+       if (!ieee80211_node_supports_vht(ni))
+               return 0;
+
+       cap_chan_width = (ni->ni_vhtcaps & IEEE80211_VHTCAP_CHAN_WIDTH_MASK) >>
+           IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT;
+       if (cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_80 &&  
+           cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160 &&         
+           cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160_8080)
+               return 0;
+
+       op_chan_width = (ni->ni_vht_chan_width &
+           IEEE80211_VHTOP0_CHAN_WIDTH_MASK) >>
+           IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT;
+
+       return (op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80 ||
+           op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160 ||
+           op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_8080);
+}
+
 struct ieee80211com;
 
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
@@ -537,6 +611,11 @@ void ieee80211_setup_htcaps(struct ieee80211_node *, c
 void ieee80211_clear_htcaps(struct ieee80211_node *);
 int ieee80211_setup_htop(struct ieee80211_node *, const uint8_t *,
     uint8_t, int);
+void ieee80211_setup_vhtcaps(struct ieee80211_node *, const uint8_t *,
+    uint8_t);
+void ieee80211_clear_vhtcaps(struct ieee80211_node *);
+int ieee80211_setup_vhtop(struct ieee80211_node *, const uint8_t *,
+    uint8_t, int);
 int ieee80211_setup_rates(struct ieee80211com *,
            struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int);
 void ieee80211_node_trigger_addba_req(struct ieee80211_node *, int);
blob - 16373cfbb38daeda4f52916589fe803fad3739f5
blob + 3a31fc3f292b7c14b1b52ee086238af5f022a722
--- sys/net80211/ieee80211_output.c
+++ sys/net80211/ieee80211_output.c
@@ -319,6 +319,12 @@ const struct ieee80211_edca_ac_params
                [EDCA_AC_VI] = { 3,  4, 2,  94 },
                [EDCA_AC_VO] = { 2,  3, 2,  47 }
        },
+       [IEEE80211_MODE_11AC] = {
+               [EDCA_AC_BK] = { 4, 10, 7,   0 },
+               [EDCA_AC_BE] = { 4, 10, 3,   0 },
+               [EDCA_AC_VI] = { 3,  4, 2,  94 },
+               [EDCA_AC_VO] = { 2,  3, 2,  47 }
+       },
 };
 
 #ifndef IEEE80211_STA_ONLY
@@ -348,6 +354,12 @@ const struct ieee80211_edca_ac_params
                [EDCA_AC_VI] = { 3,  4, 1,  94 },
                [EDCA_AC_VO] = { 2,  3, 1,  47 }
        },
+       [IEEE80211_MODE_11AC] = {
+               [EDCA_AC_BK] = { 4, 10, 7,   0 },
+               [EDCA_AC_BE] = { 4,  6, 3,   0 },
+               [EDCA_AC_VI] = { 3,  4, 1,  94 },
+               [EDCA_AC_VO] = { 2,  3, 1,  47 }
+       },
 };
 #endif /* IEEE80211_STA_ONLY */
 
@@ -1177,6 +1189,22 @@ ieee80211_add_htop(u_int8_t *frm, struct ieee80211com 
 }
 #endif /* !IEEE80211_STA_ONLY */
 
+/*
+ * Add a VHT Capabilities element to a frame (see 802.11ac-2013 8.4.2.160.2).
+ */
+u_int8_t *
+ieee80211_add_vhtcaps(u_int8_t *frm, struct ieee80211com *ic)
+{
+       *frm++ = IEEE80211_ELEMID_VHTCAPS;
+       *frm++ = 12;
+       LE_WRITE_4(frm, ic->ic_vhtcaps); frm += 4;
+       LE_WRITE_2(frm, ic->ic_vht_rxmcs); frm += 2;
+       LE_WRITE_2(frm, ic->ic_vht_rx_max_lgi_mbit_s); frm += 2;
+       LE_WRITE_2(frm, ic->ic_vht_txmcs); frm += 2;
+       LE_WRITE_2(frm, ic->ic_vht_tx_max_lgi_mbit_s); frm += 2;
+       return frm;
+}
+
 #ifndef IEEE80211_STA_ONLY
 /*
  * Add a Timeout Interval element to a frame (see 7.3.2.49).
@@ -1234,7 +1262,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struc
            2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
            ((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
                2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
-           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+           ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1247,6 +1276,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struc
                frm = ieee80211_add_htcaps(frm, ic);
                frm = ieee80211_add_wme_info(frm, ic);
        }
+       if (ic->ic_flags & IEEE80211_F_VHTON)
+               frm = ieee80211_add_htcaps(frm, ic);
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
@@ -1414,7 +1445,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struc
            (((ic->ic_flags & IEEE80211_F_RSNON) &&
              (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
                2 + IEEE80211_WPAIE_MAXLEN : 0) +
-           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+           ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+           ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1449,6 +1481,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struc
                frm = ieee80211_add_htcaps(frm, ic);
                frm = ieee80211_add_wme_info(frm, ic);
        }
+       if (ic->ic_flags & IEEE80211_F_VHTON)
+               frm = ieee80211_add_vhtcaps(frm, ic);
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
blob - afffb2b26dff94a28845e6fa407de62fce8aaf15
blob + f6b8501977be0a209efd6af8ac36744dee17c750
--- sys/net80211/ieee80211_proto.c
+++ sys/net80211/ieee80211_proto.c
@@ -75,6 +75,7 @@ const char * const ieee80211_phymode_name[] = {
        "11b",          /* IEEE80211_MODE_11B */
        "11g",          /* IEEE80211_MODE_11G */
        "11n",          /* IEEE80211_MODE_11N */
+       "11ac",         /* IEEE80211_MODE_11AC */
 };
 
 void ieee80211_set_beacon_miss_threshold(struct ieee80211com *);
@@ -616,6 +617,58 @@ ieee80211_ht_negotiate(struct ieee80211com *ic, struct
 }
 
 void
+ieee80211_vht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+       int n;
+
+       ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+           IEEE80211_NODE_VHT_SGI160);
+
+       /* Check if we support VHT. */
+       if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11AC)) == 0)
+               return;
+
+       /* Check if VHT support has been explicitly disabled. */
+       if ((ic->ic_flags & IEEE80211_F_VHTON) == 0)
+               return;
+
+       /*
+        * Check if the peer supports VHT.
+        * MCS 0-7 for a single spatial stream are mandatory.
+        */
+       if (!ieee80211_node_supports_vht(ni)) {
+               ic->ic_stats.is_vht_nego_no_mandatory_mcs++;
+               return;
+       }
+
+       if (ic->ic_opmode == IEEE80211_M_STA) {
+               /* We must support the AP's basic MCS set. */
+               for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+                       uint16_t basic_mcs = (ni->ni_vht_basic_mcs &
+                           IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+                           IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+                       uint16_t rx_mcs = (ic->ic_vht_rxmcs &
+                           IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+                           IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+                       if (basic_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP &&
+                           basic_mcs > rx_mcs) {
+                               ic->ic_stats.is_vht_nego_no_basic_mcs++;
+                               return;
+                       }
+               }
+       }
+
+       ni->ni_flags |= IEEE80211_NODE_VHT;
+
+       if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80) &&
+           (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI80))
+               ni->ni_flags |= IEEE80211_NODE_VHT_SGI80;
+       if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160) &&
+           (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI160))
+               ni->ni_flags |= IEEE80211_NODE_VHT_SGI160;
+}
+
+void
 ieee80211_tx_ba_timeout(void *arg)
 {
        struct ieee80211_tx_ba *ba = arg;
@@ -1259,7 +1312,7 @@ justcleanup:
                                else
                                        printf(" start %u%sMb",
                                            rate / 2, (rate & 1) ? ".5" : "");
-                               printf(" %s preamble %s slot time%s%s\n",
+                               printf(" %s preamble %s slot time%s%s%s\n",
                                    (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
                                        "short" : "long",
                                    (ic->ic_flags & IEEE80211_F_SHSLOT) ?
@@ -1267,7 +1320,9 @@ justcleanup:
                                    (ic->ic_flags & IEEE80211_F_USEPROT) ?
                                        " protection enabled" : "",
                                    (ni->ni_flags & IEEE80211_NODE_HT) ?
-                                       " HT enabled" : "");
+                                       " HT enabled" : "",
+                                   (ni->ni_flags & IEEE80211_NODE_VHT) ?
+                                       " VHT enabled" : "");
                        }
                        if (!(ic->ic_flags & IEEE80211_F_RSNON)) {
                                /*
blob - da0ab9168df1548f3ad0cff640e7211373c7c6ce
blob + 04292acf1a90b9776c080075410f12a9f1053f0b
--- sys/net80211/ieee80211_proto.h
+++ sys/net80211/ieee80211_proto.h
@@ -138,6 +138,7 @@ extern      u_int8_t *ieee80211_add_xrates(u_int8_t *,
                const struct ieee80211_rateset *);
 extern u_int8_t *ieee80211_add_htcaps(u_int8_t *, struct ieee80211com *);
 extern u_int8_t *ieee80211_add_htop(u_int8_t *, struct ieee80211com *);
+extern u_int8_t *ieee80211_add_vhtcaps(u_int8_t *, struct ieee80211com *);
 extern u_int8_t *ieee80211_add_tie(u_int8_t *, u_int8_t, u_int32_t);
 
 extern int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *,
@@ -168,6 +169,8 @@ extern      void ieee80211_sa_query_request(struct ieee8021
            struct ieee80211_node *);
 extern void ieee80211_ht_negotiate(struct ieee80211com *,
     struct ieee80211_node *);
+extern void ieee80211_vht_negotiate(struct ieee80211com *,
+    struct ieee80211_node *);
 extern void ieee80211_tx_ba_timeout(void *);
 extern void ieee80211_rx_ba_timeout(void *);
 extern int ieee80211_addba_request(struct ieee80211com *,
blob - 5be62abf361d73ee696752904b3a334b4ac65f38
blob + a51c1bc427c88add9132d7b5f1ed186bd151625a
--- sys/net80211/ieee80211_radiotap.h
+++ sys/net80211/ieee80211_radiotap.h
@@ -193,6 +193,7 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_CHAN_XR      0x1000  /* Extended range OFDM channel */
 #define IEEE80211_CHAN_HT      0x2000  /* 11n/HT channel */
 #define IEEE80211_CHAN_VHT     0x4000  /* 11ac/VHT channel */
+#define IEEE80211_CHAN_40MHZ   0x8000  /* use of 40 MHz is allowed */
 #endif /* !_KERNEL */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
blob - 161853a629887a1aa997480d605d4febd66dd2db
blob + d9b8d5cd8832ecf5264539c16c4d1cad78e5ad17
--- sys/net80211/ieee80211_var.h
+++ sys/net80211/ieee80211_var.h
@@ -120,6 +120,7 @@ enum ieee80211_protmode {
 struct ieee80211_channel {
        u_int16_t       ic_freq;        /* setting in MHz */
        u_int16_t       ic_flags;       /* see below */
+       u_int32_t       ic_xflags;      /* extra flags; see below */
 };
 
 /*
@@ -137,6 +138,12 @@ struct ieee80211_channel {
 #define IEEE80211_CHAN_40MHZ   0x8000  /* use of 40 MHz is allowed */
 
 /*
+ * Extra channel flags.
+ */
+#define IEEE80211_CHANX_80MHZ  0x00000001 /* use of 80 MHz is allowed */
+#define IEEE80211_CHANX_160MHZ 0x00000002 /* use of 160 MHz is allowed */
+
+/*
  * Useful combinations of channel characteristics.
  */
 #define IEEE80211_CHAN_A \
@@ -172,6 +179,13 @@ struct ieee80211_channel {
 #define        IEEE80211_IS_CHAN_XR(_c) \
        (((_c)->ic_flags & IEEE80211_CHAN_XR) != 0)
 
+#define        IEEE80211_CHAN_40MHZ_ALLOWED(_c) \
+       (((_c)->ic_flags & IEEE80211_CHAN_40MHZ) != 0)
+#define        IEEE80211_CHAN_80MHZ_ALLOWED(_c) \
+       (((_c)->ic_xflags & IEEE80211_CHANX_80MHZ) != 0)
+#define        IEEE80211_CHAN_160MHZ_ALLOWED(_c) \
+       (((_c)->ic_xflags & IEEE80211_CHANX_160MHZ) != 0)
+
 /*
  * EDCA AC parameters.
  */
@@ -362,6 +376,13 @@ struct ieee80211com {
        u_int8_t                ic_aselcaps;
        u_int8_t                ic_dialog_token;
        int                     ic_fixed_mcs;
+
+       uint32_t                ic_vhtcaps;
+       uint16_t                ic_vht_rxmcs;
+       uint16_t                ic_vht_rx_max_lgi_mbit_s;
+       uint16_t                ic_vht_txmcs;
+       uint16_t                ic_vht_tx_max_lgi_mbit_s;
+
        TAILQ_HEAD(, ieee80211_ess)      ic_ess;
 };
 #define        ic_if           ic_ac.ac_if



Reply via email to