On Thu, Nov 12, 2015 at 08:22:07PM +0100, Stefan Sperling wrote:
> This diff adds an initial implementation of 802.11n.

Some of this has now been committed.

The diff below shows the outstanding parts relative to -current.

 - receiving A-MPDU and A-MSDU frames
 - 11n support for the iwm(4) driver (this won't go in before
   the above part is proven stable)

The show stopper is crashes observed with APs that send large
A-MSDUs (perhaps a use after free). I could not reproduce
the issue myself so far due to lack of APs that send such frames.
If anyone else can reproduce such crashes, please talk to me.

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    15 Nov 2015 13:06:25 -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.6
diff -u -p -r1.6 if_iwmreg.h
--- dev/pci/if_iwmreg.h 15 Nov 2015 09:39:09 -0000      1.6
+++ dev/pci/if_iwmreg.h 15 Nov 2015 13:06:25 -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,
 };
 
 /*
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 15 Nov 2015 13:06:25 -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: 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  15 Nov 2015 13:06:28 -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,11 @@ ieee80211_amsdu_decap(struct ieee80211co
                }
                len += ETHER_HDR_LEN;
 
+               if (m->m_pkthdr.len == len) {
+                       ieee80211_deliver_data(ic, m, ni);
+                       break;
+               }
+
                /* "detach" our A-MSDU subframe from the others */
                n = m_split(m, len, M_NOWAIT);
                if (n == NULL) {
@@ -2449,6 +2470,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 +2487,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.91
diff -u -p -r1.91 ieee80211_node.c
--- net80211/ieee80211_node.c   15 Nov 2015 11:14:17 -0000      1.91
+++ net80211/ieee80211_node.c   15 Nov 2015 13:06:28 -0000
@@ -69,6 +69,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 *);
@@ -758,6 +761,9 @@ ieee80211_node_cleanup(struct ieee80211c
                free(ni->ni_rsnie, M_DEVBUF, 0);
                ni->ni_rsnie = NULL;
        }
+#ifndef IEEE80211_NOT_HT
+       ieee80211_ba_del(ni);
+#endif
 }
 
 void
@@ -1066,6 +1072,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)
 {
@@ -1079,6 +1111,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_NOT_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

Reply via email to