On Thu, Mar 10, 2022 at 12:25:17PM +0100, Stefan Sperling wrote:
> Unless anyone else finds a problem, this patch can be considered ready
> for review and commit.

Of course, I forgot to apply my sysassert fix to the second phy context
command function...  Fixed here.

diff refs/heads/master refs/heads/11ac
blob - 57bdcce64458e7f9d5802ce4247d5651f9183200
blob + 61022a02f457141e31ec6c04351519fedf7b5782
--- 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,12 @@ 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)
+                               channel->ic_xflags |= IEEE80211_CHANX_80MHZ;
+               }
        }
 }
 
@@ -2846,6 +2856,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 +3185,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 +3197,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 +4029,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 +4859,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 +4915,10 @@ 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) {
+               cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+               cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
+       } 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 +4949,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 +4971,10 @@ 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) {
+               cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+               cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
+       } 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 +5006,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 +5026,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 +5351,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 +5479,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 +5905,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 +5942,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 +6210,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 +6975,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 +7027,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 +7089,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 +7122,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 +7133,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 +7142,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 +7152,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 +7162,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 +7178,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 +7298,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 +7328,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 +7483,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 +8134,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 +8246,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 +9504,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 +9732,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 +9746,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