On Sat, Dec 19, 2015 at 01:08:26PM +0100, Stefan Sperling wrote:
> On Fri, Dec 18, 2015 at 05:40:39PM -0500, David Hill wrote:
> > With sthen@'s patch I can associate, dhcp, and use net.
> 
> Here's an updated iwn diff with a better approach for Stuart's fix.
> 
> Thanks for helping, Stuart, and to everyone who sent beacons which
> allowed us to narrow this problem down to protection settings being
> set up the wrong way in iwn_run().

And another update (hopefully) fixing some reported issues, with some
uncommitted net80211 changes included.

I haven't put these diffs in yet because I'm still hearing about regressions
in some form or another. Sometimes it's unclear what people are running,
so I hope this version will linger for a bit and get tested.
Thanks for all the help so far from more people than I expected!

Index: dev/pci/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
--- dev/pci/if_iwn.c    25 Nov 2015 03:09:59 -0000      1.148
+++ dev/pci/if_iwn.c    20 Dec 2015 11:18:52 -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 *);
@@ -280,7 +280,7 @@ void                iwn_stop(struct ifnet *, int);
 #ifdef IWN_DEBUG
 #define DPRINTF(x)     do { if (iwn_debug > 0) printf x; } while (0)
 #define DPRINTFN(n, x) do { if (iwn_debug >= (n)) printf x; } while (0)
-int iwn_debug = 0;
+int iwn_debug = 1;
 #else
 #define DPRINTF(x)
 #define DPRINTFN(n, x)
@@ -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))
@@ -2857,7 +2940,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf
 
        /* Check if frame must be protected using RTS/CTS or CTS-to-self. */
        if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
-               /* NB: Group frames are sent using CCK in 802.11b/g. */
+               /* NB: Group frames are sent using CCK in 802.11b/g/n (2GHz). */
                if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
                        flags |= IWN_TX_NEED_RTS;
                } else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
@@ -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) {
@@ -4343,8 +4478,13 @@ iwn_config(struct iwn_softc *sc)
        IEEE80211_ADDR_COPY(sc->rxon.wlap, ic->ic_myaddr);
        sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
        sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
-       if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan))
+       if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) {
                sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
+               if (ic->ic_flags & IEEE80211_F_USEPROT)
+                       sc->rxon.flags |= htole32(IWN_RXON_TGG_PROT);
+               DPRINTF(("%s: 2ghz prot 0x%x\n", __func__,
+                   le32toh(sc->rxon.flags)));
+       }
        switch (ic->ic_opmode) {
        case IEEE80211_M_STA:
                sc->rxon.mode = IWN_MODE_STA;
@@ -4376,7 +4516,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,14 +4806,19 @@ 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);
        sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
        sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
-       if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+       if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
                sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
+               if (ic->ic_flags & IEEE80211_F_USEPROT)
+                       sc->rxon.flags |= htole32(IWN_RXON_TGG_PROT);
+               DPRINTF(("%s: 2ghz prot 0x%x\n", __func__,
+                   le32toh(sc->rxon.flags)));
+       }
        if (ic->ic_flags & IEEE80211_F_SHSLOT)
                sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
        if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
@@ -4685,12 +4832,13 @@ 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;
        }
-       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));
+       DPRINTF(("%s: rxon chan %d flags %x cck %x ofdm %x\n", __func__,
+           sc->rxon.chan, le32toh(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: RXON command failed\n", sc->sc_dev.dv_xname);
@@ -4706,7 +4854,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 +4892,40 @@ 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));
+               sc->rxon.flags |= htole32(IWN_RXON_HT_PROTMODE(htprot));
+       } else
+               sc->rxon.flags &= ~htole32(IWN_RXON_HT_PROTMODE(3));
+#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(("%s: rxon chan %d flags %x cck %x ofdm %x\n", __func__,
+           sc->rxon.chan, le32toh(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: dev/pci/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
--- dev/pci/if_iwnreg.h 9 Sep 2014 18:56:24 -0000       1.49
+++ dev/pci/if_iwnreg.h 20 Dec 2015 10:27:55 -0000
@@ -488,12 +488,17 @@ struct iwn_rxon {
 #define IWN_RXON_24GHZ         (1 <<  0)
 #define IWN_RXON_CCK           (1 <<  1)
 #define IWN_RXON_AUTO          (1 <<  2)
+#define IWN_RXON_TGG_PROT      (1 <<  3)
 #define IWN_RXON_SHSLOT                (1 <<  4)
 #define IWN_RXON_SHPREAMBLE    (1 <<  5)
 #define IWN_RXON_NODIVERSITY   (1 <<  7)
 #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) << 23) /* 2 bits */
+#define IWN_RXON_HT_CHANMODE_PURE40    (1 << 25)
+#define IWN_RXON_HT_CHANMODE_MIXED2040 (2 << 25)
 #define IWN_RXON_CTS_TO_SELF   (1 << 30)
 
        uint32_t        filter;
@@ -630,7 +635,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 +1232,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 +1674,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
 
Index: net80211/ieee80211.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211.c,v
retrieving revision 1.53
diff -u -p -r1.53 ieee80211.c
--- net80211/ieee80211.c        18 Dec 2015 07:42:24 -0000      1.53
+++ net80211/ieee80211.c        19 Dec 2015 11:14:02 -0000
@@ -875,6 +875,14 @@ ieee80211_next_mode(struct ifnet *ifp)
                /* Wrap around and ignore turbo mode */
                if (ic->ic_curmode == IEEE80211_MODE_TURBO)
                        continue;
+#ifndef IEEE80211_NO_HT
+               /* 
+                * Skip over 11n mode. Its set of channels is the superset
+                * of all channels supported by the other modes.
+                */
+               if (ic->ic_curmode == IEEE80211_MODE_11N)
+                       continue;
+#endif
                if (ic->ic_curmode >= IEEE80211_MODE_MAX) {
                        ic->ic_curmode = IEEE80211_MODE_AUTO;
                        break;
@@ -894,6 +902,10 @@ ieee80211_next_mode(struct ifnet *ifp)
  * caller can select a rate set.  This is problematic and the
  * work here assumes how things work elsewhere in this code.
  *
+ * Because the result of this function is ultimately used to select a
+ * rate from the rate set of the returned mode, it must not return
+ * IEEE80211_MODE_11N, which uses MCS instead of rates for unicast frames.
+ *
  * XXX never returns turbo modes -dcy
  */
 enum ieee80211_phymode
@@ -904,19 +916,15 @@ ieee80211_chan2mode(struct ieee80211com 
         * NB: this assumes the channel would not be supplied to us
         *     unless it was already compatible with the current mode.
         */
-       if (ic->ic_curmode != IEEE80211_MODE_AUTO ||
-           chan == IEEE80211_CHAN_ANYC)
+       if (ic->ic_curmode != IEEE80211_MODE_11N &&
+           (ic->ic_curmode != IEEE80211_MODE_AUTO ||
+           chan == IEEE80211_CHAN_ANYC))
                return ic->ic_curmode;
        /*
-        * In autoselect mode; deduce a mode based on the channel
+        * In autoselect or 11n mode; deduce a mode based on the channel
         * characteristics.  We assume that turbo-only channels
         * are not considered when the channel set is constructed.
         */
-#ifndef IEEE80211_NO_HT
-       if (IEEE80211_IS_CHAN_N(chan))
-               return IEEE80211_MODE_11N;
-       else
-#endif
        if (IEEE80211_IS_CHAN_T(chan))
                return IEEE80211_MODE_TURBO;
        else if (IEEE80211_IS_CHAN_5GHZ(chan))
Index: net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.145
diff -u -p -r1.145 ieee80211_input.c
--- net80211/ieee80211_input.c  12 Dec 2015 13:56:10 -0000      1.145
+++ net80211/ieee80211_input.c  18 Dec 2015 13:35:17 -0000
@@ -210,8 +210,7 @@ ieee80211_input_print(struct ieee80211co
            "%s: received %s from %s rssi %d mode %s\n", ifp->if_xname,
            ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
            ether_sprintf(wh->i_addr2), rxi->rxi_rssi,
-           ieee80211_phymode_name[ieee80211_chan2mode(
-               ic, ic->ic_bss->ni_chan)]);
+           ieee80211_phymode_name[ic->ic_curmode]);
 
        task_set(&msg->task, ieee80211_input_print_task, msg);
        task_add(systq, &msg->task);
@@ -2250,24 +2249,17 @@ ieee80211_recv_assoc_resp(struct ieee802
        ieee80211_ht_negotiate(ic, ni);
 
        /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */
-       if (ic->ic_curmode != IEEE80211_MODE_AUTO &&
-           ic->ic_curmode != IEEE80211_MODE_11N &&
-           (ni->ni_flags & IEEE80211_NODE_HT))
+       if (ni->ni_flags & IEEE80211_NODE_HT)
                ieee80211_setmode(ic, IEEE80211_MODE_11N);
-
-       /* Hop out of 11n mode after associating to a non-HT AP. */
-       if (ic->ic_curmode == IEEE80211_MODE_11N &&
-           (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
-               if (IEEE80211_IS_CHAN_T(ni->ni_chan))
-                       ieee80211_setmode(ic, IEEE80211_MODE_TURBO);
-               else if (IEEE80211_IS_CHAN_A(ni->ni_chan))
-                       ieee80211_setmode(ic, IEEE80211_MODE_11A);
-               else if (IEEE80211_IS_CHAN_G(ni->ni_chan))
-                       ieee80211_setmode(ic, IEEE80211_MODE_11G);
-               else
-                       ieee80211_setmode(ic, IEEE80211_MODE_11B);
-       }
+       else
 #endif
+               ieee80211_setmode(ic, ieee80211_chan2mode(ic, ni->ni_chan));
+       /*
+        * Reset the erp state (mostly the slot time) now that
+        * our operating mode has been nailed down.
+        */
+       ieee80211_reset_erp(ic);
+
        /*
         * Configure state now that we are associated.
         */
Index: net80211/ieee80211_ioctl.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_ioctl.c,v
retrieving revision 1.39
diff -u -p -r1.39 ieee80211_ioctl.c
--- net80211/ieee80211_ioctl.c  14 Mar 2015 03:38:51 -0000      1.39
+++ net80211/ieee80211_ioctl.c  20 Dec 2015 11:40:02 -0000
@@ -647,6 +647,19 @@ ieee80211_ioctl(struct ifnet *ifp, u_lon
                        ic->ic_scan_lock |= IEEE80211_SCAN_REQUEST;
                        if (ic->ic_state != IEEE80211_S_SCAN) {
                                ieee80211_clean_cached(ic);
+                               if (ic->ic_opmode == IEEE80211_M_STA &&
+                                   ic->ic_state == IEEE80211_S_RUN &&
+                                   IFM_MODE(ic->ic_media.ifm_cur->ifm_media)
+                                   == IFM_AUTO) {
+                                       /* 
+                                        * We're already associated to an AP.
+                                        * Make the scanning loop start off in
+                                        * auto mode so all supported bands
+                                        * get scanned.
+                                        */
+                                       ieee80211_setmode(ic,
+                                           IEEE80211_MODE_AUTO);
+                               }
                                ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
                        }
                }
Index: net80211/ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.93
diff -u -p -r1.93 ieee80211_node.c
--- net80211/ieee80211_node.c   12 Dec 2015 11:25:46 -0000      1.93
+++ net80211/ieee80211_node.c   18 Dec 2015 13:11:35 -0000
@@ -637,14 +637,6 @@ ieee80211_end_scan(struct ifnet *ifp)
        (*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
        ni = ic->ic_bss;
 
-       /*
-        * Set the erp state (mostly the slot time) to deal with
-        * the auto-select case; this should be redundant if the
-        * mode is locked.
-        */
-       ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan);
-       ieee80211_reset_erp(ic);
-
        if (ic->ic_flags & IEEE80211_F_RSNON)
                ieee80211_choose_rsnparams(ic);
        else if (ic->ic_flags & IEEE80211_F_WEPON)
Index: net80211/ieee80211_proto.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_proto.c,v
retrieving revision 1.56
diff -u -p -r1.56 ieee80211_proto.c
--- net80211/ieee80211_proto.c  24 Nov 2015 13:45:06 -0000      1.56
+++ net80211/ieee80211_proto.c  18 Dec 2015 14:38:07 -0000
@@ -894,15 +894,8 @@ justcleanup:
                /* initialize bss for probe request */
                IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr);
                IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr);
-#ifndef IEEE80211_NO_HT
-               if (ic->ic_curmode == IEEE80211_MODE_11N)
-                       ni->ni_rates = ic->ic_sup_rates[
-                       IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ?
-                               IEEE80211_MODE_11G : IEEE80211_MODE_11A];
-               else
-#endif
-                       ni->ni_rates = ic->ic_sup_rates[
-                               ieee80211_chan2mode(ic, ni->ni_chan)];
+               ni->ni_rates = ic->ic_sup_rates[
+                       ieee80211_chan2mode(ic, ni->ni_chan)];
                ni->ni_associd = 0;
                ni->ni_rstamp = 0;
                switch (ostate) {

Reply via email to