All my outstanding 11n changes for net80211 have been committed.
What's left now is adding 11n support to device drivers.

This is my current 11n diff for the iwm(4) driver.
Supports HT MCS 0-7 (theoretical limit 65 Mbit/s) and reception
of A-MSDU and A-MPDU aggregated frames.

Changes from the version contained in previous "big 11n" diffs I sent are:

  - Use CCK rate for multicast frames on 2 Ghz (found thanks to jasper@).
    The old version sent them on OFDM rates which 11b STAs won't understand.

  - Fix rate selection with AMRR. The firmware rate tables are now updated
    when AMRR selects a new Tx rate. This allows us to start out at MCS 0
    and raise to MCS 7 from there. This fix also applies to legacy rates
    which were not kept in sync with AMRR either.

Index: 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
--- if_iwm.c    11 Dec 2015 16:07:02 -0000      1.70
+++ if_iwm.c    13 Dec 2015 13:12:09 -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 8-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. upon 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,39 @@ 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 = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ?
                    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 +3897,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 +3969,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 +4969,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);
 
@@ -5115,14 +5362,37 @@ iwm_calib_timeout(void *arg)
 {
        struct iwm_softc *sc = arg;
        struct ieee80211com *ic = &sc->sc_ic;
-       int s;
+       struct iwm_node *in = (void *)ic->ic_bss;
+       struct ieee80211_node *ni = &in->in_ni;
+       int s, otxrate;
 
        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;
+#ifndef IEEE80211_NO_HT
+               if (ni->ni_flags & IEEE80211_NODE_HT)
+                       otxrate = ni->ni_txmcs;
+               else
+#endif
+                       otxrate = ni->ni_txrate;
                ieee80211_amrr_choose(&sc->sc_amrr, &in->in_ni, &in->in_amn);
+
+               /* 
+                * If AMRR has chosen a new TX rate we must update
+                * the firwmare's LQ rate table from process context.
+                */
+#ifndef IEEE80211_NO_HT
+               if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+                   otxrate != ni->ni_txmcs)
+                       task_add(systq, &sc->setrates_task);
+               else
+#endif
+               if (otxrate != ni->ni_txrate)
+                       task_add(systq, &sc->setrates_task);
        }
        splx(s);
 
@@ -5130,83 +5400,94 @@ iwm_calib_timeout(void *arg)
 }
 
 void
+iwm_setrates_task(void *arg)
+{
+       struct iwm_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct iwm_node * in = (struct iwm_node *)ic->ic_bss;
+
+       /* Update rates table based on new tx rate determined by AMRR. */
+       iwm_setrates(in);
+}
+
+void
 iwm_setrates(struct iwm_node *in)
 {
        struct ieee80211_node *ni = &in->in_ni;
        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)
-                               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;
-       }
+       struct ieee80211_rateset *rs = &ni->ni_rates;
+       int i, ridx, j, tab = 0;
+       struct iwm_host_cmd cmd = {
+               .id = IWM_LQ_CMD,
+               .len = { sizeof(in->in_lq), },
+               .flags = IWM_CMD_SYNC,
+       };
 
-       /* then construct a lq_cmd based on those */
        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 node's current tx 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 = ni->ni_txmcs; i >= 0; 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 = ni->ni_txrate; i >= 0; 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);
        }
 
-       /* 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;
+       /* 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;
+
+       cmd.data[0] = &in->in_lq;
+       if (iwm_send_cmd(sc, &cmd) != 0) {
+               DPRINTF(("%s: IWM_LQ_CMD failed\n", DEVNAME(sc)));
+       }
 }
 
 int
@@ -5221,6 +5502,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;
@@ -5327,22 +5613,20 @@ iwm_newstate_task(void *psc)
                break;
 
        case IEEE80211_S_RUN: {
-               struct iwm_host_cmd cmd = {
-                       .id = IWM_LQ_CMD,
-                       .len = { sizeof(in->in_lq), },
-                       .flags = IWM_CMD_SYNC,
-               };
-
                in = (struct iwm_node *)ic->ic_bss;
                iwm_mvm_power_mac_update_mode(sc, in);
                iwm_mvm_enable_beacon_filter(sc, in);
                iwm_mvm_update_quotas(sc, in);
-               iwm_setrates(in);
 
-               cmd.data[0] = &in->in_lq;
-               if ((error = iwm_send_cmd(sc, &cmd)) != 0) {
-                       DPRINTF(("%s: IWM_LQ_CMD failed\n", DEVNAME(sc)));
-               }
+               /* init amrr */
+               ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
+
+               /* Start at lowest available bit-rate, AMRR will raise. */
+               in->in_ni.ni_txrate = 0;
+#ifndef IEEE80211_NO_HT
+               in->in_ni.ni_txmcs = 0;
+#endif
+               iwm_setrates(in);
 
                timeout_add_msec(&sc->sc_calib_to, 500);
                iwm_mvm_led_enable(sc);
@@ -5630,6 +5914,10 @@ 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);
+       task_del(systq, &sc->setrates_task);
+#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 +6603,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 +6626,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 +6819,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 +6863,10 @@ 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);
+       task_set(&sc->setrates_task, iwm_setrates_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: 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
--- if_iwmreg.h 10 Dec 2015 17:34:14 -0000      1.8
+++ 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: 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
--- if_iwmvar.h 8 Dec 2015 17:10:02 -0000       1.13
+++ if_iwmvar.h 13 Dec 2015 12:14:45 -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;
@@ -365,9 +366,18 @@ struct iwm_softc {
 
        struct task             init_task;
        struct task             newstate_task;
+       struct task             setrates_task;
        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 +445,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 +510,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
 

Reply via email to