This diff adds 11n MCS 0-7 with A-MPDU and A-MSDU Rx to the iwn(4) driver.

It also tweaks replay detection for CCMP encrypted frames, which needed
tweaking for A-MPDU anyway (see comments in code). Even in non-11n modes
this driver was discarding some retransmitted frames for no good reason.

This driver supports a very wide range of hardware so please test this
with as many iwn(4) adapters as possible. If we don't get enough tests
this change may not be able to make the 5.9 release.

Works for me on:
iwn0 at pci3 dev 0 function 0 "Intel Centrino Advanced-N 6200" rev 0x35: msi, 
MIMO 2T2R, MoW

Please post here or let me know in private which hardware you're testing on.
Thanks.

Note that if you'd like to test monitor mode you'll need to apply
this to a -current source tree from today.

Index: if_iwn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v
retrieving revision 1.148
diff -u -p -r1.148 if_iwn.c
--- if_iwn.c    25 Nov 2015 03:09:59 -0000      1.148
+++ if_iwn.c    16 Dec 2015 14:00:22 -0000
@@ -148,7 +148,7 @@ int         iwn_newstate(struct ieee80211com *,
 void           iwn_iter_func(void *, struct ieee80211_node *);
 void           iwn_calib_timeout(void *);
 int            iwn_ccmp_decap(struct iwn_softc *, struct mbuf *,
-                   struct ieee80211_key *);
+                   struct ieee80211_node *);
 void           iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
                    struct iwn_rx_data *);
 void           iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
@@ -189,7 +189,7 @@ int         iwn5000_add_node(struct iwn_softc *
                    int);
 int            iwn_set_link_quality(struct iwn_softc *,
                    struct ieee80211_node *);
-int            iwn_add_broadcast_node(struct iwn_softc *, int);
+int            iwn_add_broadcast_node(struct iwn_softc *, int, int);
 void           iwn_updateedca(struct ieee80211com *);
 void           iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
 int            iwn_set_critical_temp(struct iwn_softc *);
@@ -458,6 +458,15 @@ iwn_attach(struct device *parent, struct
            IEEE80211_C_PMGT;           /* power saving 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
+
+#ifdef notyet
+#ifndef IEEE80211_NO_HT
        if (sc->sc_flags & IWN_FLAG_HAS_11N) {
                /* Set HT capabilities. */
                ic->ic_htcaps =
@@ -475,6 +484,7 @@ iwn_attach(struct device *parent, struct
                        ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS;
        }
 #endif /* !IEEE80211_NO_HT */
+#endif /* notyet */
 
        /* Set supported legacy rates. */
        ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
@@ -487,10 +497,12 @@ iwn_attach(struct device *parent, struct
        if (sc->sc_flags & IWN_FLAG_HAS_11N) {
                /* Set supported HT rates. */
                ic->ic_sup_mcs[0] = 0xff;               /* MCS 0-7 */
+#ifdef notyet
                if (sc->nrxchains > 1)
-                       ic->ic_sup_mcs[1] = 0xff;       /* MCS 7-15 */
+                       ic->ic_sup_mcs[1] = 0xff;       /* MCS 8-15 */
                if (sc->nrxchains > 2)
                        ic->ic_sup_mcs[2] = 0xff;       /* MCS 16-23 */
+#endif
        }
 #endif
 
@@ -515,9 +527,11 @@ iwn_attach(struct device *parent, struct
 #ifndef IEEE80211_NO_HT
        ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
        ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
+#ifdef notyet
        ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
        ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
 #endif
+#endif
 
        /* Override 802.11 state transition machine. */
        sc->sc_newstate = ic->ic_newstate;
@@ -1635,6 +1649,11 @@ iwn_read_eeprom_channels(struct iwn_soft
                /* Save maximum allowed TX power for this channel. */
                sc->maxpwr[chan] = channels[i].maxpwr;
 
+#ifndef IEEE80211_NO_HT
+               if (sc->sc_flags & IWN_FLAG_HAS_11N)
+                       ic->ic_channels[chan].ic_flags |= IEEE80211_CHAN_HT;
+#endif
+
                DPRINTF(("adding chan %d flags=0x%x maxpwr=%d\n",
                    chan, channels[i].flags, sc->maxpwr[chan]));
        }
@@ -1693,13 +1712,18 @@ iwn_newassoc(struct ieee80211com *ic, st
        ieee80211_amrr_node_init(&sc->amrr, &wn->amn);
        /* Start at lowest available bit-rate, AMRR will raise. */
        ni->ni_txrate = 0;
+#ifndef IEEE80211_NO_HT
+       ni->ni_txmcs = 0;
+#endif
 
        for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
                rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL;
                /* Map 802.11 rate to HW rate index. */
-               for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++)
-                       if (iwn_rates[ridx].rate == rate)
+               for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
+                       if (iwn_rates[ridx].plcp != IWN_PLCP_INVALID &&
+                           iwn_rates[ridx].rate == rate)
                                break;
+               }
                wn->ridx[i] = ridx;
        }
 }
@@ -1716,12 +1740,17 @@ iwn_media_change(struct ifnet *ifp)
        if (error != ENETRESET)
                return error;
 
+#ifndef IEEE80211_NO_HT
+       if (ic->ic_fixed_mcs != -1)
+               sc->fixed_ridx = iwn_mcs2ridx[ic->ic_fixed_mcs];
+#endif
        if (ic->ic_fixed_rate != -1) {
                rate = ic->ic_sup_rates[ic->ic_curmode].
                    rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
                /* Map 802.11 rate to HW rate index. */
                for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++)
-                       if (iwn_rates[ridx].rate == rate)
+                       if (iwn_rates[ridx].plcp != IWN_PLCP_INVALID &&
+                           iwn_rates[ridx].rate == rate)
                                break;
                sc->fixed_ridx = ridx;
        }
@@ -1828,13 +1857,14 @@ iwn_calib_timeout(void *arg)
 }
 
 int
-iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_key *k)
+iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 {
+       struct ieee80211_key *k = &ni->ni_pairwise_key;
        struct ieee80211_frame *wh;
        uint64_t pn, *prsc;
        uint8_t *ivp;
        uint8_t tid;
-       int hdrlen;
+       int hdrlen, hasqos;
 
        wh = mtod(m, struct ieee80211_frame *);
        hdrlen = ieee80211_get_hdrlen(wh);
@@ -1845,8 +1875,8 @@ iwn_ccmp_decap(struct iwn_softc *sc, str
                DPRINTF(("CCMP decap ExtIV not set\n"));
                return 1;
        }
-       tid = ieee80211_has_qos(wh) ?
-           ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+       hasqos = ieee80211_has_qos(wh);
+       tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
        prsc = &k->k_rsc[tid];
 
        /* Extract the 48-bit PN from the CCMP header. */
@@ -1857,12 +1887,41 @@ iwn_ccmp_decap(struct iwn_softc *sc, str
             (uint64_t)ivp[6] << 32 |
             (uint64_t)ivp[7] << 40;
        if (pn <= *prsc) {
-               /*
-                * Not necessarily a replayed frame since we did not check
-                * the sequence number of the 802.11 header yet.
-                */
-               DPRINTF(("CCMP replayed\n"));
-               return 1;
+               struct ieee80211_rx_ba *ba = NULL;
+#ifndef IEEE80211_NO_HT
+               ba = &ni->ni_rx_ba[tid];
+#endif
+               if (ba != NULL && ieee80211_has_qos(wh) &&
+                   ba->ba_state == IEEE80211_BA_AGREED) {
+                       /*
+                        * This is an A-MPDU subframe.
+                        * Such frames may be received out of order due to
+                        * legitimate retransmissions of failed subframes
+                        * in previous A-MPDUs. Duplicates will be handled
+                        * in ieee80211_input() as part of A-MPDU reordering.
+                        */
+               } else if (ieee80211_has_seq(wh)) {
+                       /*
+                        * Not necessarily a replayed frame since we did not
+                        * check the sequence number of the 802.11 header yet.
+                        */
+                       int nrxseq, orxseq;
+
+                       nrxseq = letoh16(*(u_int16_t *)wh->i_seq) >>
+                           IEEE80211_SEQ_SEQ_SHIFT;
+                       if (hasqos)
+                               orxseq = ni->ni_qos_rxseqs[tid];
+                       else
+                               orxseq = ni->ni_rxseq;
+                       if (nrxseq < orxseq) {
+                               DPRINTF(("CCMP replayed (n=%d < o=%d)\n",
+                                   nrxseq, orxseq));
+                               return 1;
+                       }
+               } else {
+                       DPRINTF(("CCMP replayed\n"));
+                       return 1;
+               }
        }
        /* Update last seen packet number. */
        *prsc = pn;
@@ -1922,8 +1981,14 @@ iwn_rx_done(struct iwn_softc *sc, struct
                        DPRINTF(("missing RX_PHY\n"));
                        return;
                }
-               sc->last_rx_valid = 0;
                stat = &sc->last_rx_stat;
+
+               /*
+                * The firmware does not send separate RX_PHY
+                * notifications for A-MPDU subframes.
+                */
+               if ((stat->flags & htole16(IWN_STAT_FLAG_AGG)) == 0)
+                       sc->last_rx_valid = 0;
        } else
                stat = (struct iwn_rx_stat *)(desc + 1);
 
@@ -2030,7 +2095,7 @@ iwn_rx_done(struct iwn_softc *sc, struct
                        m_freem(m);
                        return;
                }
-               if (iwn_ccmp_decap(sc, m, &ni->ni_pairwise_key) != 0) {
+               if (iwn_ccmp_decap(sc, m, ni) != 0) {
                        ifp->if_ierrors++;
                        m_freem(m);
                        return;
@@ -2281,9 +2346,10 @@ iwn_tx_done(struct iwn_softc *sc, struct
        if (ackfailcnt > 0)
                wn->amn.amn_retrycnt++;
 
-       if (status != 1 && status != 2)
+       if (status != 1 && status != 2) {
+               DPRINTF(("%s: status=0x%x\n", __func__, status));
                ifp->if_oerrors++;
-       else
+       } else
                ifp->if_opackets++;
 
        /* Unmap and free mbuf. */
@@ -2397,7 +2463,7 @@ iwn_notif_intr(struct iwn_softc *sc)
                         * If more than 5 consecutive beacons are missed,
                         * reinitialize the sensitivity state machine.
                         */
-                       DPRINTF(("beacons missed %d/%d\n",
+                       DPRINTFN(2, ("beacons missed %d/%d\n",
                            letoh32(miss->consecutive), letoh32(miss->total)));
                        if (ic->ic_state == IEEE80211_S_RUN &&
                            letoh32(miss->consecutive) > 5)
@@ -2778,15 +2844,24 @@ iwn_tx(struct iwn_softc *sc, struct mbuf
 
        /* Choose a TX rate index. */
        if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
-           type != IEEE80211_FC0_TYPE_DATA) {
-               ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+           type != IEEE80211_FC0_TYPE_DATA)
+               ridx = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ?
                    IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
-       } else if (ic->ic_fixed_rate != -1) {
+#ifndef IEEE80211_NO_HT
+       else if (ic->ic_fixed_mcs != -1)
                ridx = sc->fixed_ridx;
-       } else
-               ridx = wn->ridx[ni->ni_txrate];
+#endif
+       else if (ic->ic_fixed_rate != -1)
+               ridx = sc->fixed_ridx;
+       else {
+#ifndef IEEE80211_NO_HT
+               if (ni->ni_flags & IEEE80211_NODE_HT)
+                       ridx = iwn_mcs2ridx[ni->ni_txmcs];
+               else
+#endif
+                       ridx = wn->ridx[ni->ni_txrate];
+       }       
        rinfo = &iwn_rates[ridx];
-
 #if NBPFILTER > 0
        if (sc->sc_drvbpf != NULL) {
                struct mbuf mb;
@@ -2795,7 +2870,15 @@ iwn_tx(struct iwn_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;
+#ifndef IEEE80211_NO_HT
+               if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+                   !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+                   type == IEEE80211_FC0_TYPE_DATA) {
+                       /* XXX need a way to pass current MCS in 11n mode */
+                       tap->wt_rate = 0;
+               } else
+#endif
+                       tap->wt_rate = rinfo->rate;
                tap->wt_hwqueue = ac;
                if ((ic->ic_flags & IEEE80211_F_WEPON) &&
                    (wh->i_fc[1] & IEEE80211_FC1_PROTECTED))
@@ -2867,6 +2950,10 @@ iwn_tx(struct iwn_softc *sc, struct mbuf
                        else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
                                flags |= IWN_TX_NEED_RTS;
                }
+#ifndef IEEE80211_NO_HT
+               else if (ni->ni_flags & IEEE80211_NODE_HT)
+                       flags |= IWN_TX_NEED_RTS;
+#endif
                if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
                        if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
                                /* 5000 autoselects RTS/CTS or CTS-to-self. */
@@ -2911,8 +2998,22 @@ iwn_tx(struct iwn_softc *sc, struct mbuf
        tx->rts_ntries = 60;
        tx->data_ntries = 15;
        tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
-       tx->plcp = rinfo->plcp;
-       tx->rflags = rinfo->flags;
+
+#ifndef IEEE80211_NO_HT
+       if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+           tx->id != sc->broadcast_id)
+               tx->plcp = rinfo->ht_plcp;
+       else
+#endif
+               tx->plcp = rinfo->plcp;
+
+#ifndef IEEE80211_NO_HT
+       if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+           tx->id != sc->broadcast_id)
+               tx->rflags = rinfo->ht_flags;
+       else
+#endif
+               tx->rflags = rinfo->flags;
        if (tx->id == sc->broadcast_id) {
                /* Group or management frame. */
                tx->linkq = 0;
@@ -2920,7 +3021,12 @@ iwn_tx(struct iwn_softc *sc, struct mbuf
                txant = IWN_LSB(sc->txchainmask);
                tx->rflags |= IWN_RFLAG_ANT(txant);
        } else {
-               tx->linkq = ni->ni_rates.rs_nrates - ni->ni_txrate - 1;
+#ifndef IEEE80211_NO_HT
+               if (ni->ni_flags & IEEE80211_NODE_HT)
+                       tx->linkq = 7 - ni->ni_txmcs; /* XXX revisit for MIMO */
+               else
+#endif
+                       tx->linkq = ni->ni_rates.rs_nrates - ni->ni_txrate - 1;
                flags |= IWN_TX_LINKQ;  /* enable MRR */
        }
        /* Set physical address of "scratch area". */
@@ -3304,21 +3410,49 @@ iwn_set_link_quality(struct iwn_softc *s
        linkq.id = wn->id;
        linkq.antmsk_1stream = txant;
        linkq.antmsk_2stream = IWN_ANT_AB;
-       linkq.ampdu_max = 31;
+       linkq.ampdu_max = 32;
        linkq.ampdu_threshold = 3;
        linkq.ampdu_limit = htole16(4000);      /* 4ms */
 
-       /* Start at highest available bit-rate. */
-       txrate = rs->rs_nrates - 1;
-       for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
-               rinfo = &iwn_rates[wn->ridx[txrate]];
-               linkq.retry[i].plcp = rinfo->plcp;
-               linkq.retry[i].rflags = rinfo->flags;
-               linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
-               /* Next retry at immediate lower bit-rate. */
-               if (txrate > 0)
-                       txrate--;
+#ifndef IEEE80211_NO_HT
+       if (ni->ni_flags & IEEE80211_NODE_HT) {
+               /* Fill LQ table with MCS 7 - 0 (XXX revisit for MIMO) */
+               i = 0;
+               for (txrate = 7; txrate >= 0; txrate--) {
+                       rinfo = &iwn_rates[iwn_mcs2ridx[txrate]];
+                       linkq.retry[i].plcp = rinfo->ht_plcp;
+                       linkq.retry[i].rflags = rinfo->ht_flags;
+
+                       /* XXX set correct ant mask for MIMO rates here */
+                       linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+
+                       if (i++ >= IWN_MAX_TX_RETRIES)
+                               break;
+               }
+               /* Fill the rest with MCS 0. */
+               rinfo = &iwn_rates[iwn_mcs2ridx[0]];
+               while (i < IWN_MAX_TX_RETRIES) {
+                       linkq.retry[i].plcp = rinfo->ht_plcp;
+                       linkq.retry[i].rflags = rinfo->ht_flags;
+                       linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+                       i++;
+               }
+       } else
+#endif
+       {
+               /* Start at highest available bit-rate. */
+               txrate = rs->rs_nrates - 1;
+               for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
+                       rinfo = &iwn_rates[wn->ridx[txrate]];
+                       linkq.retry[i].plcp = rinfo->plcp;
+                       linkq.retry[i].rflags = rinfo->flags;
+                       linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+                       /* Next retry at immediate lower bit-rate. */
+                       if (txrate > 0)
+                               txrate--;
+                       }
        }
+
        return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
 }
 
@@ -3326,7 +3460,7 @@ iwn_set_link_quality(struct iwn_softc *s
  * Broadcast node is used to send group-addressed and management frames.
  */
 int
-iwn_add_broadcast_node(struct iwn_softc *sc, int async)
+iwn_add_broadcast_node(struct iwn_softc *sc, int async, int ridx)
 {
        struct iwn_ops *ops = &sc->ops;
        struct iwn_node_info node;
@@ -3354,8 +3488,7 @@ iwn_add_broadcast_node(struct iwn_softc 
        linkq.ampdu_limit = htole16(4000);      /* 4ms */
 
        /* Use lowest mandatory bit-rate. */
-       rinfo = (sc->sc_ic.ic_curmode != IEEE80211_MODE_11A) ?
-           &iwn_rates[IWN_RIDX_CCK1] : &iwn_rates[IWN_RIDX_OFDM6];
+       rinfo = &iwn_rates[ridx];
        linkq.retry[0].plcp = rinfo->plcp;
        linkq.retry[0].rflags = rinfo->flags;
        linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant);
@@ -3564,8 +3697,10 @@ iwn4965_set_txpower(struct iwn_softc *sc
                for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
                        /* Convert dBm to half-dBm. */
                        maxchpwr = sc->maxpwr[chan] * 2;
-                       if ((ridx / 8) & 1)
+#ifdef notyet
+                       if (ridx > iwn_mcs2ridx[7] && ridx < iwn_mcs2ridx[16])
                                maxchpwr -= 6;  /* MIMO 2T: -3dB */
+#endif
 
                        pwr = maxpwr;
 
@@ -3584,7 +3719,7 @@ iwn4965_set_txpower(struct iwn_softc *sc
                                pwr = maxchpwr;
 
                        idx = gain - (pwr - power) - tdiff - vdiff;
-                       if ((ridx / 8) & 1)     /* MIMO */
+                       if (ridx > iwn_mcs2ridx[7]) /* MIMO */
                                idx += (int32_t)letoh32(uc->atten[grp][c]);
 
                        if (cmd.band == 0)
@@ -4277,7 +4412,7 @@ iwn_config(struct iwn_softc *sc)
        struct ifnet *ifp = &ic->ic_if;
        uint32_t txmask;
        uint16_t rxchain;
-       int error;
+       int error, ridx;
 
        /* Set radio temperature sensor offset. */
        if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
@@ -4376,7 +4511,9 @@ iwn_config(struct iwn_softc *sc)
                return error;
        }
 
-       if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
+       ridx = (sc->sc_ic.ic_curmode == IEEE80211_MODE_11A) ?
+           IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+       if ((error = iwn_add_broadcast_node(sc, 0, ridx)) != 0) {
                printf("%s: could not add broadcast node\n",
                    sc->sc_dev.dv_xname);
                return error;
@@ -4664,7 +4801,7 @@ iwn_auth(struct iwn_softc *sc)
        struct iwn_ops *ops = &sc->ops;
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_node *ni = ic->ic_bss;
-       int error;
+       int error, ridx;
 
        /* Update adapter configuration. */
        IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
@@ -4685,7 +4822,7 @@ iwn_auth(struct iwn_softc *sc)
                sc->rxon.cck_mask  = 0x03;
                sc->rxon.ofdm_mask = 0;
                break;
-       default:        /* Assume 802.11b/g. */
+       default:        /* Assume 802.11b/g/n. */
                sc->rxon.cck_mask  = 0x0f;
                sc->rxon.ofdm_mask = 0x15;
        }
@@ -4706,7 +4843,9 @@ iwn_auth(struct iwn_softc *sc)
         * Reconfiguring RXON clears the firmware nodes table so we must
         * add the broadcast node again.
         */
-       if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
+       ridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
+           IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+       if ((error = iwn_add_broadcast_node(sc, 1, ridx)) != 0) {
                printf("%s: could not add broadcast node\n",
                    sc->sc_dev.dv_xname);
                return error;
@@ -4742,7 +4881,53 @@ iwn_run(struct iwn_softc *sc)
        if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
                sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
        sc->rxon.filter |= htole32(IWN_FILTER_BSS);
-       DPRINTF(("rxon chan %d flags %x\n", sc->rxon.chan, sc->rxon.flags));
+#ifndef IEEE80211_NO_HT
+       /* HT is negotiated when associating. */
+       if (ni->ni_flags & IEEE80211_NODE_HT) {
+               enum ieee80211_htprot htprot =
+                   (ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK);
+               DPRINTF(("%s: htprot = %d\n", __func__, htprot));
+               switch (htprot) {
+               case IEEE80211_HTPROT_NONE:
+                       break;
+               case IEEE80211_HTPROT_NONMEMBER:
+               case IEEE80211_HTPROT_NONHT_MIXED:
+                       sc->rxon.flags |=
+                           htole32(IWN_RXON_HT_RXON_HT_MODE_MIXED);
+                       break;
+               case IEEE80211_HTPROT_20MHZ:
+                       sc->rxon.flags |=
+                           htole32(IWN_RXON_HT_RXON_HT_MODE_PURE40);
+                       break;
+               default:
+                       DPRINTF(("Unknown protection mode %d\n", htprot));
+                       break;
+               }
+       }
+#endif
+       if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
+               /* 11a or 11n 5GHz */
+               sc->rxon.cck_mask  = 0;
+               sc->rxon.ofdm_mask = 0x15;
+#ifndef IEEE80211_NO_HT
+       } else if (ni->ni_flags & IEEE80211_NODE_HT) {
+               /* 11n 2GHz */
+               sc->rxon.cck_mask  = 0x0f;
+               sc->rxon.ofdm_mask = 0x15;
+#endif
+       } else {
+               if (ni->ni_rates.rs_nrates == 4) {
+                       /* 11b */
+                       sc->rxon.cck_mask  = 0x03;
+                       sc->rxon.ofdm_mask = 0;
+               } else {
+                       /* assume 11g */
+                       sc->rxon.cck_mask  = 0x0f;
+                       sc->rxon.ofdm_mask = 0x15;
+               }
+       }
+       DPRINTF(("rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon.chan,
+           sc->rxon.flags, sc->rxon.cck_mask, sc->rxon.ofdm_mask));
        error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
        if (error != 0) {
                printf("%s: could not update configuration\n",
Index: if_iwnreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwnreg.h,v
retrieving revision 1.49
diff -u -p -r1.49 if_iwnreg.h
--- if_iwnreg.h 9 Sep 2014 18:56:24 -0000       1.49
+++ if_iwnreg.h 16 Dec 2015 13:34:32 -0000
@@ -494,6 +494,10 @@ struct iwn_rxon {
 #define IWN_RXON_ANTENNA_A     (1 <<  8)
 #define IWN_RXON_ANTENNA_B     (1 <<  9)
 #define IWN_RXON_TSF           (1 << 15)
+#define IWN_RXON_HT_HT40MINUS  (1 << 22)
+#define IWN_RXON_HT_PROTMODE(x)        ((x) << 22) /* 2 bits */
+#define IWN_RXON_HT_RXON_HT_MODE_PURE40        (1 << 25)
+#define IWN_RXON_HT_RXON_HT_MODE_MIXED (2 << 25)
 #define IWN_RXON_CTS_TO_SELF   (1 << 30)
 
        uint32_t        filter;
@@ -630,7 +634,12 @@ struct iwn4965_node_info {
        uint32_t        reserved7;
 } __packed;
 
+#define IWN_RFLAG_MCS          (1 << 0)
 #define IWN_RFLAG_CCK          (1 << 1)
+#define IWN_RFLAG_GREENFIELD   (1 << 2)
+#define IWN_RFLAG_HT40         (1 << 3)
+#define IWN_RFLAG_DUPLICATE    (1 << 4)
+#define IWN_RFLAG_SGI          (1 << 5)
 #define IWN_RFLAG_ANT(x)       ((x) << 6)
 
 /* Structure for command IWN_CMD_TX_DATA. */
@@ -1222,7 +1231,12 @@ struct iwn_rx_stat {
        uint64_t        tstamp;
        uint32_t        beacon;
        uint16_t        flags;
+#define IWN_STAT_FLAG_24GHZ            (1 << 0)
+#define IWN_STAT_FLAG_MOD_CCK          (1 << 1)
 #define IWN_STAT_FLAG_SHPREAMBLE       (1 << 2)
+#define IWN_STAT_FLAG_NARROW_BAND      (1 << 3)
+#define IWN_STAT_FLAG_ANT(x)           ((x) << 4) /* 3 bits */
+#define IWN_STAT_FLAG_AGG              (1 << 7)
 
        uint16_t        chan;
        uint8_t         phybuf[32];
@@ -1659,25 +1673,33 @@ static const struct iwn_chan_band {
 #define IWN_RIDX_CCK1  0
 #define IWN_RIDX_OFDM6 4
 
+#define IWN_PLCP_INVALID 0xff
+
 static const struct iwn_rate {
        uint8_t rate;
        uint8_t plcp;
        uint8_t flags;
+       uint8_t ht_plcp;
+       uint8_t ht_flags;
 } iwn_rates[IWN_RIDX_MAX + 1] = {
-       {   2,  10, IWN_RFLAG_CCK },
-       {   4,  20, IWN_RFLAG_CCK },
-       {  11,  55, IWN_RFLAG_CCK },
-       {  22, 110, IWN_RFLAG_CCK },
-       {  12, 0xd, 0 },
-       {  18, 0xf, 0 },
-       {  24, 0x5, 0 },
-       {  36, 0x7, 0 },
-       {  48, 0x9, 0 },
-       {  72, 0xb, 0 },
-       {  96, 0x1, 0 },
-       { 108, 0x3, 0 },
-       { 120, 0x3, 0 }
+               /* Legacy */            /* HT */ 
+       {   2,  10, IWN_RFLAG_CCK,      IWN_PLCP_INVALID, 0 },
+       {   4,  20, IWN_RFLAG_CCK,      IWN_PLCP_INVALID, 0 },
+       {  11,  55, IWN_RFLAG_CCK,      IWN_PLCP_INVALID, 0 },
+       {  22, 110, IWN_RFLAG_CCK,      IWN_PLCP_INVALID, 0 },
+       {  12, 0xd, 0,                  0, IWN_RFLAG_MCS    },
+       {  18, 0xf, 0,                  IWN_PLCP_INVALID, 0 },
+       {  24, 0x5, 0,                  1, IWN_RFLAG_MCS    },
+       {  36, 0x7, 0,                  2, IWN_RFLAG_MCS,   },
+       {  48, 0x9, 0,                  3, IWN_RFLAG_MCS,   },
+       {  72, 0xb, 0,                  4, IWN_RFLAG_MCS,   },
+       {  96, 0x1, 0,                  5, IWN_RFLAG_MCS,   },
+       { 108, 0x3, 0,                  6, IWN_RFLAG_MCS,   },
+       { 128, IWN_PLCP_INVALID, 0,     7, IWN_RFLAG_MCS,   }
 };
+
+/* Convert an MCS index into an iwn_rates[] index. */
+const int iwn_mcs2ridx[] = { 4, 6, 7, 8, 9, 10, 11, 12 };
 
 #define IWN4965_MAX_PWR_INDEX  107
 

Reply via email to