Here's an updated diff, which applies to -current, for testing.
I'll spend some time tomorrow splitting this up into smaller
chunks for review with explanations of the changes.

No known problems exist with this diff.
Please test anywhere, even without iwm(4). Thanks.

Index: dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.70
diff -u -p -r1.70 if_iwm.c
--- dev/pci/if_iwm.c    11 Dec 2015 16:07:02 -0000      1.70
+++ dev/pci/if_iwm.c    11 Dec 2015 22:48:52 -0000
@@ -170,19 +170,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
@@ -190,6 +193,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);
@@ -278,6 +293,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);
@@ -2370,6 +2403,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 */
@@ -2394,6 +2434,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) */
@@ -2477,9 +2518,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;
Index: dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.70
diff -u -p -r1.70 if_iwm.c
--- dev/pci/if_iwm.c    11 Dec 2015 16:07:02 -0000      1.70
+++ dev/pci/if_iwm.c    11 Dec 2015 22:48:52 -0000
@@ -170,19 +170,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
@@ -190,6 +193,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);
@@ -278,6 +293,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);
@@ -2370,6 +2403,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 */
@@ -2394,6 +2434,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) */
@@ -2477,9 +2518,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
+}
+
+
+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,
@@ -2502,7 +2680,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",
@@ -2544,13 +2728,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)
 {
@@ -3075,23 +3252,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;
@@ -3660,7 +3849,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;
@@ -3668,16 +3857,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];
        }
 
@@ -3685,7 +3898,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;
 }
@@ -3750,7 +3970,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))
@@ -4746,6 +4970,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);
 
@@ -5118,8 +5366,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);
@@ -5136,77 +5387,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
@@ -5221,6 +5487,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;
@@ -5630,6 +5901,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);
@@ -6315,6 +6589,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,
@@ -6334,6 +6612,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;
@@ -6519,6 +6805,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;
@@ -6555,6 +6849,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.8
diff -u -p -r1.8 if_iwmreg.h
--- dev/pci/if_iwmreg.h 10 Dec 2015 17:34:14 -0000      1.8
+++ dev/pci/if_iwmreg.h 11 Dec 2015 22:48:52 -0000
@@ -2398,9 +2398,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;
@@ -3461,10 +3459,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,
@@ -3518,7 +3562,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,
 };
 
 /*
Index: dev/pci/if_iwmvar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v
retrieving revision 1.13
diff -u -p -r1.13 if_iwmvar.h
--- dev/pci/if_iwmvar.h 8 Dec 2015 17:10:02 -0000       1.13
+++ dev/pci/if_iwmvar.h 11 Dec 2015 22:48:52 -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;
@@ -497,8 +509,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: net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.142
diff -u -p -r1.142 ieee80211_input.c
--- net80211/ieee80211_input.c  15 Nov 2015 11:14:17 -0000      1.142
+++ net80211/ieee80211_input.c  11 Dec 2015 22:43:24 -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))) {
@@ -1046,6 +1062,14 @@ ieee80211_amsdu_decap(struct ieee80211co
                }
                len += ETHER_HDR_LEN;
 
+               if (len > m->m_pkthdr.len) {
+                       /* stop processing A-MSDU subframes */
+                       DPRINTF(("A-MSDU subframe too long (%d)\n", len));
+                       ic->ic_stats.is_rx_decap++;
+                       m_freem(m);
+                       break;
+               }
+
                /* "detach" our A-MSDU subframe from the others */
                n = m_split(m, len, M_NOWAIT);
                if (n == NULL) {
@@ -1056,8 +1080,12 @@ ieee80211_amsdu_decap(struct ieee80211co
                }
                ieee80211_deliver_data(ic, m, ni);
 
+               if (n->m_len == 0) {
+                       m_freem(n);
+                       break;
+               }
                m = n;
-               /* remove padding */
+               /* remove padding (last subframe has no padding) */
                pad = ((len + 3) & ~3) - len;
                m_adj(m, pad);
        }
@@ -1569,10 +1597,13 @@ ieee80211_recv_probe_resp(struct ieee802
         */
        if (ni->ni_flags & IEEE80211_NODE_QOS) {
                /* always prefer EDCA IE over Wi-Fi Alliance WMM IE */
-               if (edcaie != NULL)
-                       ieee80211_parse_edca_params(ic, edcaie);
-               else if (wmmie != NULL)
-                       ieee80211_parse_wmm_params(ic, wmmie);
+               if ((edcaie != NULL &&
+                    ieee80211_parse_edca_params(ic, edcaie) == 0) ||
+                   (wmmie != NULL &&
+                    ieee80211_parse_wmm_params(ic, wmmie) == 0))
+                       ni->ni_flags |= IEEE80211_NODE_QOS;
+               else
+                       ni->ni_flags &= ~IEEE80211_NODE_QOS;
        }
 
        if (ic->ic_state == IEEE80211_S_SCAN
@@ -2177,7 +2208,7 @@ ieee80211_recv_assoc_resp(struct ieee802
                                break;
                        }
                        if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
-                               if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 1)
+                               if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 0)
                                        wmmie = frm;
                        }
                        break;
@@ -2449,6 +2480,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)
@@ -2465,7 +2497,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_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.92
diff -u -p -r1.92 ieee80211_node.c
--- net80211/ieee80211_node.c   24 Nov 2015 13:45:06 -0000      1.92
+++ net80211/ieee80211_node.c   3 Dec 2015 12:24:16 -0000
@@ -68,6 +68,9 @@ u_int8_t ieee80211_node_getrssi(struct i
 void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *,
     const u_int8_t *);
 void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *);
+#ifndef IEEE80211_NO_HT
+void ieee80211_ba_del(struct ieee80211_node *);
+#endif
 struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
 void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *);
@@ -757,6 +760,9 @@ ieee80211_node_cleanup(struct ieee80211c
                free(ni->ni_rsnie, M_DEVBUF, 0);
                ni->ni_rsnie = NULL;
        }
+#ifndef IEEE80211_NO_HT
+       ieee80211_ba_del(ni);
+#endif
 }
 
 void
@@ -1065,6 +1071,32 @@ ieee80211_find_node_for_beacon(struct ie
        return (keep);
 }
 
+#ifndef IEEE80211_NO_HT
+void
+ieee80211_ba_del(struct ieee80211_node *ni)
+{
+       int tid;
+
+       for (tid = 0; tid < nitems(ni->ni_rx_ba); tid++) {
+               struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
+               if (ba->ba_state == IEEE80211_BA_AGREED) {
+                       if (timeout_pending(&ba->ba_to))
+                               timeout_del(&ba->ba_to);
+                       ba->ba_state = IEEE80211_BA_INIT;
+               }
+       }
+
+       for (tid = 0; tid < nitems(ni->ni_tx_ba); tid++) {
+               struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
+               if (ba->ba_state == IEEE80211_BA_AGREED) {
+                       if (timeout_pending(&ba->ba_to))
+                               timeout_del(&ba->ba_to);
+                       ba->ba_state = IEEE80211_BA_INIT;
+               }
+       }
+}
+#endif
+
 void
 ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
@@ -1078,6 +1110,9 @@ ieee80211_free_node(struct ieee80211com 
        timeout_del(&ni->ni_eapol_to);
        timeout_del(&ni->ni_sa_query_to);
        IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+#endif
+#ifndef IEEE80211_NO_HT
+       ieee80211_ba_del(ni);
 #endif
        RB_REMOVE(ieee80211_tree, &ic->ic_tree, ni);
        ic->ic_nnodes--;
Index: net80211/ieee80211_node.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.49
diff -u -p -r1.49 ieee80211_node.h
--- net80211/ieee80211_node.h   15 Nov 2015 12:34:07 -0000      1.49
+++ net80211/ieee80211_node.h   15 Nov 2015 13:06:28 -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       (60 * 1000 * 1000)      /* 60 sec */
 
        int                     ba_state;
 #define IEEE80211_BA_INIT      0
Index: net80211/ieee80211_output.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.101
diff -u -p -r1.101 ieee80211_output.c
--- net80211/ieee80211_output.c 24 Nov 2015 12:32:53 -0000      1.101
+++ net80211/ieee80211_output.c 10 Dec 2015 14:00:34 -0000
@@ -832,6 +832,25 @@ ieee80211_add_qos_capability(u_int8_t *f
 }
 
 /*
+ * Add a Wifi-Alliance WME (aka WMM) info element to a frame.
+ * WME is a requirement for Wifi-Alliance compliance and some
+ * 11n APs will not negotiate HT if this element is missing.
+ */
+u_int8_t *
+ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211com *ic)
+{
+       *frm++ = IEEE80211_ELEMID_VENDOR;
+       *frm++ = 7;
+       memcpy(frm, MICROSOFT_OUI, 3); frm += 3;
+       *frm++ = 2; /* OUI type */
+       *frm++ = 0; /* OUI subtype */
+       *frm++ = 1; /* version */
+       *frm++ = 0; /* info */
+
+       return frm;
+}
+
+/*
  * Add an RSN element to a frame (see 802.11-2012 8.4.2.27)
  */
 u_int8_t *
@@ -1097,7 +1116,7 @@ ieee80211_get_probe_req(struct ieee80211
            2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
            ((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
                2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
-           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0));
+           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 9 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1107,8 +1126,10 @@ 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);
+               frm = ieee80211_add_wme_info(frm, ic);
+       }
 #endif
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1278,7 +1299,7 @@ ieee80211_get_assoc_req(struct ieee80211
            (((ic->ic_flags & IEEE80211_F_RSNON) &&
              (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
                2 + IEEE80211_WPAIE_MAXLEN : 0) +
-           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0));
+           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 9 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1310,8 +1331,10 @@ 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);
+               frm = ieee80211_add_wme_info(frm, ic);
+       }
 #endif
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1370,7 +1393,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);
        }


+       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,
@@ -2502,7 +2680,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",
@@ -2544,13 +2728,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)
 {
@@ -3075,23 +3252,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;
@@ -3660,7 +3849,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;
@@ -3668,16 +3857,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];
        }
 
@@ -3685,7 +3898,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;
 }
@@ -3750,7 +3970,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))
@@ -4746,6 +4970,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);
 
@@ -5118,8 +5366,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);
@@ -5136,77 +5387,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
@@ -5221,6 +5487,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;
@@ -5630,6 +5901,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);
@@ -6315,6 +6589,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,
@@ -6334,6 +6612,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;
@@ -6519,6 +6805,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;
@@ -6555,6 +6849,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.8
diff -u -p -r1.8 if_iwmreg.h
--- dev/pci/if_iwmreg.h 10 Dec 2015 17:34:14 -0000      1.8
+++ dev/pci/if_iwmreg.h 11 Dec 2015 22:48:52 -0000
@@ -2398,9 +2398,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;
@@ -3461,10 +3459,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,
@@ -3518,7 +3562,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,
 };
 
 /*
Index: dev/pci/if_iwmvar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v
retrieving revision 1.13
diff -u -p -r1.13 if_iwmvar.h
--- dev/pci/if_iwmvar.h 8 Dec 2015 17:10:02 -0000       1.13
+++ dev/pci/if_iwmvar.h 11 Dec 2015 22:48:52 -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;
@@ -497,8 +509,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: net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.142
diff -u -p -r1.142 ieee80211_input.c
--- net80211/ieee80211_input.c  15 Nov 2015 11:14:17 -0000      1.142
+++ net80211/ieee80211_input.c  11 Dec 2015 22:43:24 -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))) {
@@ -1046,6 +1062,14 @@ ieee80211_amsdu_decap(struct ieee80211co
                }
                len += ETHER_HDR_LEN;
 
+               if (len > m->m_pkthdr.len) {
+                       /* stop processing A-MSDU subframes */
+                       DPRINTF(("A-MSDU subframe too long (%d)\n", len));
+                       ic->ic_stats.is_rx_decap++;
+                       m_freem(m);
+                       break;
+               }
+
                /* "detach" our A-MSDU subframe from the others */
                n = m_split(m, len, M_NOWAIT);
                if (n == NULL) {
@@ -1056,8 +1080,12 @@ ieee80211_amsdu_decap(struct ieee80211co
                }
                ieee80211_deliver_data(ic, m, ni);
 
+               if (n->m_len == 0) {
+                       m_freem(n);
+                       break;
+               }
                m = n;
-               /* remove padding */
+               /* remove padding (last subframe has no padding) */
                pad = ((len + 3) & ~3) - len;
                m_adj(m, pad);
        }
@@ -1569,10 +1597,13 @@ ieee80211_recv_probe_resp(struct ieee802
         */
        if (ni->ni_flags & IEEE80211_NODE_QOS) {
                /* always prefer EDCA IE over Wi-Fi Alliance WMM IE */
-               if (edcaie != NULL)
-                       ieee80211_parse_edca_params(ic, edcaie);
-               else if (wmmie != NULL)
-                       ieee80211_parse_wmm_params(ic, wmmie);
+               if ((edcaie != NULL &&
+                    ieee80211_parse_edca_params(ic, edcaie) == 0) ||
+                   (wmmie != NULL &&
+                    ieee80211_parse_wmm_params(ic, wmmie) == 0))
+                       ni->ni_flags |= IEEE80211_NODE_QOS;
+               else
+                       ni->ni_flags &= ~IEEE80211_NODE_QOS;
        }
 
        if (ic->ic_state == IEEE80211_S_SCAN
@@ -2177,7 +2208,7 @@ ieee80211_recv_assoc_resp(struct ieee802
                                break;
                        }
                        if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
-                               if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 1)
+                               if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 0)
                                        wmmie = frm;
                        }
                        break;
@@ -2449,6 +2480,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)
@@ -2465,7 +2497,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_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.92
diff -u -p -r1.92 ieee80211_node.c
--- net80211/ieee80211_node.c   24 Nov 2015 13:45:06 -0000      1.92
+++ net80211/ieee80211_node.c   3 Dec 2015 12:24:16 -0000
@@ -68,6 +68,9 @@ u_int8_t ieee80211_node_getrssi(struct i
 void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *,
     const u_int8_t *);
 void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *);
+#ifndef IEEE80211_NO_HT
+void ieee80211_ba_del(struct ieee80211_node *);
+#endif
 struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
 void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *);
 void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *);
@@ -757,6 +760,9 @@ ieee80211_node_cleanup(struct ieee80211c
                free(ni->ni_rsnie, M_DEVBUF, 0);
                ni->ni_rsnie = NULL;
        }
+#ifndef IEEE80211_NO_HT
+       ieee80211_ba_del(ni);
+#endif
 }
 
 void
@@ -1065,6 +1071,32 @@ ieee80211_find_node_for_beacon(struct ie
        return (keep);
 }
 
+#ifndef IEEE80211_NO_HT
+void
+ieee80211_ba_del(struct ieee80211_node *ni)
+{
+       int tid;
+
+       for (tid = 0; tid < nitems(ni->ni_rx_ba); tid++) {
+               struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
+               if (ba->ba_state == IEEE80211_BA_AGREED) {
+                       if (timeout_pending(&ba->ba_to))
+                               timeout_del(&ba->ba_to);
+                       ba->ba_state = IEEE80211_BA_INIT;
+               }
+       }
+
+       for (tid = 0; tid < nitems(ni->ni_tx_ba); tid++) {
+               struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
+               if (ba->ba_state == IEEE80211_BA_AGREED) {
+                       if (timeout_pending(&ba->ba_to))
+                               timeout_del(&ba->ba_to);
+                       ba->ba_state = IEEE80211_BA_INIT;
+               }
+       }
+}
+#endif
+
 void
 ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
@@ -1078,6 +1110,9 @@ ieee80211_free_node(struct ieee80211com 
        timeout_del(&ni->ni_eapol_to);
        timeout_del(&ni->ni_sa_query_to);
        IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+#endif
+#ifndef IEEE80211_NO_HT
+       ieee80211_ba_del(ni);
 #endif
        RB_REMOVE(ieee80211_tree, &ic->ic_tree, ni);
        ic->ic_nnodes--;
Index: net80211/ieee80211_node.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.49
diff -u -p -r1.49 ieee80211_node.h
--- net80211/ieee80211_node.h   15 Nov 2015 12:34:07 -0000      1.49
+++ net80211/ieee80211_node.h   15 Nov 2015 13:06:28 -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       (60 * 1000 * 1000)      /* 60 sec */
 
        int                     ba_state;
 #define IEEE80211_BA_INIT      0
Index: net80211/ieee80211_output.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.101
diff -u -p -r1.101 ieee80211_output.c
--- net80211/ieee80211_output.c 24 Nov 2015 12:32:53 -0000      1.101
+++ net80211/ieee80211_output.c 10 Dec 2015 14:00:34 -0000
@@ -832,6 +832,25 @@ ieee80211_add_qos_capability(u_int8_t *f
 }
 
 /*
+ * Add a Wifi-Alliance WME (aka WMM) info element to a frame.
+ * WME is a requirement for Wifi-Alliance compliance and some
+ * 11n APs will not negotiate HT if this element is missing.
+ */
+u_int8_t *
+ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211com *ic)
+{
+       *frm++ = IEEE80211_ELEMID_VENDOR;
+       *frm++ = 7;
+       memcpy(frm, MICROSOFT_OUI, 3); frm += 3;
+       *frm++ = 2; /* OUI type */
+       *frm++ = 0; /* OUI subtype */
+       *frm++ = 1; /* version */
+       *frm++ = 0; /* info */
+
+       return frm;
+}
+
+/*
  * Add an RSN element to a frame (see 802.11-2012 8.4.2.27)
  */
 u_int8_t *
@@ -1097,7 +1116,7 @@ ieee80211_get_probe_req(struct ieee80211
            2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
            ((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
                2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
-           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0));
+           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 9 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1107,8 +1126,10 @@ 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);
+               frm = ieee80211_add_wme_info(frm, ic);
+       }
 #endif
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1278,7 +1299,7 @@ ieee80211_get_assoc_req(struct ieee80211
            (((ic->ic_flags & IEEE80211_F_RSNON) &&
              (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
                2 + IEEE80211_WPAIE_MAXLEN : 0) +
-           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0));
+           ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 9 : 0));
        if (m == NULL)
                return NULL;
 
@@ -1310,8 +1331,10 @@ 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);
+               frm = ieee80211_add_wme_info(frm, ic);
+       }
 #endif
 
        m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1370,7 +1393,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);
        }

Reply via email to