This diff adds an initial implementation of 802.11n.

It is functional but mostly adds foundations to build future work on top of.
It completes the little bits of 11n code added by damien@ years ago.
It supports all non-optional 11n features for STA (client) operation.

It adds 11n support only for the iwm(4) driver.
Support for more drivers will be added later.

 - Add a new '11n' mode with all the ifmedia goo that goes with it.
   This is straightforward because the ifmedia word was extended
   to 64 bits back in September during the l2k15 hackathon in
   preparation for 11n.

 - Add a concept of MCS (modulation coding scheme) alongside the
   "rates" used in 11a/b/g. My initial plan was to extend the
   ieee80211_rateset structure until I noticed this struct is
   copied out verbatim into frames during 11a/b/g operation.
   So changing its size is not an option and I'm adding MCS
   as a new concept which exists only in 11n mode.
   The mandatory MCS 0-7 are supported, providing a maximum
   theoretical data rate of 65Mbit/s.

 - Add more 11n related state to various data structures,
   most notably ieee80211_node and ieee80211com.

 - Add support for block ack agreements and receiving A-MPDUs and A-MSDUs.
   This mostly builds on code which damien@ added years ago.
   I'm very happy he left a complete implementation for me to find.
   Sending A-MPDUs is left for later (the sender has to initiate block
   ack, and since we don't our 11n peers won't care, so no problem).

The 11n standard adds a lot of new features, most of which are optional.
The optional 11n features which this diff does not add support for include:

  - MIMO (MCS 8 and above)
  - Short guard interval aka SGI (improves throughput)
  - 40 MHz wide channels

I've been testing against OpenWRT and Fritzbox APs based on the
ath10k chipset. Testing against all sorts of APs is much appreciated.

Apart from 11n operation I've been testing 11a/b/g operation as well.
Everything seems to work.

The diff has survived a full 'make release' + install cycle on amd64.
Most development and testing was done on i386.

To test this diff, apply it to /usr/src/sys, compile a new kernel
and reboot into it. Then install the new if_media.h header and
recompile ifconfig:

doas install -m 444 -o root -g bin  /usr/src/sys/net/if_media.h 
/usr/include/net/
cd /usr/src/sbin/ifconfig
make obj
make depend
make
doas make install

`ifconfig iwm0 media' should show an additional line advertising 11n support:

        media autoselect mode 11n

While associated to an 11n AP, `ifconfig iwm0' should indicate the MCS
being used for transmissions, e.g.:

        media: IEEE802.11 autoselect (HT-MCS7 mode 11n)


This work is being supported by genua gmbh which allows me to spend
the necessary time to keep working on 11n and do my best at building
this feature together with the OpenBSD community.


Index: dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.63
diff -u -p -r1.63 if_iwm.c
--- dev/pci/if_iwm.c    4 Nov 2015 12:11:59 -0000       1.63
+++ dev/pci/if_iwm.c    12 Nov 2015 15:33:07 -0000
@@ -175,19 +175,22 @@ const uint8_t iwm_nvm_channels[] = {
 const struct iwm_rate {
        uint8_t rate;
        uint8_t plcp;
+       uint8_t ht_plcp;
 } iwm_rates[] = {
-       {   2,  IWM_RATE_1M_PLCP  },
-       {   4,  IWM_RATE_2M_PLCP  },
-       {  11,  IWM_RATE_5M_PLCP  },
-       {  22,  IWM_RATE_11M_PLCP },
-       {  12,  IWM_RATE_6M_PLCP  },
-       {  18,  IWM_RATE_9M_PLCP  },
-       {  24,  IWM_RATE_12M_PLCP },
-       {  36,  IWM_RATE_18M_PLCP },
-       {  48,  IWM_RATE_24M_PLCP },
-       {  72,  IWM_RATE_36M_PLCP },
-       {  96,  IWM_RATE_48M_PLCP },
-       { 108,  IWM_RATE_54M_PLCP },
+               /* Legacy */            /* HT */
+       {   2,  IWM_RATE_1M_PLCP,       IWM_RATE_HT_SISO_MCS_INV_PLCP  },
+       {   4,  IWM_RATE_2M_PLCP,       IWM_RATE_HT_SISO_MCS_INV_PLCP },
+       {  11,  IWM_RATE_5M_PLCP,       IWM_RATE_HT_SISO_MCS_INV_PLCP  },
+       {  22,  IWM_RATE_11M_PLCP,      IWM_RATE_HT_SISO_MCS_INV_PLCP },
+       {  12,  IWM_RATE_6M_PLCP,       IWM_RATE_HT_SISO_MCS_0_PLCP },
+       {  18,  IWM_RATE_9M_PLCP,       IWM_RATE_HT_SISO_MCS_INV_PLCP  },
+       {  24,  IWM_RATE_12M_PLCP,      IWM_RATE_HT_SISO_MCS_1_PLCP },
+       {  36,  IWM_RATE_18M_PLCP,      IWM_RATE_HT_SISO_MCS_2_PLCP },
+       {  48,  IWM_RATE_24M_PLCP,      IWM_RATE_HT_SISO_MCS_3_PLCP },
+       {  72,  IWM_RATE_36M_PLCP,      IWM_RATE_HT_SISO_MCS_4_PLCP },
+       {  96,  IWM_RATE_48M_PLCP,      IWM_RATE_HT_SISO_MCS_5_PLCP },
+       { 108,  IWM_RATE_54M_PLCP,      IWM_RATE_HT_SISO_MCS_6_PLCP },
+       { 128,  IWM_RATE_INVM_PLCP,     IWM_RATE_HT_SISO_MCS_7_PLCP },
 };
 #define IWM_RIDX_CCK   0
 #define IWM_RIDX_OFDM  4
@@ -195,6 +198,18 @@ const struct iwm_rate {
 #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
 #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
 
+/* Convert an MCS index into an iwm_rates[] index. */
+const int iwm_mcs2ridx[] = {
+       IWM_RATE_MCS_0_INDEX,
+       IWM_RATE_MCS_1_INDEX,
+       IWM_RATE_MCS_2_INDEX,
+       IWM_RATE_MCS_3_INDEX,
+       IWM_RATE_MCS_4_INDEX,
+       IWM_RATE_MCS_5_INDEX,
+       IWM_RATE_MCS_6_INDEX,
+       IWM_RATE_MCS_7_INDEX,
+};
+
 int    iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t);
 int    iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type,
                                        uint8_t *, size_t);
@@ -283,6 +298,24 @@ int        iwm_nvm_read_chunk(struct iwm_softc 
 int    iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
                                uint16_t *);
 void   iwm_init_channel_map(struct iwm_softc *, const uint16_t * const);
+#ifndef IEEE80211_NO_HT
+void   iwm_setup_ht_rates(struct iwm_softc *);
+int    iwm_ampdu_rx_start(struct ieee80211com *,
+                   struct ieee80211_node *, uint8_t);
+void   iwm_ampdu_rx_stop(struct ieee80211com *,
+                   struct ieee80211_node *, uint8_t);
+void   iwm_mvm_sta_rx_agg(struct iwm_softc *, struct ieee80211_node *,
+                               uint8_t, uint16_t, int);
+#ifdef notyet
+int    iwm_ampdu_tx_start(struct ieee80211com *,
+                   struct ieee80211_node *, uint8_t);
+void   iwm_ampdu_tx_stop(struct ieee80211com *,
+                   struct ieee80211_node *, uint8_t);
+#endif
+void   iwm_ba_task(void *);
+
+#endif /* IEEE80211_NO_HT */
+
 int    iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *,
                                const uint16_t *, const uint16_t *, uint8_t,
                                uint8_t);
@@ -2375,6 +2408,13 @@ iwm_nvm_read_section(struct iwm_softc *s
  * BEGIN IWM_NVM_PARSE
  */
 
+#define IWM_FW_VALID_TX_ANT(sc) \
+    ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) \
+    >> IWM_FW_PHY_CFG_TX_CHAIN_POS)
+#define IWM_FW_VALID_RX_ANT(sc) \
+    ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) \
+    >> IWM_FW_PHY_CFG_RX_CHAIN_POS)
+
 /* NVM offsets (in words) definitions */
 enum wkp_nvm_offsets {
        /* NVM HW-Section offset (in words) definitions */
@@ -2399,6 +2439,7 @@ enum nvm_sku_bits {
        IWM_NVM_SKU_CAP_BAND_52GHZ      = (1 << 1),
        IWM_NVM_SKU_CAP_11N_ENABLE      = (1 << 2),
        IWM_NVM_SKU_CAP_11AC_ENABLE     = (1 << 3),
+       IWM_NVM_SKU_CAP_MIMO_DISABLE    = (1 << 5),
 };
 
 /* radio config bits (actual values from NVM definition) */
@@ -2482,9 +2523,146 @@ iwm_init_channel_map(struct iwm_softc *s
 
                if (!(ch_flags & IWM_NVM_CHANNEL_ACTIVE))
                        channel->ic_flags |= IEEE80211_CHAN_PASSIVE;
+
+#ifndef IEEE80211_NO_HT
+               if (data->sku_cap_11n_enable)
+                       channel->ic_flags |= IEEE80211_CHAN_HT;
+#endif
+       }
+}
+
+#ifndef IEEE80211_NO_HT
+void
+iwm_setup_ht_rates(struct iwm_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       /* TX is supported with the same MCS as RX. */
+       ic->ic_tx_mcs_set = IEEE80211_TX_MCS_SET_DEFINED;
+
+       ic->ic_sup_mcs[0] = 0xff;               /* MCS 0-7 */
+
+#ifdef notyet
+       if (sc->sc_nvm.sku_cap_mimo_disable)
+               return;
+
+       if (IWM_FW_VALID_RX_ANT(sc) > 1)
+               ic->ic_sup_mcs[1] = 0xff;       /* MCS 7-15 */
+       if (IWM_FW_VALID_RX_ANT(sc) > 2)
+               ic->ic_sup_mcs[2] = 0xff;       /* MCS 16-23 */
+#endif
+}
+
+#define IWM_MAX_RX_BA_SESSIONS 16
+
+void
+iwm_mvm_sta_rx_agg(struct iwm_softc *sc, struct ieee80211_node *ni,
+    uint8_t tid, uint16_t ssn, int start)
+{
+       struct iwm_mvm_add_sta_cmd_v6 cmd;
+       struct iwm_node *in = (void *)ni;
+       int ret, s;
+       uint32_t status;
+
+       if (start && sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS)
+               return;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.sta_id = IWM_STATION_ID;
+       cmd.mac_id_n_color
+           = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color));
+       cmd.add_modify = IWM_STA_MODE_MODIFY;
+
+       if (start) {
+               cmd.add_immediate_ba_tid = (uint8_t)tid;
+               cmd.add_immediate_ba_ssn = ssn;
+       } else {
+               cmd.remove_immediate_ba_tid = (uint8_t)tid;
+       }
+       cmd.modify_mask = start ? IWM_STA_MODIFY_ADD_BA_TID :
+           IWM_STA_MODIFY_REMOVE_BA_TID;
+
+       status = IWM_ADD_STA_SUCCESS;
+       ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status);
+       if (ret)
+               return;
+
+       switch (status) {
+       case IWM_ADD_STA_SUCCESS:
+               DPRINTF(("RX BA Session %sed in fw\n",
+                   start ? "start" : "stopp"));
+               s = splnet();
+               if (start)
+                       sc->sc_rx_ba_sessions++;
+               else if (sc->sc_rx_ba_sessions > 0)
+                       /* check that restart flow didn't zero counter */
+                       sc->sc_rx_ba_sessions--;
+               splx(s);
+               break;
+       case IWM_ADD_STA_IMMEDIATE_BA_FAILURE:
+               ret = EIO;
+               DPRINTF(("RX BA Session refused by fw\n"));
+               break;
+       default:
+               ret = EIO;
+               DPRINTF(("RX BA Session failed %sing, status 0x%x\n",
+                   start ? "start" : "stopp", status));
+               break;
        }
 }
 
+void
+iwm_ba_task(void *arg)
+{
+       struct iwm_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_node *ni = ic->ic_bss;
+       
+       if (sc->ba_start)
+               iwm_mvm_sta_rx_agg(sc, ni, sc->ba_tid, sc->ba_ssn, 1);
+       else
+               iwm_mvm_sta_rx_agg(sc, ni, sc->ba_tid, 0, 0);
+}
+
+/*
+ * This function is called by upper layer when an ADDBA request is received
+ * from another STA and before the ADDBA response is sent.
+ */
+int
+iwm_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
+    uint8_t tid)
+{
+       struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
+       struct iwm_softc *sc = IC2IFP(ic)->if_softc;
+
+       if (sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS)
+               return ENOSPC;
+
+       sc->ba_start = 1;
+       sc->ba_tid = tid;
+       sc->ba_ssn = htole16(ba->ba_winstart);
+       task_add(systq, &sc->ba_task);
+
+       return 0; /* XXX firmware may still fail to add BA agreement... */
+}
+
+/*
+ * This function is called by upper layer on teardown of an HT-immediate
+ * Block Ack agreement (eg. uppon receipt of a DELBA frame).
+ */
+void
+iwm_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
+    uint8_t tid)
+{
+       struct iwm_softc *sc = IC2IFP(ic)->if_softc;
+
+       sc->ba_start = 0;
+       sc->ba_tid = tid;
+       task_add(systq, &sc->ba_task);
+}
+#endif /* IEEE80211_NO_HT */
+
 int
 iwm_parse_nvm_data(struct iwm_softc *sc,
        const uint16_t *nvm_hw, const uint16_t *nvm_sw,
@@ -2507,7 +2685,13 @@ iwm_parse_nvm_data(struct iwm_softc *sc,
        sku = le16_to_cpup(nvm_sw + IWM_SKU);
        data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ;
        data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ;
+#ifndef IEEE80211_NO_HT
+       data->sku_cap_11n_enable = sku & IWM_NVM_SKU_CAP_11N_ENABLE;
+       data->sku_cap_mimo_disable = sku & IWM_NVM_SKU_CAP_MIMO_DISABLE;
+#else
        data->sku_cap_11n_enable = 0;
+       data->sku_cap_mimo_disable = 1;
+#endif
 
        if (!data->valid_tx_ant || !data->valid_rx_ant) {
                DPRINTF(("%s: invalid antennas (0x%x, 0x%x)\n",
@@ -2549,13 +2733,6 @@ struct iwm_nvm_section {
        const uint8_t *data;
 };
 
-#define IWM_FW_VALID_TX_ANT(sc) \
-    ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) \
-    >> IWM_FW_PHY_CFG_TX_CHAIN_POS)
-#define IWM_FW_VALID_RX_ANT(sc) \
-    ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) \
-    >> IWM_FW_PHY_CFG_RX_CHAIN_POS)
-
 int
 iwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections)
 {
@@ -3080,23 +3257,35 @@ iwm_mvm_rx_rx_mpdu(struct iwm_softc *sc,
                tap->wr_dbm_antsignal = (int8_t)rssi;
                tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
                tap->wr_tsft = phy_info->system_timestamp;
-               switch (phy_info->rate) {
-               /* CCK rates. */
-               case  10: tap->wr_rate =   2; break;
-               case  20: tap->wr_rate =   4; break;
-               case  55: tap->wr_rate =  11; break;
-               case 110: tap->wr_rate =  22; break;
-               /* OFDM rates. */
-               case 0xd: tap->wr_rate =  12; break;
-               case 0xf: tap->wr_rate =  18; break;
-               case 0x5: tap->wr_rate =  24; break;
-               case 0x7: tap->wr_rate =  36; break;
-               case 0x9: tap->wr_rate =  48; break;
-               case 0xb: tap->wr_rate =  72; break;
-               case 0x1: tap->wr_rate =  96; break;
-               case 0x3: tap->wr_rate = 108; break;
-               /* Unknown rate: should not happen. */
-               default:  tap->wr_rate =   0;
+               if (phy_info->phy_flags &
+                   htole16(IWM_RX_RES_PHY_FLAGS_OFDM_HT)) {
+#ifdef notyet
+                       uint8_t mcs = (phy_info->rate_n_flags &
+                           htole32(IWM_RATE_HT_MCS_RATE_CODE_MSK));
+#endif
+                       /* XXX need a way to pass current MCS in 11n mode */
+                       tap->wr_rate = 0;
+               } else {
+                       uint8_t rate = (phy_info->rate_n_flags &
+                           htole32(IWM_RATE_LEGACY_RATE_MSK));
+                       switch (rate) {
+                       /* CCK rates. */
+                       case  10: tap->wr_rate =   2; break;
+                       case  20: tap->wr_rate =   4; break;
+                       case  55: tap->wr_rate =  11; break;
+                       case 110: tap->wr_rate =  22; break;
+                       /* OFDM rates. */
+                       case 0xd: tap->wr_rate =  12; break;
+                       case 0xf: tap->wr_rate =  18; break;
+                       case 0x5: tap->wr_rate =  24; break;
+                       case 0x7: tap->wr_rate =  36; break;
+                       case 0x9: tap->wr_rate =  48; break;
+                       case 0xb: tap->wr_rate =  72; break;
+                       case 0x1: tap->wr_rate =  96; break;
+                       case 0x3: tap->wr_rate = 108; break;
+                       /* Unknown rate: should not happen. */
+                       default:  tap->wr_rate =   0;
+                       }
                }
 
                mb.m_data = (caddr_t)tap;
@@ -3665,7 +3854,7 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, st
        struct ieee80211_node *ni = &in->in_ni;
        const struct iwm_rate *rinfo;
        int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
-       int ridx, rate_flags;
+       int ridx, rate_flags, i;
        int nrates = ni->ni_rates.rs_nrates;
 
        tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT;
@@ -3673,16 +3862,40 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, st
 
        if (type != IEEE80211_FC0_TYPE_DATA) {
                /* for non-data, use the lowest supported rate */
-               ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+               ridx = (ic->ic_curmode == IEEE80211_MODE_11A ||
+                   ic->ic_curmode == IEEE80211_MODE_11N) ?
                    IWM_RIDX_OFDM : IWM_RIDX_CCK;
+#ifndef IEEE80211_NO_HT
+       } else if (ic->ic_fixed_mcs != -1) {
+               ridx = sc->sc_fixed_ridx;
+#endif
        } else if (ic->ic_fixed_rate != -1) {
                ridx = sc->sc_fixed_ridx;
        } else {
                /* for data frames, use RS table */
-               tx->initial_rate_index = (nrates - 1) - ni->ni_txrate;
+#ifndef IEEE80211_NO_HT
+               if (ni->ni_flags & IEEE80211_NODE_HT) {
+                       tx->initial_rate_index =
+                           (nitems(iwm_mcs2ridx) - 1) - ni->ni_txmcs;
+               } else
+#endif
+                       tx->initial_rate_index = (nrates - 1) - ni->ni_txrate;
                tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE);
                DPRINTFN(12, ("start with txrate %d\n", 
tx->initial_rate_index));
-               ridx = in->in_ridx[ni->ni_txrate];
+#ifndef IEEE80211_NO_HT
+               if (ni->ni_flags & IEEE80211_NODE_HT) {
+                       ridx = iwm_mcs2ridx[ni->ni_txmcs];
+                       return &iwm_rates[ridx];
+               }
+#endif
+               ridx = 0;
+               for (i = 0; i < nrates; i++) {
+                       if (iwm_rates[i].rate == (ni->ni_txrate &
+                           IEEE80211_RATE_VAL)) {
+                               ridx = i;
+                               break;
+                       }
+               }
                return &iwm_rates[ridx];
        }
 
@@ -3690,7 +3903,14 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, st
        rate_flags = 1 << IWM_RATE_MCS_ANT_POS;
        if (IWM_RIDX_IS_CCK(ridx))
                rate_flags |= IWM_RATE_MCS_CCK_MSK;
-       tx->rate_n_flags = htole32(rate_flags | rinfo->plcp);
+#ifndef IEEE80211_NO_HT
+       if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+           rinfo->ht_plcp != IWM_RATE_HT_SISO_MCS_INV_PLCP) {
+               rate_flags |= IWM_RATE_MCS_HT_MSK; 
+               tx->rate_n_flags = htole32(rate_flags | rinfo->ht_plcp);
+       } else
+#endif
+               tx->rate_n_flags = htole32(rate_flags | rinfo->plcp);
 
        return rinfo;
 }
@@ -3755,7 +3975,11 @@ iwm_tx(struct iwm_softc *sc, struct mbuf
                tap->wt_flags = 0;
                tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
                tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
-               tap->wt_rate = rinfo->rate;
+               if (rinfo->plcp == IWM_RATE_INVM_PLCP) {
+                       /* XXX need a way to pass current MCS in 11n mode */
+                       tap->wt_rate = 0;
+               } else
+                       tap->wt_rate = rinfo->rate;
                tap->wt_hwqueue = ac;
                if ((ic->ic_flags & IEEE80211_F_WEPON) &&
                    (wh->i_fc[1] & IEEE80211_FC1_PROTECTED))
@@ -4751,6 +4975,30 @@ iwm_mvm_mac_ctxt_cmd_common(struct iwm_s
                cmd->ac[txf].edca_txop = 0;
        }
 
+#ifndef IEEE80211_NO_HT
+       if (ni->ni_flags & IEEE80211_NODE_HT) {
+               enum ieee80211_htprot htprot =
+                   (ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK);
+               switch (htprot) {
+               case IEEE80211_HTPROT_NONE:
+                       break;
+               case IEEE80211_HTPROT_NONMEMBER:
+               case IEEE80211_HTPROT_NONHT_MIXED:
+                       cmd->protection_flags |=
+                           htole32(IWM_MAC_PROT_FLG_HT_PROT);
+               case IEEE80211_HTPROT_20MHZ:
+                       cmd->protection_flags |=
+                           htole32(IWM_MAC_PROT_FLG_HT_PROT |
+                           IWM_MAC_PROT_FLG_FAT_PROT);
+                       break;
+               default:
+                       DPRINTF(("Unknown protection mode %d\n", htprot));
+                       break;
+               }
+
+               cmd->qos_flags |= htole32(IWM_MAC_QOS_FLG_TGN);
+       }
+#endif
        if (ic->ic_flags & IEEE80211_F_USEPROT)
                cmd->protection_flags |= htole32(IWM_MAC_PROT_FLG_TGG_PROTECT);
 
@@ -5142,8 +5390,11 @@ iwm_calib_timeout(void *arg)
        int s;
 
        s = splnet();
-       if (ic->ic_fixed_rate == -1
-           && ic->ic_opmode == IEEE80211_M_STA
+       if ((ic->ic_fixed_rate == -1
+#ifndef IEEE80211_NO_HT
+           || ic->ic_fixed_mcs == -1
+#endif
+           ) && ic->ic_opmode == IEEE80211_M_STA
            && ic->ic_bss) {
                struct iwm_node *in = (void *)ic->ic_bss;
                ieee80211_amrr_choose(&sc->sc_amrr, &in->in_ni, &in->in_amn);
@@ -5160,77 +5411,92 @@ iwm_setrates(struct iwm_node *in)
        struct ieee80211com *ic = ni->ni_ic;
        struct iwm_softc *sc = IC2IFP(ic)->if_softc;
        struct iwm_lq_cmd *lq = &in->in_lq;
-       int nrates = ni->ni_rates.rs_nrates;
-       int i, ridx, tab = 0;
-       int txant = 0;
-
-       if (nrates > nitems(lq->rs_table)) {
-               DPRINTF(("%s: node supports %d rates, driver handles "
-                   "only %zu\n", DEVNAME(sc), nrates, nitems(lq->rs_table)));
-               return;
-       }
-
-       /* first figure out which rates we should support */
-       memset(&in->in_ridx, -1, sizeof(in->in_ridx));
-       for (i = 0; i < nrates; i++) {
-               int rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL;
-
-               /* Map 802.11 rate to HW rate index. */
-               for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++)
-                       if (iwm_rates[ridx].rate == rate)
+       struct ieee80211_rateset *rs = &ni->ni_rates;
+       int i, ridx, j, tab = 0;
+#ifndef IEEE80211_NO_HT
+       int maxmcs = 0;
+
+       /* Figure out which HT rates we should support. */
+       if (ni->ni_flags & IEEE80211_NODE_HT) {
+               for (i = 0; i < IEEE80211_HT_NUM_MCS; i++) {
+                       if (isclr(ic->ic_sup_mcs, i))
                                break;
-               if (ridx > IWM_RIDX_MAX)
-                       DPRINTF(("%s: WARNING: device rate for %d not found!\n",
-                           DEVNAME(sc), rate));
-               else
-                       in->in_ridx[i] = ridx;
-       }
-
-       /* then construct a lq_cmd based on those */
+                       if (isset(ni->ni_rxmcs, i))
+                               maxmcs = i;
+               }
+       }
+#endif
        memset(lq, 0, sizeof(*lq));
        lq->sta_id = IWM_STATION_ID;
 
        /*
-        * are these used? (we don't do SISO or MIMO)
-        * need to set them to non-zero, though, or we get an error.
+        * Fill the LQ rate selection table with legacy and/or HT rates
+        * in descending order, i.e. with the highest rate first.
+        * In cases where throughput of an HT rate corresponds to a legacy
+        * rate it makes no sense to add both. We rely on the fact that
+        * iwm_rates is laid out such that equivalent HT/legacy rates share
+        * the same IWM_RATE_*_INDEX value. Also, rates not applicable to
+        * legacy/HT are assumed to be marked with an 'invalid' PLCP value.
         */
-       lq->single_stream_ant_msk = 1;
-       lq->dual_stream_ant_msk = 1;
+       j = 0;
+       for (ridx = IWM_RIDX_MAX; ridx >= 0; ridx--) {
+               if (j >= nitems(lq->rs_table))
+                       break;
+               tab = 0;
+#ifndef IEEE80211_NO_HT
+               if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+                   iwm_rates[ridx].ht_plcp != IWM_RATE_HT_SISO_MCS_INV_PLCP) {
+                       for (i = 0; i <= maxmcs; i++) {
+                               if (isclr(ni->ni_rxmcs, i))
+                                       continue;
+                               if (ridx == iwm_mcs2ridx[i]) {
+                                       tab = iwm_rates[ridx].ht_plcp;
+                                       tab |= IWM_RATE_MCS_HT_MSK;
+                                       break;
+                               }
+                       }
+               }
+#endif
+               if (tab == 0 && iwm_rates[ridx].plcp != IWM_RATE_INVM_PLCP) {
+                       for (i = 0; i < rs->rs_nrates; i++) {
+                               if (iwm_rates[ridx].rate == (rs->rs_rates[i] &
+                                   IEEE80211_RATE_VAL)) {
+                                       tab = iwm_rates[ridx].plcp;
+                                       break;
+                               }
+                       }
+               }
 
-       /*
-        * Build the actual rate selection table.
-        * The lowest bits are the rates.  Additionally,
-        * CCK needs bit 9 to be set.  The rest of the bits
-        * we add to the table select the tx antenna
-        * Note that we add the rates in the highest rate first
-        * (opposite of ni_rates).
-        */
-       for (i = 0; i < nrates; i++) {
-               int nextant;
+               if (tab == 0)
+                       continue;
 
-               if (txant == 0)
-                       txant = IWM_FW_VALID_TX_ANT(sc);
-               nextant = 1<<(ffs(txant)-1);
-               txant &= ~nextant;
-
-               ridx = in->in_ridx[(nrates-1)-i];
-               tab = iwm_rates[ridx].plcp;
-               tab |= nextant << IWM_RATE_MCS_ANT_POS;
+               tab |= 1 << IWM_RATE_MCS_ANT_POS;
                if (IWM_RIDX_IS_CCK(ridx))
                        tab |= IWM_RATE_MCS_CCK_MSK;
-               DPRINTFN(2, ("station rate %d %x\n", i, tab));
-               lq->rs_table[i] = htole32(tab);
-       }
-       /* then fill the rest with the lowest possible rate */
-       for (i = nrates; i < nitems(lq->rs_table); i++) {
-               KASSERT(tab != 0);
-               lq->rs_table[i] = htole32(tab);
+               DPRINTFN(2, ("station rate %d %x\n", j, tab));
+               lq->rs_table[j++] = htole32(tab);
        }
 
+       /* Fill the rest with the lowest possible rate */
+       i = j > 0 ? j - 1 : 0;
+       while (j < nitems(lq->rs_table))
+               lq->rs_table[j++] = lq->rs_table[i];
+
+       lq->single_stream_ant_msk = IWM_ANT_A;
+       lq->dual_stream_ant_msk = IWM_ANT_AB;
+
        /* init amrr */
        ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
        /* Start at lowest available bit-rate, AMRR will raise. */
        ni->ni_txrate = 0;
+
+#ifndef IEEE80211_NO_HT
+       /* 
+        * XXX AMRR doesn't seem to raise properly in HT mode.
+        * For now, start with the highest rate and let AMRR drop down.
+        */
+       ni->ni_txmcs = 7;
+#endif
 }
 
 int
@@ -5245,6 +5511,11 @@ iwm_media_change(struct ifnet *ifp)
        if (error != ENETRESET)
                return error;
 
+#ifndef IEEE80211_NO_HT
+       if (ic->ic_fixed_mcs != -1)
+               sc->sc_fixed_ridx = iwm_mcs2ridx[ic->ic_fixed_mcs];
+       else
+#endif
        if (ic->ic_fixed_rate != -1) {
                rate = ic->ic_sup_rates[ic->ic_curmode].
                    rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
@@ -5654,6 +5925,9 @@ iwm_stop(struct ifnet *ifp, int disable)
        task_del(systq, &sc->init_task);
        task_del(sc->sc_nswq, &sc->newstate_task);
        task_del(sc->sc_eswq, &sc->sc_eswk);
+#ifndef IEEE80211_NO_HT
+       task_del(systq, &sc->ba_task);
+#endif
        sc->sc_newstate(ic, IEEE80211_S_INIT, -1);
 
        timeout_del(&sc->sc_calib_to);
@@ -6347,6 +6621,10 @@ iwm_preinit(struct iwm_softc *sc)
            IWM_UCODE_API(sc->sc_fwver),
            ether_sprintf(sc->sc_nvm.hw_addr));
 
+#ifndef IEEE80211_NO_HT
+       if (sc->sc_nvm.sku_cap_11n_enable)
+               iwm_setup_ht_rates(sc);
+#endif
        /* not all hardware can do 5GHz band */
        if (!sc->sc_nvm.sku_cap_band_52GHz_enable)
                memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0,
@@ -6366,6 +6644,14 @@ iwm_preinit(struct iwm_softc *sc)
        /* Override 802.11 state transition machine. */
        sc->sc_newstate = ic->ic_newstate;
        ic->ic_newstate = iwm_newstate;
+#ifndef IEEE80211_NO_HT
+       ic->ic_ampdu_rx_start = iwm_ampdu_rx_start;
+       ic->ic_ampdu_rx_stop = iwm_ampdu_rx_stop;
+#ifdef notyet
+       ic->ic_ampdu_tx_start = iwm_ampdu_tx_start;
+       ic->ic_ampdu_tx_stop = iwm_ampdu_tx_stop;
+#endif
+#endif
        ieee80211_media_init(ifp, iwm_media_change, ieee80211_media_status);
 
        return 0;
@@ -6551,6 +6837,14 @@ iwm_attach(struct device *parent, struct
            IEEE80211_C_SHSLOT |        /* short slot time supported */
            IEEE80211_C_SHPREAMBLE;     /* short preamble supported */
 
+#ifndef IEEE80211_NO_HT
+       /* No optional HT features supported for now, */
+       ic->ic_htcaps = 0;
+       ic->ic_htxcaps = 0;
+       ic->ic_txbfcaps = 0;
+       ic->ic_aselcaps = 0;
+#endif
+
        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;
@@ -6587,6 +6881,9 @@ iwm_attach(struct device *parent, struct
        timeout_set(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc);
        task_set(&sc->init_task, iwm_init_task, sc);
        task_set(&sc->newstate_task, iwm_newstate_task, sc);
+#ifndef IEEE80211_NO_HT
+       task_set(&sc->ba_task, iwm_ba_task, sc);
+#endif
 
        /*
         * We cannot read the MAC address without loading the
Index: dev/pci/if_iwmreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwmreg.h,v
retrieving revision 1.5
diff -u -p -r1.5 if_iwmreg.h
--- dev/pci/if_iwmreg.h 16 Oct 2015 10:04:56 -0000      1.5
+++ dev/pci/if_iwmreg.h 12 Nov 2015 15:33:07 -0000
@@ -2361,9 +2361,7 @@ struct iwm_rx_phy_info {
 #define IWM_PHY_INFO_FLAG_SHPREAMBLE   (1 << 2)
        uint16_t channel;
        uint32_t non_cfg_phy[IWM_RX_INFO_PHY_CNT];
-       uint8_t rate;
-       uint8_t rflags;
-       uint16_t xrflags;
+       uint32_t rate_n_flags;
        uint32_t byte_count;
        uint16_t mac_active_msk;
        uint16_t frame_time;
@@ -3424,10 +3422,56 @@ struct iwm_beacon_filter_cmd {
        .bf_escape_timer = htole32(IWM_BF_ESCAPE_TIMER_DEFAULT),             \
        .ba_escape_timer = htole32(IWM_BA_ESCAPE_TIMER_DEFAULT)
 
+/* uCode API values for HT/VHT bit rates */
+enum {
+       IWM_RATE_HT_SISO_MCS_0_PLCP = 0,
+       IWM_RATE_HT_SISO_MCS_1_PLCP = 1,
+       IWM_RATE_HT_SISO_MCS_2_PLCP = 2,
+       IWM_RATE_HT_SISO_MCS_3_PLCP = 3,
+       IWM_RATE_HT_SISO_MCS_4_PLCP = 4,
+       IWM_RATE_HT_SISO_MCS_5_PLCP = 5,
+       IWM_RATE_HT_SISO_MCS_6_PLCP = 6,
+       IWM_RATE_HT_SISO_MCS_7_PLCP = 7,
+       IWM_RATE_HT_MIMO2_MCS_0_PLCP = 0x8,
+       IWM_RATE_HT_MIMO2_MCS_1_PLCP = 0x9,
+       IWM_RATE_HT_MIMO2_MCS_2_PLCP = 0xA,
+       IWM_RATE_HT_MIMO2_MCS_3_PLCP = 0xB,
+       IWM_RATE_HT_MIMO2_MCS_4_PLCP = 0xC,
+       IWM_RATE_HT_MIMO2_MCS_5_PLCP = 0xD,
+       IWM_RATE_HT_MIMO2_MCS_6_PLCP = 0xE,
+       IWM_RATE_HT_MIMO2_MCS_7_PLCP = 0xF,
+       IWM_RATE_VHT_SISO_MCS_0_PLCP = 0,
+       IWM_RATE_VHT_SISO_MCS_1_PLCP = 1,
+       IWM_RATE_VHT_SISO_MCS_2_PLCP = 2,
+       IWM_RATE_VHT_SISO_MCS_3_PLCP = 3,
+       IWM_RATE_VHT_SISO_MCS_4_PLCP = 4,
+       IWM_RATE_VHT_SISO_MCS_5_PLCP = 5,
+       IWM_RATE_VHT_SISO_MCS_6_PLCP = 6,
+       IWM_RATE_VHT_SISO_MCS_7_PLCP = 7,
+       IWM_RATE_VHT_SISO_MCS_8_PLCP = 8,
+       IWM_RATE_VHT_SISO_MCS_9_PLCP = 9,
+       IWM_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10,
+       IWM_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11,
+       IWM_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12,
+       IWM_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13,
+       IWM_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14,
+       IWM_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15,
+       IWM_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16,
+       IWM_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17,
+       IWM_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18,
+       IWM_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19,
+       IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_HT_MIMO2_MCS_INV_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_VHT_SISO_MCS_INV_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_VHT_MIMO2_MCS_INV_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_HT_SISO_MCS_8_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_HT_SISO_MCS_9_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_HT_MIMO2_MCS_8_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+       IWM_RATE_HT_MIMO2_MCS_9_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP,
+};
+
 /*
- * These serve as indexes into
- * struct iwm_rate_info fw_rate_idx_to_plcp[IWM_RATE_COUNT];
- * TODO: avoid overlap between legacy and HT rates
+ * These serve as indexes into struct iwm_rate iwm_rates[IWM_RIDX_MAX].
  */
 enum {
        IWM_RATE_1M_INDEX = 0,
@@ -3481,7 +3525,7 @@ enum {
        IWM_RATE_2M_PLCP  = 20,
        IWM_RATE_5M_PLCP  = 55,
        IWM_RATE_11M_PLCP = 110,
-       IWM_RATE_INVM_PLCP = -1,
+       IWM_RATE_INVM_PLCP = 0xff,
 };
 
 /*
@@ -3666,6 +3710,15 @@ enum {
 #define IWM_LQ_FLAG_DYNAMIC_BW_POS          6
 #define IWM_LQ_FLAG_DYNAMIC_BW_MSK          (1 << IWM_LQ_FLAG_DYNAMIC_BW_POS)
 
+/* Antenna flags. */
+#define IWM_ANT_A      (1 << 0)
+#define IWM_ANT_B      (1 << 1)
+#define IWM_ANT_C      (1 << 2)
+/* Shortcuts. */
+#define IWM_ANT_AB     (IWM_ANT_A | IWM_ANT_B)
+#define IWM_ANT_BC     (IWM_ANT_B | IWM_ANT_C)
+#define IWM_ANT_ABC    (IWM_ANT_A | IWM_ANT_B | IWM_ANT_C)
+
 /**
  * struct iwm_lq_cmd - link quality command
  * @sta_id: station to update
@@ -3674,8 +3727,8 @@ enum {
  * @mimo_delim: the first SISO index in rs_table, which separates MIMO
  *     and SISO rates
  * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD).
- *     Should be ANT_[ABC]
- * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC]
+ *     Should be IWM_ANT_[ABC]
+ * @dual_stream_ant_msk: best antennas for MIMO, combination of IWM_ANT_[ABC]
  * @initial_rate_index: first index from rs_table per AC category
  * @agg_time_limit: aggregation max time threshold in usec/100, meaning
  *     value of 100 is one usec. Range is 100 to 8000
Index: dev/pci/if_iwmvar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v
retrieving revision 1.12
diff -u -p -r1.12 if_iwmvar.h
--- dev/pci/if_iwmvar.h 22 Oct 2015 11:51:28 -0000      1.12
+++ dev/pci/if_iwmvar.h 12 Nov 2015 15:33:07 -0000
@@ -191,6 +191,7 @@ struct iwm_nvm_data {
        int sku_cap_11n_enable;
        int sku_cap_amt_enable;
        int sku_cap_ipan_enable;
+       int sku_cap_mimo_disable;
 
        uint8_t radio_cfg_type;
        uint8_t radio_cfg_step;
@@ -368,6 +369,14 @@ struct iwm_softc {
        enum ieee80211_state    ns_nstate;
        int                     ns_arg;
 
+#ifndef IEEE80211_NO_HT
+       /* Task for firmware BlockAck setup/teardown and its arguments. */
+       struct task             ba_task;
+       int                     ba_start;
+       int                     ba_tid;
+       uint16_t                ba_ssn;
+#endif
+
        bus_space_tag_t sc_st;
        bus_space_handle_t sc_sh;
        bus_size_t sc_sz;
@@ -435,6 +444,9 @@ struct iwm_softc {
        struct iwm_bf_data sc_bf;
 
        int sc_tx_timer;
+#ifndef IEEE80211_NO_HT
+       int sc_rx_ba_sessions;
+#endif
 
        struct iwm_scan_cmd *sc_scan_cmd;
        size_t sc_scan_cmd_len;
@@ -499,8 +511,6 @@ struct iwm_node {
 
        struct iwm_lq_cmd in_lq;
        struct ieee80211_amrr_node in_amn;
-
-       uint8_t in_ridx[IEEE80211_RATE_MAXSIZE];
 };
 #define IWM_STATION_ID 0
 
Index: dev/usb/if_rsu.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_rsu.c,v
retrieving revision 1.28
diff -u -p -r1.28 if_rsu.c
--- dev/usb/if_rsu.c    25 Oct 2015 12:11:56 -0000      1.28
+++ dev/usb/if_rsu.c    12 Nov 2015 17:18:10 -0000
@@ -1982,7 +1982,9 @@ rsu_fw_loadsection(struct rsu_softc *sc,
 int
 rsu_load_firmware(struct rsu_softc *sc)
 {
+#ifndef IEEE80211_NO_HT
        struct ieee80211com *ic = &sc->sc_ic;
+#endif
        struct r92s_fw_hdr *hdr;
        struct r92s_fw_priv *dmem;
        uint8_t *imem, *emem;
@@ -2113,7 +2115,9 @@ rsu_load_firmware(struct rsu_softc *sc)
        dmem->rf_config = 0x12; /* 1T2R */
        dmem->vcs_type = R92S_VCS_TYPE_AUTO;
        dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
+#ifndef IEEE80211_NO_HT
        dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0;
+#endif
        dmem->turbo_mode = 1;
        /* Load DMEM section. */
        error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
@@ -2254,6 +2258,7 @@ rsu_init(struct ifnet *ifp)
                goto fail;
        }
 
+#ifndef IEEE80211_NO_HT
        if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) {
                /* Enable 40MHz mode. */
                error = rsu_fw_iocmd(sc,
@@ -2266,7 +2271,7 @@ rsu_init(struct ifnet *ifp)
                        goto fail;
                }
        }
-
+#endif
        /* Set default channel. */
        ic->ic_bss->ni_chan = ic->ic_ibss_chan;
 
Index: net/if_media.h
===================================================================
RCS file: /cvs/src/sys/net/if_media.h,v
retrieving revision 1.34
diff -u -p -r1.34 if_media.h
--- net/if_media.h      11 Sep 2015 13:02:28 -0000      1.34
+++ net/if_media.h      12 Nov 2015 15:33:07 -0000
@@ -202,6 +202,83 @@ uint64_t   ifmedia_baudrate(uint64_t);
 #define IFM_IEEE80211_OFDM48   16      /* OFDM 48Mbps */
 #define IFM_IEEE80211_OFDM54   17      /* OFDM 54Mbps */
 #define IFM_IEEE80211_OFDM72   18      /* OFDM 72Mbps */
+#define IFM_IEEE80211_HT_MCS0  19      /* 11n MCS 0 */
+#define IFM_IEEE80211_HT_MCS1  20      /* 11n MCS 1 */
+#define IFM_IEEE80211_HT_MCS2  21      /* 11n MCS 2 */
+#define IFM_IEEE80211_HT_MCS3  22      /* 11n MCS 3 */
+#define IFM_IEEE80211_HT_MCS4  23      /* 11n MCS 4 */
+#define IFM_IEEE80211_HT_MCS5  24      /* 11n MCS 5 */
+#define IFM_IEEE80211_HT_MCS6  25      /* 11n MCS 6 */
+#define IFM_IEEE80211_HT_MCS7  26      /* 11n MCS 7 */
+#define IFM_IEEE80211_HT_MCS8  27      /* 11n MCS 8 */
+#define IFM_IEEE80211_HT_MCS9  28      /* 11n MCS 9 */
+#define IFM_IEEE80211_HT_MCS10 29      /* 11n MCS 10 */
+#define IFM_IEEE80211_HT_MCS11 30      /* 11n MCS 11 */
+#define IFM_IEEE80211_HT_MCS12 31      /* 11n MCS 12 */
+#define IFM_IEEE80211_HT_MCS13 32      /* 11n MCS 13 */
+#define IFM_IEEE80211_HT_MCS14 33      /* 11n MCS 14 */
+#define IFM_IEEE80211_HT_MCS15 34      /* 11n MCS 15 */
+#define IFM_IEEE80211_HT_MCS16 35      /* 11n MCS 16 */
+#define IFM_IEEE80211_HT_MCS17 36      /* 11n MCS 17 */
+#define IFM_IEEE80211_HT_MCS18 37      /* 11n MCS 18 */
+#define IFM_IEEE80211_HT_MCS19 38      /* 11n MCS 19 */
+#define IFM_IEEE80211_HT_MCS20 39      /* 11n MCS 20 */
+#define IFM_IEEE80211_HT_MCS21 40      /* 11n MCS 21 */
+#define IFM_IEEE80211_HT_MCS22 41      /* 11n MCS 22 */
+#define IFM_IEEE80211_HT_MCS23 42      /* 11n MCS 23 */
+#define IFM_IEEE80211_HT_MCS24 43      /* 11n MCS 24 */
+#define IFM_IEEE80211_HT_MCS25 44      /* 11n MCS 25 */
+#define IFM_IEEE80211_HT_MCS26 45      /* 11n MCS 26 */
+#define IFM_IEEE80211_HT_MCS27 46      /* 11n MCS 27 */
+#define IFM_IEEE80211_HT_MCS28 47      /* 11n MCS 28 */
+#define IFM_IEEE80211_HT_MCS29 48      /* 11n MCS 29 */
+#define IFM_IEEE80211_HT_MCS30 49      /* 11n MCS 30 */
+#define IFM_IEEE80211_HT_MCS31 50      /* 11n MCS 31 */
+#define IFM_IEEE80211_HT_MCS32 51      /* 11n MCS 32 */
+#define IFM_IEEE80211_HT_MCS33 52      /* 11n MCS 33 */
+#define IFM_IEEE80211_HT_MCS34 53      /* 11n MCS 34 */
+#define IFM_IEEE80211_HT_MCS35 54      /* 11n MCS 35 */
+#define IFM_IEEE80211_HT_MCS36 55      /* 11n MCS 36 */
+#define IFM_IEEE80211_HT_MCS37 56      /* 11n MCS 37 */
+#define IFM_IEEE80211_HT_MCS38 57      /* 11n MCS 38 */
+#define IFM_IEEE80211_HT_MCS39 58      /* 11n MCS 39 */
+#define IFM_IEEE80211_HT_MCS40 59      /* 11n MCS 40 */
+#define IFM_IEEE80211_HT_MCS41 60      /* 11n MCS 41 */
+#define IFM_IEEE80211_HT_MCS42 61      /* 11n MCS 42 */
+#define IFM_IEEE80211_HT_MCS43 62      /* 11n MCS 43 */
+#define IFM_IEEE80211_HT_MCS44 63      /* 11n MCS 44 */
+#define IFM_IEEE80211_HT_MCS45 64      /* 11n MCS 45 */
+#define IFM_IEEE80211_HT_MCS46 65      /* 11n MCS 46 */
+#define IFM_IEEE80211_HT_MCS47 66      /* 11n MCS 47 */
+#define IFM_IEEE80211_HT_MCS48 67      /* 11n MCS 48 */
+#define IFM_IEEE80211_HT_MCS49 68      /* 11n MCS 49 */
+#define IFM_IEEE80211_HT_MCS50 69      /* 11n MCS 50 */
+#define IFM_IEEE80211_HT_MCS51 70      /* 11n MCS 51 */
+#define IFM_IEEE80211_HT_MCS52 71      /* 11n MCS 52 */
+#define IFM_IEEE80211_HT_MCS53 72      /* 11n MCS 53 */
+#define IFM_IEEE80211_HT_MCS54 73      /* 11n MCS 54 */
+#define IFM_IEEE80211_HT_MCS55 74      /* 11n MCS 55 */
+#define IFM_IEEE80211_HT_MCS56 75      /* 11n MCS 56 */
+#define IFM_IEEE80211_HT_MCS57 76      /* 11n MCS 57 */
+#define IFM_IEEE80211_HT_MCS58 77      /* 11n MCS 58 */
+#define IFM_IEEE80211_HT_MCS59 78      /* 11n MCS 59 */
+#define IFM_IEEE80211_HT_MCS60 79      /* 11n MCS 60 */
+#define IFM_IEEE80211_HT_MCS61 80      /* 11n MCS 61 */
+#define IFM_IEEE80211_HT_MCS62 81      /* 11n MCS 62 */
+#define IFM_IEEE80211_HT_MCS63 82      /* 11n MCS 63 */
+#define IFM_IEEE80211_HT_MCS64 83      /* 11n MCS 64 */
+#define IFM_IEEE80211_HT_MCS65 84      /* 11n MCS 65 */
+#define IFM_IEEE80211_HT_MCS66 85      /* 11n MCS 66 */
+#define IFM_IEEE80211_HT_MCS67 86      /* 11n MCS 67 */
+#define IFM_IEEE80211_HT_MCS68 87      /* 11n MCS 68 */
+#define IFM_IEEE80211_HT_MCS69 88      /* 11n MCS 69 */
+#define IFM_IEEE80211_HT_MCS70 89      /* 11n MCS 70 */
+#define IFM_IEEE80211_HT_MCS71 90      /* 11n MCS 71 */
+#define IFM_IEEE80211_HT_MCS72 91      /* 11n MCS 72 */
+#define IFM_IEEE80211_HT_MCS73 92      /* 11n MCS 73 */
+#define IFM_IEEE80211_HT_MCS74 93      /* 11n MCS 74 */
+#define IFM_IEEE80211_HT_MCS75 94      /* 11n MCS 75 */
+#define IFM_IEEE80211_HT_MCS76 95      /* 11n MCS 76 */
 
 #define        IFM_IEEE80211_ADHOC     0x0000000000010000ULL   /* Operate in 
Adhoc mode */
 #define        IFM_IEEE80211_HOSTAP    0x0000000000020000ULL   /* Operate in 
Host AP mode */
@@ -215,6 +292,7 @@ uint64_t    ifmedia_baudrate(uint64_t);
 #define IFM_IEEE80211_11B      0x0000000200000000ULL   /* Direct Sequence mode 
*/
 #define IFM_IEEE80211_11G      0x0000000300000000ULL   /* 2GHz, CCK mode */
 #define IFM_IEEE80211_FH       0x0000000400000000ULL   /* 2GHz, GFSK mode */
+#define IFM_IEEE80211_11N      0x0000000800000000ULL   /* 11n/HT 2GHz/5GHz */
 
 /*
  * Digitally multiplexed "Carrier" Serial Interfaces
@@ -444,6 +522,83 @@ struct ifmedia_description {
        { IFM_IEEE80211|IFM_IEEE80211_OFDM48,   "OFDM48" },             \
        { IFM_IEEE80211|IFM_IEEE80211_OFDM54,   "OFDM54" },             \
        { IFM_IEEE80211|IFM_IEEE80211_OFDM72,   "OFDM72" },             \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS0,  "HT-MCS0" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS1,  "HT-MCS1" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS2,  "HT-MCS2" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS3,  "HT-MCS3" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS4,  "HT-MCS4" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS5,  "HT-MCS5" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS6,  "HT-MCS6" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS7,  "HT-MCS7" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS8,  "HT-MCS8" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS9,  "HT-MCS9" },            \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS10, "HT-MCS10" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS11, "HT-MCS11" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS12, "HT-MCS12" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS13, "HT-MCS13" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS14, "HT-MCS14" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS15, "HT-MCS15" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS16, "HT-MCS16" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS17, "HT-MCS17" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS18, "HT-MCS18" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS19, "HT-MCS19" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS20, "HT-MCS20" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS21, "HT-MCS21" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS22, "HT-MCS22" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS23, "HT-MCS23" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS24, "HT-MCS24" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS25, "HT-MCS25" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS26, "HT-MCS26" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS27, "HT-MCS27" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS28, "HT-MCS28" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS29, "HT-MCS29" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS30, "HT-MCS30" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS31, "HT-MCS31" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS32, "HT-MCS32" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS33, "HT-MCS33" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS34, "HT-MCS34" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS35, "HT-MCS35" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS36, "HT-MCS36" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS37, "HT-MCS37" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS38, "HT-MCS38" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS39, "HT-MCS39" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS40, "HT-MCS40" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS41, "HT-MCS41" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS42, "HT-MCS42" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS43, "HT-MCS43" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS44, "HT-MCS44" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS45, "HT-MCS45" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS46, "HT-MCS46" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS47, "HT-MCS47" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS48, "HT-MCS48" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS49, "HT-MCS49" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS50, "HT-MCS50" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS51, "HT-MCS51" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS52, "HT-MCS52" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS53, "HT-MCS53" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS54, "HT-MCS54" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS55, "HT-MCS55" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS56, "HT-MCS56" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS57, "HT-MCS57" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS58, "HT-MCS58" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS59, "HT-MCS59" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS60, "HT-MCS60" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS61, "HT-MCS61" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS62, "HT-MCS62" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS63, "HT-MCS63" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS64, "HT-MCS64" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS65, "HT-MCS65" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS66, "HT-MCS66" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS67, "HT-MCS67" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS68, "HT-MCS68" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS69, "HT-MCS69" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS70, "HT-MCS70" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS71, "HT-MCS71" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS72, "HT-MCS72" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS73, "HT-MCS73" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS74, "HT-MCS74" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS75, "HT-MCS75" },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS76, "HT-MCS76" },           \
                                                                        \
        { IFM_TDM|IFM_TDM_T1,           "t1" },                         \
        { IFM_TDM|IFM_TDM_T1_AMI,       "t1-ami" },                     \
@@ -468,6 +623,7 @@ struct ifmedia_description {
        { IFM_IEEE80211|IFM_IEEE80211_11B,      "11b" },                \
        { IFM_IEEE80211|IFM_IEEE80211_11G,      "11g" },                \
        { IFM_IEEE80211|IFM_IEEE80211_FH,       "fh" },                 \
+       { IFM_IEEE80211|IFM_IEEE80211_11N,      "11n" },                \
        { IFM_TDM|IFM_TDM_MASTER,               "master" },             \
        { 0, NULL },                                                    \
 }
@@ -558,6 +714,84 @@ struct ifmedia_baudrate {
        { IFM_IEEE80211|IFM_IEEE80211_OFDM48, IF_Mbps(48) },            \
        { IFM_IEEE80211|IFM_IEEE80211_OFDM54, IF_Mbps(54) },            \
        { IFM_IEEE80211|IFM_IEEE80211_OFDM72, IF_Mbps(72) },            \
+       /* These HT rates correspond to 20 MHz channel with no SGI. */  \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS0, IF_Kbps(6500) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS1, IF_Mbps(13) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS2, IF_Kbps(19500) },        \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS3, IF_Mbps(26) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS4, IF_Mbps(39) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS5, IF_Mbps(52) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS6, IF_Kbps(58500) },        \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS7, IF_Mbps(65) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS8, IF_Mbps(13) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS9, IF_Mbps(26) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS10, IF_Mbps(39) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS11, IF_Mbps(52) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS12, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS13, IF_Mbps(104) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS14, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS15, IF_Mbps(130) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS16, IF_Kbps(19500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS17, IF_Mbps(39) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS18, IF_Kbps(58500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS19, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS20, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS21, IF_Mbps(156) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS22, IF_Kbps(175500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS23, IF_Mbps(195) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS24, IF_Mbps(26) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS25, IF_Mbps(52) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS26, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS27, IF_Mbps(104) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS28, IF_Mbps(156) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS29, IF_Mbps(208) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS30, IF_Mbps(234) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS31, IF_Mbps(260) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS32, IF_Mbps(0) },           \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS33, IF_Mbps(39) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS34, IF_Mbps(52) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS35, IF_Mbps(65) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS36, IF_Kbps(58500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS37, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS38, IF_Kbps(97500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS39, IF_Mbps(52) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS40, IF_Mbps(65) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS41, IF_Mbps(65) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS42, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS43, IF_Mbps(91) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS44, IF_Mbps(91) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS45, IF_Mbps(104) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS46, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS47, IF_Kbps(97500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS48, IF_Kbps(97500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS49, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS50, IF_Kbps(136500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS51, IF_Kbps(136500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS52, IF_Mbps(156) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS53, IF_Mbps(65) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS54, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS55, IF_Mbps(91) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS56, IF_Mbps(78) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS57, IF_Mbps(91) },          \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS58, IF_Mbps(104) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS59, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS60, IF_Mbps(104) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS61, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS62, IF_Mbps(130) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS63, IF_Mbps(130) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS64, IF_Mbps(143) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS65, IF_Kbps(97500) },       \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS66, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS67, IF_Kbps(136500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS68, IF_Mbps(117) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS69, IF_Kbps(136500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS70, IF_Mbps(156) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS71, IF_Kbps(175500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS72, IF_Mbps(156) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS73, IF_Kbps(175500) },      \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS74, IF_Mbps(195) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS75, IF_Mbps(195) },         \
+       { IFM_IEEE80211|IFM_IEEE80211_HT_MCS76, IF_Kbps(214500) },      \
                                                                        \
        { IFM_TDM|IFM_TDM_T1,           IF_Kbps(1536) },                \
        { IFM_TDM|IFM_TDM_T1_AMI,       IF_Kbps(1536) },                \
Index: net80211/ieee80211.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211.c,v
retrieving revision 1.47
diff -u -p -r1.47 ieee80211.c
--- net80211/ieee80211.c        17 Oct 2015 21:30:29 -0000      1.47
+++ net80211/ieee80211.c        12 Nov 2015 15:33:07 -0000
@@ -111,6 +111,10 @@ ieee80211_channel_init(struct ifnet *ifp
                                ic->ic_modecaps |= 1<<IEEE80211_MODE_11G;
                        if (IEEE80211_IS_CHAN_T(c))
                                ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
+#ifndef IEEE80211_NO_HT
+                       if (IEEE80211_IS_CHAN_N(c))
+                               ic->ic_modecaps |= 1<<IEEE80211_MODE_11N;
+#endif
                }
        }
        /* validate ic->ic_curmode */
@@ -275,7 +279,7 @@ ieee80211_media_init(struct ifnet *ifp,
        ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
        maxrate = 0;
        memset(&allrates, 0, sizeof(allrates));
-       for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) {
+       for (mode = IEEE80211_MODE_AUTO; mode <= IEEE80211_MODE_TURBO; mode++) {
                static const uint64_t mopts[] = {
                        IFM_AUTO,
                        IFM_IEEE80211_11A,
@@ -351,6 +355,34 @@ ieee80211_media_init(struct ifnet *ifp,
                if (ic->ic_caps & IEEE80211_C_MONITOR)
                        ADD(ic, mword, IFM_IEEE80211_MONITOR);
        }
+
+#ifndef IEEE80211_NO_HT
+       if (ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) {
+               mopt = IFM_IEEE80211_11N;
+               ADD(ic, IFM_AUTO, mopt);
+#ifndef IEEE80211_STA_ONLY
+               if (ic->ic_caps & IEEE80211_C_IBSS)
+                       ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_IBSS);
+               if (ic->ic_caps & IEEE80211_C_HOSTAP)
+                       ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP);
+#endif
+               for (i = 0; i < IEEE80211_HT_NUM_MCS; i++) {
+                       if (!isset(ic->ic_sup_mcs, i))
+                               continue;
+                       ADD(ic, IFM_IEEE80211_HT_MCS0 + i, mopt);
+#ifndef IEEE80211_STA_ONLY
+                       if (ic->ic_caps & IEEE80211_C_IBSS)
+                               ADD(ic, IFM_IEEE80211_HT_MCS0 + i,
+                                    mopt | IFM_IEEE80211_IBSS);
+                       if (ic->ic_caps & IEEE80211_C_HOSTAP)
+                               ADD(ic, IFM_IEEE80211_HT_MCS0 + i,
+                                   mopt | IFM_IEEE80211_HOSTAP);
+#endif
+               }
+               ic->ic_flags |= IEEE80211_F_HTON; /* enable 11n by default */
+       }
+#endif /* IEEE80211_NO_HT */
+
        ieee80211_media_status(ifp, &imr);
        ifmedia_set(&ic->ic_media, imr.ifm_active);
 
@@ -400,6 +432,11 @@ ieee80211_media_change(struct ifnet *ifp
        case IFM_IEEE80211_11G:
                newphymode = IEEE80211_MODE_11G;
                break;
+#ifndef IEEE80211_NO_HT
+       case IFM_IEEE80211_11N:
+               newphymode = IEEE80211_MODE_11N;
+               break;
+#endif
        case IFM_AUTO:
                newphymode = IEEE80211_MODE_AUTO;
                break;
@@ -425,7 +462,21 @@ ieee80211_media_change(struct ifnet *ifp
         * Next, the fixed/variable rate.
         */
        i = -1;
-       if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
+       if (IFM_SUBTYPE(ime->ifm_media) >= IFM_IEEE80211_HT_MCS0 &&
+           IFM_SUBTYPE(ime->ifm_media) <= IFM_IEEE80211_HT_MCS76) {
+#ifndef IEEE80211_NO_HT
+               if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) == 0)
+                       return EINVAL;
+               if (newphymode != IEEE80211_MODE_AUTO ||
+                   newphymode != IEEE80211_MODE_11N)
+                       return EINVAL;
+               i = ieee80211_media2mcs(ime->ifm_media);
+               if (i == -1 || isclr(ic->ic_sup_mcs, i))
+                       return EINVAL;
+#else
+                       return EINVAL;
+#endif
+       } else if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
                /*
                 * Convert media subtype to rate.
                 */
@@ -484,11 +535,17 @@ ieee80211_media_change(struct ifnet *ifp
         */
        if (newopmode == IEEE80211_M_HOSTAP &&
            newphymode == IEEE80211_MODE_AUTO) {
-               for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++)
-                       if (ic->ic_modecaps & (1<<j)) {
-                               newphymode = j;
-                               break;
-                       }
+#ifndef IEEE80211_NO_HT
+               if (ic->ic_modecaps & (1 << IEEE80211_MODE_11N))
+                       newphymode = IEEE80211_MODE_11N;
+               else
+#endif
+               if (ic->ic_modecaps & (1 << IEEE80211_MODE_11A))
+                       newphymode = IEEE80211_MODE_11A;
+               else if (ic->ic_modecaps & (1 << IEEE80211_MODE_11G))
+                       newphymode = IEEE80211_MODE_11G;
+               else
+                       newphymode = IEEE80211_MODE_11B;
        }
 #endif
 
@@ -503,12 +560,33 @@ ieee80211_media_change(struct ifnet *ifp
        }
 
        /*
-        * Committed to changes, install the rate setting.
+        * Committed to changes, install the MCS/rate setting.
         */
-       if (ic->ic_fixed_rate != i) {
-               ic->ic_fixed_rate = i;                  /* set fixed tx rate */
-               error = ENETRESET;
+       ic->ic_flags &= ~IEEE80211_F_HTON;
+#ifndef IEEE80211_NO_HT
+       if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) &&
+           (newphymode == IEEE80211_MODE_AUTO ||
+           newphymode == IEEE80211_MODE_11N))
+               ic->ic_flags |= IEEE80211_F_HTON;
+#endif
+       if ((ic->ic_flags & IEEE80211_F_HTON) == 0) {
+#ifndef IEEE80211_NO_HT
+               ic->ic_fixed_mcs = -1;
+#endif
+               if (ic->ic_fixed_rate != i) {
+                       ic->ic_fixed_rate = i;          /* set fixed tx rate */
+                       error = ENETRESET;
+               }
        }
+#ifndef IEEE80211_NO_HT
+       else {
+               ic->ic_fixed_rate = -1;
+               if (ic->ic_fixed_mcs != i) {
+                       ic->ic_fixed_mcs = i;           /* set fixed mcs */
+                       error = ENETRESET;
+               }
+       }
+#endif
 
        /*
         * Handle operating mode change.
@@ -560,9 +638,16 @@ ieee80211_media_status(struct ifnet *ifp
        switch (ic->ic_opmode) {
        case IEEE80211_M_STA:
                ni = ic->ic_bss;
-               /* calculate rate subtype */
-               imr->ifm_active |= ieee80211_rate2media(ic,
-                       ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
+#ifndef IEEE80211_NO_HT
+               if (ic->ic_curmode == IEEE80211_MODE_11N)
+                       imr->ifm_active |= ieee80211_mcs2media(ic,
+                               ni->ni_txmcs, ic->ic_curmode);
+               else
+#endif
+                       /* calculate rate subtype */
+                       imr->ifm_active |= ieee80211_rate2media(ic,
+                               ni->ni_rates.rs_rates[ni->ni_txrate],
+                               ic->ic_curmode);
                break;
 #ifndef IEEE80211_STA_ONLY
        case IEEE80211_M_IBSS:
@@ -595,6 +680,11 @@ ieee80211_media_status(struct ifnet *ifp
                imr->ifm_active |= IFM_IEEE80211_11A
                                |  IFM_IEEE80211_TURBO;
                break;
+#ifndef IEEE80211_NO_HT
+       case IEEE80211_MODE_11N:
+               imr->ifm_active |= IFM_IEEE80211_11N;
+               break;
+#endif
        }
 }
 
@@ -672,6 +762,9 @@ ieee80211_setmode(struct ieee80211com *i
                IEEE80211_CHAN_B,       /* IEEE80211_MODE_11B */
                IEEE80211_CHAN_PUREG,   /* IEEE80211_MODE_11G */
                IEEE80211_CHAN_T,       /* IEEE80211_MODE_TURBO */
+#ifndef IEEE80211_NO_HT
+               IEEE80211_CHAN_HT,      /* IEEE80211_MODE_11N */
+#endif
        };
        const struct ieee80211_channel *c;
        u_int modeflags;
@@ -771,10 +864,12 @@ ieee80211_next_mode(struct ifnet *ifp)
         * Get the next supported mode
         */
        for (++ic->ic_curmode;
-           ic->ic_curmode <= IEEE80211_MODE_TURBO;
+           ic->ic_curmode <= IEEE80211_MODE_MAX;
            ic->ic_curmode++) {
                /* Wrap around and ignore turbo mode */
-               if (ic->ic_curmode >= IEEE80211_MODE_TURBO) {
+               if (ic->ic_curmode == IEEE80211_MODE_TURBO)
+                       continue;
+               if (ic->ic_curmode >= IEEE80211_MODE_MAX) {
                        ic->ic_curmode = IEEE80211_MODE_AUTO;
                        break;
                }
@@ -811,6 +906,11 @@ ieee80211_chan2mode(struct ieee80211com 
         * characteristics.  We assume that turbo-only channels
         * are not considered when the channel set is constructed.
         */
+#ifndef IEEE80211_NO_HT
+       if (IEEE80211_IS_CHAN_N(chan))
+               return IEEE80211_MODE_11N;
+       else
+#endif
        if (IEEE80211_IS_CHAN_T(chan))
                return IEEE80211_MODE_TURBO;
        else if (IEEE80211_IS_CHAN_5GHZ(chan))
@@ -821,6 +921,56 @@ ieee80211_chan2mode(struct ieee80211com 
                return IEEE80211_MODE_11B;
 }
 
+#ifndef IEEE80211_NO_HT
+/*
+ * Convert IEEE80211 MCS index to ifmedia subtype.
+ */
+uint64_t
+ieee80211_mcs2media(struct ieee80211com *ic, int mcs,
+    enum ieee80211_phymode mode)
+{
+       switch (mode) {
+       case IEEE80211_MODE_11A:
+       case IEEE80211_MODE_TURBO:
+       case IEEE80211_MODE_11B:
+       case IEEE80211_MODE_11G:
+               /* these modes use rates, not MCS */
+               panic("unexpected mode %d", mode);
+               break;
+       case IEEE80211_MODE_AUTO:
+       case IEEE80211_MODE_11N:
+               if (mcs >= 0 && mcs < IEEE80211_HT_NUM_MCS)
+                       return (IFM_IEEE80211_11N |
+                           (IFM_IEEE80211_HT_MCS0 + mcs));
+               break;
+       }
+
+       return IFM_AUTO;
+}
+
+/*
+ * Convert ifmedia subtype to IEEE80211 MCS index.
+ */
+int
+ieee80211_media2mcs(uint64_t mword)
+{
+       uint64_t subtype;
+
+       subtype = IFM_SUBTYPE(mword);
+
+       if (subtype == IFM_AUTO)
+               return -1;
+       else if (subtype == IFM_MANUAL || subtype == IFM_NONE)
+               return 0;
+
+       if (subtype >= IFM_IEEE80211_HT_MCS0 &&
+           subtype <= IFM_IEEE80211_HT_MCS76)
+               return (int)(subtype - IFM_IEEE80211_HT_MCS0);
+
+       return -1;
+}
+#endif /* IEEE80211_NO_HT */
+
 /*
  * convert IEEE80211 rate value to ifmedia subtype.
  * ieee80211 rate is in unit of 0.5Mbps.
@@ -877,6 +1027,10 @@ ieee80211_rate2media(struct ieee80211com
                /* FALLTHROUGH */
        case IEEE80211_MODE_11G:
                mask |= IFM_IEEE80211_11G;
+               break;
+       case IEEE80211_MODE_11N:
+               /* 11n uses MCS, not rates. */
+               panic("unexpected mode %d", mode);
                break;
        }
        for (i = 0; i < nitems(rates); i++)
Index: net80211/ieee80211.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211.h,v
retrieving revision 1.53
diff -u -p -r1.53 ieee80211.h
--- net80211/ieee80211.h        10 Oct 2015 07:51:47 -0000      1.53
+++ net80211/ieee80211.h        12 Nov 2015 15:33:07 -0000
@@ -504,6 +504,8 @@ enum {
 #define        IEEE80211_RATE_SIZE                     8       /* 802.11 
standard */
 #define        IEEE80211_RATE_MAXSIZE                  15      /* max rates 
we'll handle */
 
+#define        IEEE80211_HT_NUM_MCS                    77
+
 /*
  * BlockAck/BlockAckReq Control field (see 802.11-2012 8.3.1.9 Figure 8-25).
  */
@@ -576,11 +578,13 @@ enum {
  */
 #define IEEE80211_AMPDU_PARAM_LE       0x03
 #define IEEE80211_AMPDU_PARAM_SS       0x1c
+/* bits 5-7 reserved */
 
 /*
  * HT Supported MCS Set (see 802.11-2012 8.4.2.58.4).
  * This field is 16 bytes in size. Bitmasks given below
- * operate on 8 or 16 bit integer subsets of this field.
+ * operate on 8 or 16 bit integer subsets of this field
+ * for use with ieee80211com and ieee80211_node.
  */
 /* Bits 0-76: Supported Rx MCS bitmask */
 /* Bits 77-79: Reserved */
@@ -661,6 +665,7 @@ enum {
 /*
  * HT Operation element (see 802.11-2012 8.4.2.59).
  */
+/* Byte 0 contains primary channel number. */
 /* Byte 1. */
 #define IEEE80211_HTOP0_SCO_MASK       0x03
 #define IEEE80211_HTOP0_SCO_SHIFT      0
@@ -987,6 +992,16 @@ enum {
        IEEE80211_KDE_LIFETIME  = 7,
        IEEE80211_KDE_ERROR     = 8,
        IEEE80211_KDE_IGTK      = 9     /* 11w */
+};
+
+/*
+ * HT protection modes (see 802.11-2012 8.4.2.59)
+ */
+enum ieee80211_htprot {
+       IEEE80211_HTPROT_NONE = 0,
+       IEEE80211_HTPROT_NONMEMBER,
+       IEEE80211_HTPROT_20MHZ,
+       IEEE80211_HTPROT_NONHT_MIXED,
 };
 
 #endif /* _NET80211_IEEE80211_H_ */
Index: net80211/ieee80211_amrr.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_amrr.c,v
retrieving revision 1.8
diff -u -p -r1.8 ieee80211_amrr.c
--- net80211/ieee80211_amrr.c   23 Dec 2014 03:24:08 -0000      1.8
+++ net80211/ieee80211_amrr.c   12 Nov 2015 15:33:07 -0000
@@ -38,16 +38,57 @@
        ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
 #define is_enough(amn)         \
        ((amn)->amn_txcnt > 10)
-#define is_min_rate(ni)                \
-       ((ni)->ni_txrate == 0)
-#define is_max_rate(ni)                \
-       ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1)
-#define increase_rate(ni)      \
-       ((ni)->ni_txrate++)
-#define decrease_rate(ni)      \
-       ((ni)->ni_txrate--)
 #define reset_cnt(amn)         \
        do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0)
+
+/* 
+ * XXX In HT mode we only support MCS 0-7, for now.
+ * Beyond MCS 7, incrementing the MCS index does not imply a
+ * higher data rate, so this simple implementation will need
+ * to be enhanced.
+ */
+
+static inline int
+is_min_rate(struct ieee80211_node *ni)
+{
+#ifndef IEEE80211_NO_HT
+       if (ni->ni_flags & IEEE80211_NODE_HT)
+               return (ni->ni_txmcs == 0);
+#endif
+       return (ni->ni_txrate == 0);
+}
+
+static inline int
+is_max_rate(struct ieee80211_node *ni)
+{
+#ifndef IEEE80211_NO_HT
+       if (ni->ni_flags & IEEE80211_NODE_HT)
+               return (ni->ni_txmcs == 7); /* XXX up to MCS 7 only */
+#endif
+       return (ni->ni_txrate == ni->ni_rates.rs_nrates - 1);
+}
+
+static inline void
+increase_rate(struct ieee80211_node *ni)
+{
+#ifndef IEEE80211_NO_HT
+       if (ni->ni_flags & IEEE80211_NODE_HT)
+               ni->ni_txmcs++;
+       else
+#endif
+               ni->ni_txrate++;
+}
+
+static inline void
+decrease_rate(struct ieee80211_node *ni)
+{
+#ifndef IEEE80211_NO_HT
+       if (ni->ni_flags & IEEE80211_NODE_HT)
+               ni->ni_txmcs--;
+       else
+#endif
+               ni->ni_txrate--;
+}
 
 void
 ieee80211_amrr_node_init(const struct ieee80211_amrr *amrr,
Index: net80211/ieee80211_crypto_ccmp.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_crypto_ccmp.c,v
retrieving revision 1.16
diff -u -p -r1.16 ieee80211_crypto_ccmp.c
--- net80211/ieee80211_crypto_ccmp.c    15 Jul 2015 22:16:42 -0000      1.16
+++ net80211/ieee80211_crypto_ccmp.c    12 Nov 2015 15:33:07 -0000
@@ -88,17 +88,18 @@ ieee80211_ccmp_phase1(rijndael_ctx *ctx,
        /* construct AAD (additional authenticated data) */
        aad = &auth[2]; /* skip l(a), will be filled later */
        *aad = wh->i_fc[0];
-       /* 11w: conditionnally mask subtype field */
+       /* 11w: conditionally mask subtype field */
        if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
            IEEE80211_FC0_TYPE_DATA)
-               *aad &= ~IEEE80211_FC0_SUBTYPE_MASK;
+               *aad &= ~IEEE80211_FC0_SUBTYPE_MASK |
+                  IEEE80211_FC0_SUBTYPE_QOS;
        aad++;
        /* protected bit is already set in wh */
        *aad = wh->i_fc[1];
        *aad &= ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT |
            IEEE80211_FC1_MORE_DATA);
-       /* 11n: conditionnally mask order bit */
-       if (ieee80211_has_htc(wh))
+       /* 11n: conditionally mask order bit */
+       if (ieee80211_has_qos(wh))
                *aad &= ~IEEE80211_FC1_ORDER;
        aad++;
        IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN;
@@ -112,6 +113,10 @@ ieee80211_ccmp_phase1(rijndael_ctx *ctx,
                aad += IEEE80211_ADDR_LEN;
        }
        if (ieee80211_has_qos(wh)) {
+               /* 
+                * XXX 802.11-2012 11.4.3.3.3 g says the A-MSDU present bit
+                * should be set here if both STAs are SPP A-MSDU capable.
+                */
                *aad++ = tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID;
                *aad++ = 0;
        }
Index: net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.140
diff -u -p -r1.140 ieee80211_input.c
--- net80211/ieee80211_input.c  8 Nov 2015 18:51:47 -0000       1.140
+++ net80211/ieee80211_input.c  12 Nov 2015 15:33:07 -0000
@@ -280,6 +280,43 @@ ieee80211_input(struct ifnet *ifp, struc
                tid = 0;
        }
 
+#ifndef IEEE80211_NO_HT
+       if (type == IEEE80211_FC0_TYPE_DATA && hasqos &&
+           !(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE)) {
+               int ba_state = ni->ni_rx_ba[tid].ba_state;
+
+               /* 
+                * If Block Ack was explicitly requested, check
+                * if we have a BA agreement for this RA/TID.
+                */
+               if ((qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
+                   IEEE80211_QOS_ACK_POLICY_BA &&
+                   ba_state != IEEE80211_BA_AGREED) {
+                       DPRINTF(("no BA agreement for %s, TID %d\n",
+                           ether_sprintf(ni->ni_macaddr), tid));
+                       /* send a DELBA with reason code UNKNOWN-BA */
+                       IEEE80211_SEND_ACTION(ic, ni,
+                           IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA,
+                           IEEE80211_REASON_SETUP_REQUIRED << 16 | tid);
+                       goto err;
+               }
+
+               /* 
+                * Check if we have an explicit or implicit
+                * Block Ack Request for a valid BA agreement.
+                */
+               if (ba_state == IEEE80211_BA_AGREED &&
+                   ((qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
+                   IEEE80211_QOS_ACK_POLICY_BA ||
+                   (qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
+                   IEEE80211_QOS_ACK_POLICY_NORMAL)) {
+                       /* go through A-MPDU reordering */
+                       ieee80211_input_ba(ifp, m, ni, tid, rxi);
+                       return; /* don't free m! */
+               }
+       }
+#endif
+
        /* duplicate detection (see 9.2.9) */
        if (ieee80211_has_seq(wh) &&
            ic->ic_state != IEEE80211_S_SCAN) {
@@ -430,27 +467,6 @@ ieee80211_input(struct ifnet *ifp, struc
                        goto out;
                }
 
-#ifndef IEEE80211_NO_HT
-               if (!(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE) &&
-                   hasqos && (qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
-                   IEEE80211_QOS_ACK_POLICY_BA) {
-                       /* check if we have a BA agreement for this RA/TID */
-                       if (ni->ni_rx_ba[tid].ba_state !=
-                           IEEE80211_BA_AGREED) {
-                               DPRINTF(("no BA agreement for %s, TID %d\n",
-                                   ether_sprintf(ni->ni_macaddr), tid));
-                               /* send a DELBA with reason code UNKNOWN-BA */
-                               IEEE80211_SEND_ACTION(ic, ni,
-                                   IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA,
-                                   IEEE80211_REASON_SETUP_REQUIRED << 16 |
-                                   tid);
-                               goto err;
-                       }
-                       /* go through A-MPDU reordering */
-                       ieee80211_input_ba(ifp, m, ni, tid, rxi);
-                       return; /* don't free m! */
-               }
-#endif
                if ((ic->ic_flags & IEEE80211_F_WEPON) ||
                    ((ic->ic_flags & IEEE80211_F_RSNON) &&
                     (ni->ni_flags & IEEE80211_NODE_RXPROT))) {
@@ -1628,7 +1644,12 @@ ieee80211_recv_probe_resp(struct ieee802
        ni->ni_erp = erp;
        /* NB: must be after ni_chan is setup */
        ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
-
+#ifndef IEEE80211_NO_HT
+       if (htcaps)
+               ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
+       if (htop)
+               ieee80211_setup_htop(ni, htop + 2, htop[1]);
+#endif
 #ifndef IEEE80211_STA_ONLY
        if (ic->ic_opmode == IEEE80211_M_IBSS && is_new && isprobe) {
                /*
@@ -1734,6 +1755,10 @@ ieee80211_recv_probe_req(struct ieee8021
                    ether_sprintf((u_int8_t *)wh->i_addr2)));
                return;
        }
+#ifndef IEEE80211_NO_HT
+       if (htcaps)
+               ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
+#endif
        IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
 }
 #endif /* IEEE80211_STA_ONLY */
@@ -2073,6 +2098,10 @@ ieee80211_recv_assoc_req(struct ieee8021
        ni->ni_intval = bintval;
        ni->ni_capinfo = capinfo;
        ni->ni_chan = ic->ic_bss->ni_chan;
+#ifndef IEEE80211_NO_HT
+       if (htcaps)
+               ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
+#endif
  end:
        if (status != 0) {
                IEEE80211_SEND_MGMT(ic, ni, resp, status);
@@ -2199,6 +2228,32 @@ ieee80211_recv_assoc_resp(struct ieee802
                else    /* for Reassociation */
                        ni->ni_flags &= ~IEEE80211_NODE_QOS;
        }
+#ifndef IEEE80211_NO_HT
+       if (htcaps)
+               ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
+       if (htop)
+               ieee80211_setup_htop(ni, htop + 2, htop[1]);
+       ieee80211_ht_negotiate(ic, ni);
+
+       /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */
+       if (ic->ic_curmode != IEEE80211_MODE_AUTO &&
+           ic->ic_curmode != IEEE80211_MODE_11N &&
+           (ni->ni_flags & IEEE80211_NODE_HT))
+               ieee80211_setmode(ic, IEEE80211_MODE_11N);
+
+       /* Hop out of 11n mode after associating to a non-HT AP. */
+       if (ic->ic_curmode == IEEE80211_MODE_11N &&
+           (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+               if (IEEE80211_IS_CHAN_T(ni->ni_chan))
+                       ieee80211_setmode(ic, IEEE80211_MODE_TURBO);
+               else if (IEEE80211_IS_CHAN_A(ni->ni_chan))
+                       ieee80211_setmode(ic, IEEE80211_MODE_11A);
+               else if (IEEE80211_IS_CHAN_G(ni->ni_chan))
+                       ieee80211_setmode(ic, IEEE80211_MODE_11G);
+               else
+                       ieee80211_setmode(ic, IEEE80211_MODE_11B);
+       }
+#endif
        /*
         * Configure state now that we are associated.
         */
@@ -2410,6 +2465,7 @@ ieee80211_recv_addba_req(struct ieee8021
                ba->ba_timeout_val = IEEE80211_BA_MIN_TIMEOUT;
        else if (ba->ba_timeout_val > IEEE80211_BA_MAX_TIMEOUT)
                ba->ba_timeout_val = IEEE80211_BA_MAX_TIMEOUT;
+       ba->ba_ni = ni;
        timeout_set(&ba->ba_to, ieee80211_rx_ba_timeout, ba);
        ba->ba_winsize = bufsz;
        if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ)
@@ -2426,7 +2482,7 @@ ieee80211_recv_addba_req(struct ieee8021
        ba->ba_head = 0;
 
        /* notify drivers of this new Block Ack agreement */
-       if (ic->ic_ampdu_rx_start != NULL &&
+       if (ic->ic_ampdu_rx_start == NULL ||
            ic->ic_ampdu_rx_start(ic, ni, tid) != 0) {
                /* driver failed to setup, rollback */
                free(ba->ba_buf, M_DEVBUF, 0);
Index: net80211/ieee80211_ioctl.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_ioctl.h,v
retrieving revision 1.21
diff -u -p -r1.21 ieee80211_ioctl.h
--- net80211/ieee80211_ioctl.h  9 Jan 2015 20:34:21 -0000       1.21
+++ net80211/ieee80211_ioctl.h  12 Nov 2015 15:33:07 -0000
@@ -175,6 +175,7 @@ struct ieee80211_channel {
 #define IEEE80211_CHAN_PASSIVE 0x0200  /* Only passive scan allowed */
 #define IEEE80211_CHAN_DYN     0x0400  /* Dynamic CCK-OFDM channel */
 #define IEEE80211_CHAN_XR      0x1000  /* Extended range OFDM channel */
+#define IEEE80211_CHAN_HT      0x2000  /* 11n/HT channel */
 #endif /* !_KERNEL */
 
 struct ieee80211_chanreq_all {
Index: net80211/ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.89
diff -u -p -r1.89 ieee80211_node.c
--- net80211/ieee80211_node.c   4 Nov 2015 12:12:00 -0000       1.89
+++ net80211/ieee80211_node.c   12 Nov 2015 15:33:07 -0000
@@ -1255,6 +1255,61 @@ ieee80211_iterate_nodes(struct ieee80211
        splx(s);
 }
 
+
+#ifndef IEEE80211_NO_HT
+/*
+ * Install received HT caps information in the node's state block.
+ */
+void
+ieee80211_setup_htcaps(struct ieee80211_node *ni, const uint8_t *data,
+    uint8_t len)
+{
+       uint16_t rxrate;
+
+       if (len != 26)
+               return;
+
+       ni->ni_htcaps = (data[0] | (data[1] << 8));
+       ni->ni_ampdu_param = data[2];
+
+       memcpy(ni->ni_rxmcs, &data[3], sizeof(ni->ni_rxmcs));
+       /* clear reserved bits */
+       clrbit(ni->ni_rxmcs, 77);
+       clrbit(ni->ni_rxmcs, 78);
+       clrbit(ni->ni_rxmcs, 79);
+
+       /* Max MCS Rx rate in 1Mb/s units (0 means "not specified"). */
+       rxrate = ((data[13] | (data[14]) << 8) & IEEE80211_MCS_RX_RATE_HIGH);
+       if (rxrate < 1024)
+               ni->ni_max_rxrate = rxrate;
+
+       ni->ni_tx_mcs_set = data[15];
+       ni->ni_htxcaps = (data[19] | (data[20] << 8));
+       ni->ni_txbfcaps = (data[21] | (data[22] << 8) | (data[23] << 16) |
+               (data[24] << 24));
+       ni->ni_aselcaps = data[25];
+}
+
+/*
+ * Install received HT op information in the node's state block.
+ */
+void
+ieee80211_setup_htop(struct ieee80211_node *ni, const uint8_t *data,
+    uint8_t len)
+{
+       if (len != 22)
+               return;
+
+       ni->ni_primary_chan = data[0]; /* XXX corresponds to ni_chan */
+
+       ni->ni_htop0 = data[1];
+       ni->ni_htop1 = (data[2] | (data[3] << 8));
+       ni->ni_htop2 = (data[3] | (data[4] << 8));
+
+       memcpy(ni->ni_basic_mcs, &data[6], sizeof(ni->ni_basic_mcs));
+}
+#endif /* IEEE80211_NO_HT */
+
 /*
  * Install received rate set information in the node's state block.
  */
@@ -1470,6 +1525,7 @@ ieee80211_node_join(struct ieee80211com 
                ieee80211_node_join_rsn(ic, ni);
 
 #ifndef IEEE80211_NO_HT
+       ieee80211_ht_negotiate(ic, ni);
        if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_node_join_ht(ic, ni);
 #endif
Index: net80211/ieee80211_node.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.47
diff -u -p -r1.47 ieee80211_node.h
--- net80211/ieee80211_node.h   4 Nov 2015 12:12:00 -0000       1.47
+++ net80211/ieee80211_node.h   12 Nov 2015 15:33:07 -0000
@@ -112,8 +112,8 @@ struct ieee80211_tx_ba {
        struct ieee80211_node   *ba_ni; /* backpointer for callbacks */
        struct timeout          ba_to;
        int                     ba_timeout_val;
-#define IEEE80211_BA_MIN_TIMEOUT       (10 * 1000)             /* 10msec */
-#define IEEE80211_BA_MAX_TIMEOUT       (10 * 1000 * 1000)      /* 10sec */
+#define IEEE80211_BA_MIN_TIMEOUT       (10 * 1000 * 1000)      /* 10 sec */
+#define IEEE80211_BA_MAX_TIMEOUT       (10 * 1000 * 1000 * 60) /* 60 sec */
 
        int                     ba_state;
 #define IEEE80211_BA_INIT      0
@@ -220,10 +220,31 @@ struct ieee80211_node {
        struct timeout          ni_sa_query_to;
        int                     ni_sa_query_count;
 
+#ifndef IEEE80211_NO_HT
+       /* HT capabilities */
+       uint16_t                ni_htcaps;
+       uint8_t                 ni_ampdu_param;
+       uint8_t                 ni_rxmcs[howmany(80,NBBY)];
+       uint16_t                ni_max_rxrate;  /* in Mb/s, 0 <= rate <= 1023 */
+       uint8_t                 ni_tx_mcs_set;
+       uint16_t                ni_htxcaps;
+       uint32_t                ni_txbfcaps;
+       uint8_t                 ni_aselcaps;
+
+       /* HT operation */
+       uint8_t                 ni_primary_chan; /* XXX corresponds to ni_chan 
*/
+       uint8_t                 ni_htop0;
+       uint16_t                ni_htop1;
+       uint16_t                ni_htop2;
+       uint8_t                 ni_basic_mcs[howmany(128,NBBY)];
+
        /* Block Ack records */
        struct ieee80211_tx_ba  ni_tx_ba[IEEE80211_NUM_TID];
        struct ieee80211_rx_ba  ni_rx_ba[IEEE80211_NUM_TID];
 
+       int                     ni_txmcs;       /* current MCS used for TX */
+#endif
+
        /* others */
        u_int16_t               ni_associd;     /* assoc response */
        u_int16_t               ni_txseq;       /* seq to be transmitted */
@@ -327,6 +348,12 @@ extern     void ieee80211_iterate_nodes(stru
                ieee80211_iter_func *, void *);
 extern void ieee80211_clean_cached(struct ieee80211com *ic);
 extern void ieee80211_clean_nodes(struct ieee80211com *, int);
+#ifndef IEEE80211_NO_HT
+void ieee80211_setup_htcaps(struct ieee80211_node *, const uint8_t *,
+    uint8_t);
+void ieee80211_setup_htop(struct ieee80211_node *, const uint8_t *,
+    uint8_t);
+#endif 
 extern int ieee80211_setup_rates(struct ieee80211com *,
            struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int);
 extern  int ieee80211_iserp_sta(const struct ieee80211_node *);
Index: net80211/ieee80211_output.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.99
diff -u -p -r1.99 ieee80211_output.c
--- net80211/ieee80211_output.c 8 Nov 2015 18:48:07 -0000       1.99
+++ net80211/ieee80211_output.c 12 Nov 2015 15:33:07 -0000
@@ -1009,8 +1009,14 @@ ieee80211_add_htcaps(u_int8_t *frm, stru
        *frm++ = IEEE80211_ELEMID_HTCAPS;
        *frm++ = 26;
        LE_WRITE_2(frm, ic->ic_htcaps); frm += 2;
-       *frm++ = 0;
-       memcpy(frm, ic->ic_sup_mcs, 16); frm += 16;
+       *frm++ = 0; /* XXX A-MPDU params */
+       memcpy(frm, ic->ic_sup_mcs, 10); frm += 10;
+       LE_WRITE_2(frm, (ic->ic_max_rxrate & IEEE80211_MCS_RX_RATE_HIGH));
+       frm += 2;
+       *frm++ = ic->ic_tx_mcs_set;
+       *frm++ = 0; /* reserved */
+       *frm++ = 0; /* reserved */
+       *frm++ = 0; /* reserved */
        LE_WRITE_2(frm, ic->ic_htxcaps); frm += 2;
        LE_WRITE_4(frm, ic->ic_txbfcaps); frm += 4;
        *frm++ = ic->ic_aselcaps;
@@ -1082,11 +1088,17 @@ ieee80211_getmgmt(int flags, int type, u
 struct mbuf *
 ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
-       const struct ieee80211_rateset *rs =
-           &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
+       const struct ieee80211_rateset *rs;
        struct mbuf *m;
        u_int8_t *frm;
 
+#ifndef IEEE80211_NO_HT
+       if (ic->ic_curmode == IEEE80211_MODE_11N)
+               rs = &ic->ic_sup_rates[IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ?
+                   IEEE80211_MODE_11G : IEEE80211_MODE_11A];
+       else
+#endif
+               rs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
        m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA,
            2 + ic->ic_des_esslen +
            2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
@@ -1102,7 +1114,7 @@ ieee80211_get_probe_req(struct ieee80211
        if (rs->rs_nrates > IEEE80211_RATE_SIZE)
                frm = ieee80211_add_xrates(frm, rs);
 #ifndef IEEE80211_NO_HT
-       if (ni->ni_flags & IEEE80211_NODE_HT)
+       if (ic->ic_flags & IEEE80211_F_HTON)
                frm = ieee80211_add_htcaps(frm, ic);
 #endif
 
@@ -1305,7 +1317,7 @@ ieee80211_get_assoc_req(struct ieee80211
            (ni->ni_rsnprotos & IEEE80211_PROTO_WPA))
                frm = ieee80211_add_wpa(frm, ic, ni);
 #ifndef IEEE80211_NO_HT
-       if (ni->ni_flags & IEEE80211_NODE_HT)
+       if (ic->ic_flags & IEEE80211_F_HTON)
                frm = ieee80211_add_htcaps(frm, ic);
 #endif
 
@@ -1365,7 +1377,7 @@ ieee80211_get_assoc_resp(struct ieee8021
                frm = ieee80211_add_tie(frm, 3, 1000 /* XXX */);
        }
 #ifndef IEEE80211_NO_HT
-       if (ni->ni_flags & IEEE80211_NODE_HT) {
+       if (ic->ic_flags & IEEE80211_F_HTON) {
                frm = ieee80211_add_htcaps(frm, ic);
                frm = ieee80211_add_htop(frm, ic);
        }
Index: net80211/ieee80211_proto.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_proto.c,v
retrieving revision 1.53
diff -u -p -r1.53 ieee80211_proto.c
--- net80211/ieee80211_proto.c  4 Nov 2015 12:12:00 -0000       1.53
+++ net80211/ieee80211_proto.c  12 Nov 2015 15:33:07 -0000
@@ -75,6 +75,7 @@ const char * const ieee80211_phymode_nam
        "11b",          /* IEEE80211_MODE_11B */
        "11g",          /* IEEE80211_MODE_11G */
        "turbo",        /* IEEE80211_MODE_TURBO */
+       "11n",          /* IEEE80211_MODE_11N */
 };
 
 int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
@@ -96,6 +97,9 @@ ieee80211_proto_attach(struct ifnet *ifp
 #endif
        ic->ic_fragthreshold = 2346;            /* XXX not used yet */
        ic->ic_fixed_rate = -1;                 /* no fixed rate */
+#ifndef IEEE80211_NO_HT
+       ic->ic_fixed_mcs = -1;                  /* no fixed mcs */
+#endif
        ic->ic_protmode = IEEE80211_PROT_CTSONLY;
 
        /* protocol state change handler */
@@ -546,6 +550,48 @@ ieee80211_sa_query_request(struct ieee80
 
 #ifndef IEEE80211_NO_HT
 void
+ieee80211_ht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+       int i;
+
+       ni->ni_flags &= ~IEEE80211_NODE_HT; 
+
+       /* Check if we support HT. */
+       if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) == 0)
+               return;
+
+       /* Check if HT support has been explicitly disabled. */
+       if ((ic->ic_flags & IEEE80211_F_HTON) == 0)
+               return;
+
+       /* Check if the peer supports HT. MCS 0-7 are mandatory. */
+       if (ni->ni_rxmcs[0] != 0xff)
+               return;
+
+       if (ic->ic_opmode == IEEE80211_M_STA) {
+               /* We must support the AP's basic MCS set. */
+               for (i = 0; i < IEEE80211_HT_NUM_MCS; i++) {
+                       if (isset(ni->ni_basic_mcs, i) &&
+                           !isset(ic->ic_sup_mcs, i))
+                               return;
+               }
+       }
+
+       /* 
+        * Don't allow group cipher (includes WEP) or TKIP
+        * for pairwise encryption (see 802.11-2012 11.1.6).
+        */
+       if (ic->ic_flags & IEEE80211_F_WEPON)
+               return;
+       if ((ic->ic_flags & IEEE80211_F_RSNON) &&
+           (ni->ni_rsnciphers & IEEE80211_CIPHER_USEGROUP ||
+           ni->ni_rsnciphers & IEEE80211_CIPHER_TKIP))
+               return;
+
+       ni->ni_flags |= IEEE80211_NODE_HT; 
+}
+
+void
 ieee80211_tx_ba_timeout(void *arg)
 {
        struct ieee80211_tx_ba *ba = arg;
@@ -849,8 +895,15 @@ justcleanup:
                /* initialize bss for probe request */
                IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr);
                IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr);
-               ni->ni_rates = ic->ic_sup_rates[
-                       ieee80211_chan2mode(ic, ni->ni_chan)];
+#ifndef IEEE80211_NO_HT
+               if (ic->ic_curmode == IEEE80211_MODE_11N)
+                       ni->ni_rates = ic->ic_sup_rates[
+                       IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ?
+                               IEEE80211_MODE_11G : IEEE80211_MODE_11A];
+               else
+#endif
+                       ni->ni_rates = ic->ic_sup_rates[
+                               ieee80211_chan2mode(ic, ni->ni_chan)];
                ni->ni_associd = 0;
                ni->ni_rstamp = 0;
                switch (ostate) {
@@ -971,16 +1024,24 @@ justcleanup:
                                    ni->ni_esslen);
                                rate = ni->ni_rates.rs_rates[ni->ni_txrate] &
                                    IEEE80211_RATE_VAL;
-                               printf(" channel %d start %u%sMb",
-                                   ieee80211_chan2ieee(ic, ni->ni_chan),
-                                   rate / 2, (rate & 1) ? ".5" : "");
-                               printf(" %s preamble %s slot time%s\n",
+                               printf(" channel %d",
+                                   ieee80211_chan2ieee(ic, ni->ni_chan));
+#ifndef IEEE80211_NO_HT
+                               if (ni->ni_flags & IEEE80211_NODE_HT)
+                                       printf(" start MCS %u", ni->ni_txmcs);
+                               else
+#endif
+                                       printf(" start %u%sMb",
+                                           rate / 2, (rate & 1) ? ".5" : "");
+                               printf(" %s preamble %s slot time%s%s\n",
                                    (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
                                        "short" : "long",
                                    (ic->ic_flags & IEEE80211_F_SHSLOT) ?
                                        "short" : "long",
                                    (ic->ic_flags & IEEE80211_F_USEPROT) ?
-                                       " protection enabled" : "");
+                                       " protection enabled" : "",
+                                   (ni->ni_flags & IEEE80211_NODE_HT) ?
+                                       " HT enabled" : "");
                        }
                        if (!(ic->ic_flags & IEEE80211_F_RSNON)) {
                                /*
Index: net80211/ieee80211_proto.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_proto.h,v
retrieving revision 1.39
diff -u -p -r1.39 ieee80211_proto.h
--- net80211/ieee80211_proto.h  21 Nov 2009 18:09:31 -0000      1.39
+++ net80211/ieee80211_proto.h  12 Nov 2015 15:33:07 -0000
@@ -154,6 +154,8 @@ extern      void ieee80211_sa_query_timeout(v
 extern void ieee80211_sa_query_request(struct ieee80211com *,
            struct ieee80211_node *);
 #ifndef IEEE80211_NO_HT
+extern void ieee80211_ht_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 *,
Index: net80211/ieee80211_radiotap.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_radiotap.h,v
retrieving revision 1.11
diff -u -p -r1.11 ieee80211_radiotap.h
--- net80211/ieee80211_radiotap.h       17 Jul 2010 16:25:09 -0000      1.11
+++ net80211/ieee80211_radiotap.h       12 Nov 2015 15:33:07 -0000
@@ -196,6 +196,7 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_CHAN_DYN     0x0400  /* Dynamic CCK-OFDM channel */
 #define IEEE80211_CHAN_GFSK    0x0800  /* GFSK channel (FHSS PHY) */
 #define IEEE80211_CHAN_XR      0x1000  /* Extended range OFDM channel */
+#define IEEE80211_CHAN_HT      0x2000  /* 11n/HT channel */
 #endif /* !_KERNEL */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
Index: net80211/ieee80211_var.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.65
diff -u -p -r1.65 ieee80211_var.h
--- net80211/ieee80211_var.h    4 Nov 2015 12:12:00 -0000       1.65
+++ net80211/ieee80211_var.h    12 Nov 2015 15:33:07 -0000
@@ -72,9 +72,10 @@ enum ieee80211_phymode {
        IEEE80211_MODE_11A      = 1,    /* 5GHz, OFDM */
        IEEE80211_MODE_11B      = 2,    /* 2GHz, CCK */
        IEEE80211_MODE_11G      = 3,    /* 2GHz, OFDM */
-       IEEE80211_MODE_TURBO    = 4     /* 5GHz, OFDM, 2x clock */
+       IEEE80211_MODE_TURBO    = 4,    /* 5GHz, OFDM, 2x clock */
+       IEEE80211_MODE_11N      = 5,    /* 11n, 2GHz/5GHz */
 };
-#define        IEEE80211_MODE_MAX      (IEEE80211_MODE_TURBO+1)
+#define        IEEE80211_MODE_MAX      (IEEE80211_MODE_11N+1)
 
 enum ieee80211_opmode {
        IEEE80211_M_STA         = 1,    /* infrastructure station */
@@ -114,6 +115,7 @@ struct ieee80211_channel {
 #define IEEE80211_CHAN_PASSIVE 0x0200  /* Only passive scan allowed */
 #define IEEE80211_CHAN_DYN     0x0400  /* Dynamic CCK-OFDM channel */
 #define IEEE80211_CHAN_XR      0x1000  /* Extended range OFDM channel */
+#define IEEE80211_CHAN_HT      0x2000  /* 11n/HT channel */
 
 /*
  * Useful combinations of channel characteristics.
@@ -143,6 +145,8 @@ struct ieee80211_channel {
        (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
 #define        IEEE80211_IS_CHAN_TG(_c) \
        (((_c)->ic_flags & IEEE80211_CHAN_TG) == IEEE80211_CHAN_TG)
+#define        IEEE80211_IS_CHAN_N(_c) \
+       (((_c)->ic_flags & IEEE80211_CHAN_HT) == IEEE80211_CHAN_HT)
 
 #define        IEEE80211_IS_CHAN_2GHZ(_c) \
        (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
@@ -313,12 +317,17 @@ struct ieee80211com {
        u_int                   ic_dtim_period;
        u_int                   ic_dtim_count;
 
+#ifndef IEEE80211_NO_HT
        u_int32_t               ic_txbfcaps;
        u_int16_t               ic_htcaps;
+       u_int8_t                ic_sup_mcs[howmany(80, NBBY)];
+       u_int16_t               ic_max_rxrate;  /* in Mb/s, 0 <= rate <= 1023 */
+       u_int8_t                ic_tx_mcs_set;
        u_int16_t               ic_htxcaps;
        u_int8_t                ic_aselcaps;
-       u_int8_t                ic_sup_mcs[16];
        u_int8_t                ic_dialog_token;
+       int                     ic_fixed_mcs;
+#endif
 
        LIST_HEAD(, ieee80211_vap) ic_vaps;
 };
@@ -391,6 +400,11 @@ int        ieee80211_fix_rate(struct ieee80211c
 uint64_t       ieee80211_rate2media(struct ieee80211com *, int,
                    enum ieee80211_phymode);
 int    ieee80211_media2rate(uint64_t);
+#ifndef IEEE80211_NO_HT
+uint64_t       ieee80211_mcs2media(struct ieee80211com *, int,
+                   enum ieee80211_phymode);
+int    ieee80211_media2mcs(uint64_t);
+#endif
 u_int8_t ieee80211_rate2plcp(u_int8_t, enum ieee80211_phymode);
 u_int8_t ieee80211_plcp2rate(u_int8_t, enum ieee80211_phymode);
 u_int  ieee80211_mhz2ieee(u_int, u_int);

Reply via email to