On Tue, Jan 10, 2017 at 12:27:47AM +0100, Stefan Sperling wrote:
> On Mon, Jan 09, 2017 at 01:54:55PM +0100, Stefan Sperling wrote:
> > This diff adds 11n support to the athn(4) driver.
> > Requires -current net80211 code from today.
> 
> A better diff which fixes several bugs.

jmc@ found out I didn't enable 11n on 2GHz-only cards (such as AR9287).
This was a stupid logic error in the previous diff. This version is fixed.

Apart from that I have had no reports of problems. Can this go in?

Index: dev/cardbus/if_athn_cardbus.c
===================================================================
RCS file: /cvs/src/sys/dev/cardbus/if_athn_cardbus.c,v
retrieving revision 1.14
diff -u -p -r1.14 if_athn_cardbus.c
--- dev/cardbus/if_athn_cardbus.c       24 Nov 2015 17:11:39 -0000      1.14
+++ dev/cardbus/if_athn_cardbus.c       8 Jan 2017 09:31:28 -0000
@@ -43,6 +43,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/ar5008.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar5008.c,v
retrieving revision 1.37
diff -u -p -r1.37 ar5008.c
--- dev/ic/ar5008.c     29 Nov 2016 10:22:30 -0000      1.37
+++ dev/ic/ar5008.c     12 Jan 2017 06:10:49 -0000
@@ -51,6 +51,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
@@ -213,12 +214,24 @@ ar5008_attach(struct athn_softc *sc)
                return (EINVAL);
        }
 
-       if (base->opCapFlags & AR_OPFLAGS_11A)
+       if (base->opCapFlags & AR_OPFLAGS_11A) {
                sc->flags |= ATHN_FLAG_11A;
-       if (base->opCapFlags & AR_OPFLAGS_11G)
+               if ((base->opCapFlags & AR_OPFLAGS_11N_5G20) == 0)
+                       sc->flags |= ATHN_FLAG_11N;
+#ifdef notyet
+               if ((base->opCapFlags & AR_OPFLAGS_11N_5G40) == 0)
+                       sc->flags |= ATHN_FLAG_11N;
+#endif
+       }
+       if (base->opCapFlags & AR_OPFLAGS_11G) {
                sc->flags |= ATHN_FLAG_11G;
-       if (base->opCapFlags & AR_OPFLAGS_11N)
-               sc->flags |= ATHN_FLAG_11N;
+               if ((base->opCapFlags & AR_OPFLAGS_11N_2G20) == 0)
+                       sc->flags |= ATHN_FLAG_11N;
+#ifdef notyet
+               if ((base->opCapFlags & AR_OPFLAGS_11N_2G40) == 0)
+                       sc->flags |= ATHN_FLAG_11N;
+#endif
+       }
 
        IEEE80211_ADDR_COPY(ic->ic_myaddr, base->macAddr);
 
@@ -952,9 +965,11 @@ ar5008_tx_process(struct athn_softc *sc,
        struct ifnet *ifp = &ic->ic_if;
        struct athn_txq *txq = &sc->txq[qid];
        struct athn_node *an;
+       struct ieee80211_node *ni;
        struct athn_tx_buf *bf;
        struct ar_tx_desc *ds;
        uint8_t failcnt;
+       int txfail;
 
        bf = SIMPLEQ_FIRST(&txq->head);
        if (bf == NULL)
@@ -970,13 +985,16 @@ ar5008_tx_process(struct athn_softc *sc,
 
        sc->sc_tx_timer = 0;
 
-       if (ds->ds_status1 & AR_TXS1_EXCESSIVE_RETRIES)
+       txfail = (ds->ds_status1 & AR_TXS1_EXCESSIVE_RETRIES);
+       if (txfail)
                ifp->if_oerrors++;
 
        if (ds->ds_status1 & AR_TXS1_UNDERRUN)
                athn_inc_tx_trigger_level(sc);
 
        an = (struct athn_node *)bf->bf_ni;
+       ni = (struct ieee80211_node *)bf->bf_ni;
+
        /*
         * NB: the data fail count contains the number of un-acked tries
         * for the final series used.  We must add the number of tries for
@@ -987,10 +1005,26 @@ ar5008_tx_process(struct athn_softc *sc,
        failcnt += MS(ds->ds_status9, AR_TXS9_FINAL_IDX) * 2;
 
        /* Update rate control statistics. */
-       an->amn.amn_txcnt++;
-       if (failcnt > 0)
-               an->amn.amn_retrycnt++;
-
+       if (ni->ni_flags & IEEE80211_NODE_HT) {
+               an->mn.frames++;
+               an->mn.ampdu_size = bf->bf_m->m_pkthdr.len + IEEE80211_CRC_LEN;
+               an->mn.agglen = 1; /* XXX We do not yet support Tx agg. */
+               if (failcnt > 0)
+                       an->mn.retries++;
+               if (txfail)
+                       an->mn.txfail++;
+               if (ic->ic_state == IEEE80211_S_RUN) {
+#ifndef IEEE80211_STA_ONLY
+                       if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
+                           ni->ni_state == IEEE80211_STA_ASSOC)
+#endif
+                               ieee80211_mira_choose(&an->mn, ic, ni);
+               }
+       } else {
+               an->amn.amn_txcnt++;
+               if (failcnt > 0)
+                       an->amn.amn_retrycnt++;
+       }
        DPRINTFN(5, ("Tx done qid=%d status1=%d fail count=%d\n",
            qid, ds->ds_status1, failcnt));
 
@@ -1110,7 +1144,7 @@ ar5008_swba_intr(struct athn_softc *sc)
        ds->ds_ctl2 = SM(AR_TXC2_XMIT_DATA_TRIES0, 1);
 
        /* Write Tx rate. */
-       ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+       ridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
            ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK1;
        hwrate = athn_rates[ridx].hwrate;
        ds->ds_ctl3 = SM(AR_TXC3_XMIT_RATE0, hwrate);
@@ -1315,15 +1349,25 @@ ar5008_tx(struct athn_softc *sc, struct 
            IEEE80211_FC0_TYPE_DATA) {
                /* Use lowest rate for all tries. */
                ridx[0] = ridx[1] = ridx[2] = ridx[3] =
-                   (ic->ic_curmode == IEEE80211_MODE_11A) ?
-                       ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK1;
+                   (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
+                       ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK1);
+       } else if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+           ic->ic_fixed_mcs != -1) {
+               /* Use same fixed rate for all tries. */
+               ridx[0] = ridx[1] = ridx[2] = ridx[3] =
+                   ATHN_RIDX_MCS0 + ic->ic_fixed_mcs;
        } else if (ic->ic_fixed_rate != -1) {
                /* Use same fixed rate for all tries. */
                ridx[0] = ridx[1] = ridx[2] = ridx[3] =
                    sc->fixed_ridx;
        } else {
-               int txrate = ni->ni_txrate;
                /* Use fallback table of the node. */
+               int txrate;
+               
+               if (ni->ni_flags & IEEE80211_NODE_HT)
+                       txrate = ATHN_NUM_LEGACY_RATES + ni->ni_txmcs;
+               else
+                       txrate = ni->ni_txrate;
                for (i = 0; i < 4; i++) {
                        ridx[i] = an->ridx[txrate];
                        txrate = an->fallback[txrate];
@@ -1337,7 +1381,10 @@ ar5008_tx(struct athn_softc *sc, struct 
 
                tap->wt_flags = 0;
                /* Use initial transmit rate. */
-               tap->wt_rate = athn_rates[ridx[0]].rate;
+               if (athn_rates[ridx[0]].hwrate & 0x80) /* MCS */
+                       tap->wt_rate = athn_rates[ridx[0]].hwrate;
+               else
+                       tap->wt_rate = athn_rates[ridx[0]].rate;
                tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
                tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
                tap->wt_hwqueue = qid;
@@ -1455,11 +1502,16 @@ ar5008_tx(struct athn_softc *sc, struct 
 
        /* Check if frame must be protected using RTS/CTS or CTS-to-self. */
        if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+               enum ieee80211_htprot htprot;
+               
+               htprot = (ic->ic_bss->ni_htop1 & IEEE80211_HTOP1_PROT_MASK);
                /* NB: Group frames are sent using CCK in 802.11b/g. */
                if (totlen > ic->ic_rtsthreshold) {
                        ds->ds_ctl0 |= AR_TXC0_RTS_ENABLE;
-               } else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
-                   athn_rates[ridx[0]].phy == IEEE80211_T_OFDM) {
+               } else if (((ic->ic_flags & IEEE80211_F_USEPROT) &&
+                   athn_rates[ridx[0]].phy == IEEE80211_T_OFDM) ||
+                   ((ic->ic_flags & IEEE80211_F_HTON) &&
+                   htprot != IEEE80211_HTPROT_NONE)) {
                        if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
                                ds->ds_ctl0 |= AR_TXC0_RTS_ENABLE;
                        else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
@@ -1525,7 +1577,7 @@ ar5008_tx(struct athn_softc *sc, struct 
            SM(AR_TXC7_CHAIN_SEL3, sc->txchainmask);
 #ifdef notyet
        /* Use the same short GI setting for all tries. */
-       if (ic->ic_flags & IEEE80211_F_SHGI)
+       if (ni->ni_htcaps & IEEE80211_HTCAP_SGI20)
                ds->ds_ctl7 |= AR_TXC7_GI0123;
        /* Use the same channel width for all tries. */
        if (ic->ic_flags & IEEE80211_F_CBW40)
@@ -1542,8 +1594,8 @@ ar5008_tx(struct athn_softc *sc, struct 
                        ds->ds_ctl5 |= AR_TXC5_RTSCTS_QUAL23;
                }
                /* Select protection rate (suboptimal but ok). */
-               protridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
-                   ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK2;
+               protridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
+                   ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK1;
                if (ds->ds_ctl0 & AR_TXC0_RTS_ENABLE) {
                        /* Account for CTS duration. */
                        dur += athn_txtime(sc, IEEE80211_ACK_LEN,
Index: dev/ic/ar5008reg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar5008reg.h,v
retrieving revision 1.3
diff -u -p -r1.3 ar5008reg.h
--- dev/ic/ar5008reg.h  31 Dec 2010 17:50:48 -0000      1.3
+++ dev/ic/ar5008reg.h  12 Jan 2017 06:11:43 -0000
@@ -950,12 +950,11 @@ struct ar_base_eep_header {
        uint8_t         opCapFlags;
 #define AR_OPFLAGS_11A                 0x01
 #define AR_OPFLAGS_11G                 0x02
+/* NB: If set, 11n is _disabled_ in the corresponding mode: */
 #define AR_OPFLAGS_11N_5G40            0x04
 #define AR_OPFLAGS_11N_2G40            0x08
 #define AR_OPFLAGS_11N_5G20            0x10
 #define AR_OPFLAGS_11N_2G20            0x20
-/* Shortcut. */
-#define AR_OPFLAGS_11N                 0x3c
 
        uint8_t         eepMisc;
        uint16_t        regDmn[2];
Index: dev/ic/ar5416.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar5416.c,v
retrieving revision 1.19
diff -u -p -r1.19 ar5416.c
--- dev/ic/ar5416.c     5 Jan 2016 18:41:15 -0000       1.19
+++ dev/ic/ar5416.c     8 Jan 2017 09:29:59 -0000
@@ -51,6 +51,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/ar9003.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar9003.c,v
retrieving revision 1.41
diff -u -p -r1.41 ar9003.c
--- dev/ic/ar9003.c     29 Nov 2016 10:22:30 -0000      1.41
+++ dev/ic/ar9003.c     8 Jan 2017 09:30:50 -0000
@@ -51,6 +51,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/ar9280.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar9280.c,v
retrieving revision 1.25
diff -u -p -r1.25 ar9280.c
--- dev/ic/ar9280.c     5 Jan 2016 18:41:15 -0000       1.25
+++ dev/ic/ar9280.c     8 Jan 2017 09:30:11 -0000
@@ -51,6 +51,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/ar9285.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar9285.c,v
retrieving revision 1.26
diff -u -p -r1.26 ar9285.c
--- dev/ic/ar9285.c     5 Jan 2016 18:41:15 -0000       1.26
+++ dev/ic/ar9285.c     8 Jan 2017 09:30:24 -0000
@@ -52,6 +52,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/ar9287.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar9287.c,v
retrieving revision 1.24
diff -u -p -r1.24 ar9287.c
--- dev/ic/ar9287.c     5 Jan 2016 18:41:15 -0000       1.24
+++ dev/ic/ar9287.c     8 Jan 2017 09:30:37 -0000
@@ -51,6 +51,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/ar9380.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar9380.c,v
retrieving revision 1.24
diff -u -p -r1.24 ar9380.c
--- dev/ic/ar9380.c     5 Jan 2016 18:41:15 -0000       1.24
+++ dev/ic/ar9380.c     8 Jan 2017 15:10:10 -0000
@@ -49,6 +49,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/ic/athn.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/athn.c,v
retrieving revision 1.93
diff -u -p -r1.93 athn.c
--- dev/ic/athn.c       13 Apr 2016 10:49:26 -0000      1.93
+++ dev/ic/athn.c       10 Jan 2017 07:18:24 -0000
@@ -53,6 +53,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
@@ -93,7 +94,7 @@ int           athn_set_key(struct ieee80211com *,
                    struct ieee80211_key *);
 void           athn_delete_key(struct ieee80211com *, struct ieee80211_node *,
                    struct ieee80211_key *);
-void           athn_iter_func(void *, struct ieee80211_node *);
+void           athn_iter_calib(void *, struct ieee80211_node *);
 void           athn_calib_to(void *);
 int            athn_init_calib(struct athn_softc *,
                    struct ieee80211_channel *, struct ieee80211_channel *);
@@ -122,8 +123,11 @@ int                athn_hw_reset(struct athn_softc *, 
 struct         ieee80211_node *athn_node_alloc(struct ieee80211com *);
 void           athn_newassoc(struct ieee80211com *, struct ieee80211_node *,
                    int);
+void           athn_node_leave(struct ieee80211com *, struct ieee80211_node *);
 int            athn_media_change(struct ifnet *);
 void           athn_next_scan(void *);
+void           athn_iter_mira_delete(void *, struct ieee80211_node *);
+void           athn_delete_mira_nodes(struct athn_softc *);
 int            athn_newstate(struct ieee80211com *, enum ieee80211_state,
                    int);
 void           athn_updateedca(struct ieee80211com *);
@@ -289,11 +293,15 @@ athn_attach(struct athn_softc *sc)
                int i, ntxstreams, nrxstreams;
 
                /* Set HT capabilities. */
-               ic->ic_htcaps =
-                   IEEE80211_HTCAP_SMPS_DIS |
-                   IEEE80211_HTCAP_CBW20_40 |
+               ic->ic_htcaps = (IEEE80211_HTCAP_SMPS_DIS <<
+                   IEEE80211_HTCAP_SMPS_SHIFT);
+#ifdef notyet
+               ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40 |
                    IEEE80211_HTCAP_SGI40 |
                    IEEE80211_HTCAP_DSSSCCK40;
+#endif
+               ic->ic_htxcaps = 0;
+#ifdef notyet
                if (AR_SREV_9271(sc) || AR_SREV_9287_10_OR_LATER(sc))
                        ic->ic_htcaps |= IEEE80211_HTCAP_SGI20;
                if (AR_SREV_9380_10_OR_LATER(sc))
@@ -302,6 +310,7 @@ athn_attach(struct athn_softc *sc)
                        ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC;
                        ic->ic_htcaps |= 1 << IEEE80211_HTCAP_RXSTBC_SHIFT;
                }
+#endif
                ntxstreams = sc->ntxchains;
                nrxstreams = sc->nrxchains;
                if (!AR_SREV_9380_10_OR_LATER(sc)) {
@@ -346,6 +355,9 @@ athn_attach(struct athn_softc *sc)
        if_attach(ifp);
        ieee80211_ifattach(ifp);
        ic->ic_node_alloc = athn_node_alloc;
+#ifndef IEEE80211_STA_ONLY
+       ic->ic_node_leave = athn_node_leave;
+#endif
        ic->ic_newassoc = athn_newassoc;
        ic->ic_updateslot = athn_updateslot;
        ic->ic_updateedca = athn_updateedca;
@@ -370,10 +382,13 @@ void
 athn_detach(struct athn_softc *sc)
 {
        struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct ieee80211com *ic = &sc->sc_ic;
        int qid;
 
        timeout_del(&sc->scan_to);
        timeout_del(&sc->calib_to);
+       if (ic->ic_flags & IEEE80211_F_HTON)
+               athn_delete_mira_nodes(sc);
 
        if (!(sc->flags & ATHN_FLAG_USB)) {
                for (qid = 0; qid < ATHN_QID_COUNT; qid++)
@@ -425,6 +440,9 @@ athn_get_chanlist(struct athn_softc *sc)
                        ic->ic_channels[chan].ic_flags =
                            IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
                            IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
+                       if (sc->flags & ATHN_FLAG_11N)
+                               ic->ic_channels[chan].ic_flags |=
+                                   IEEE80211_CHAN_HT;
                }
        }
        if (sc->flags & ATHN_FLAG_11A) {
@@ -433,6 +451,9 @@ athn_get_chanlist(struct athn_softc *sc)
                        ic->ic_channels[chan].ic_freq =
                            ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
                        ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A;
+                       if (sc->flags & ATHN_FLAG_11N)
+                               ic->ic_channels[chan].ic_flags |=
+                                   IEEE80211_CHAN_HT;
                }
        }
 }
@@ -1206,12 +1227,13 @@ athn_btcoex_disable(struct athn_softc *s
 #endif
 
 void
-athn_iter_func(void *arg, struct ieee80211_node *ni)
+athn_iter_calib(void *arg, struct ieee80211_node *ni)
 {
        struct athn_softc *sc = arg;
        struct athn_node *an = (struct athn_node *)ni;
 
-       ieee80211_amrr_choose(&sc->amrr, ni, &an->amn);
+       if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
+               ieee80211_amrr_choose(&sc->amrr, ni, &an->amn);
 }
 
 void
@@ -1251,9 +1273,9 @@ athn_calib_to(void *arg)
 #endif
        if (ic->ic_fixed_rate == -1) {
                if (ic->ic_opmode == IEEE80211_M_STA)
-                       athn_iter_func(sc, ic->ic_bss);
+                       athn_iter_calib(sc, ic->ic_bss);
                else
-                       ieee80211_iterate_nodes(ic, athn_iter_func, sc);
+                       ieee80211_iterate_nodes(ic, athn_iter_calib, sc);
        }
        timeout_add_msec(&sc->calib_to, 500);
        splx(s);
@@ -1377,7 +1399,7 @@ athn_ani_ofdm_err_trigger(struct athn_so
                        ani->firstep_level++;
                        ops->set_firstep_level(sc, ani->firstep_level);
                }
-       } else if (sc->sc_ic.ic_curmode != IEEE80211_MODE_11A) {
+       } else if (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_bss->ni_chan)) {
                /*
                 * Beacon RSSI is low, if in b/g mode, turn off OFDM weak
                 * signal detection and zero first step level to maximize
@@ -1427,7 +1449,7 @@ athn_ani_cck_err_trigger(struct athn_sof
                        ani->firstep_level++;
                        ops->set_firstep_level(sc, ani->firstep_level);
                }
-       } else if (sc->sc_ic.ic_curmode != IEEE80211_MODE_11A) {
+       } else if (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_bss->ni_chan)) {
                /*
                 * Beacon RSSI is low, zero first step level to maximize
                 * CCK sensitivity.
@@ -1790,11 +1812,17 @@ athn_stop_tx_dma(struct athn_softc *sc, 
 int
 athn_txtime(struct athn_softc *sc, int len, int ridx, u_int flags)
 {
+       struct ieee80211com *ic = &sc->sc_ic;
 #define divround(a, b) (((a) + (b) - 1) / (b))
        int txtime;
 
-       /* XXX HT. */
-       if (athn_rates[ridx].phy == IEEE80211_T_OFDM) {
+       if (athn_rates[ridx].hwrate & 0x80) { /* MCS */
+               /* Assumes a 20MHz channel, HT-mixed frame format, no STBC. */
+               txtime = 8 + 8 + 4 + 4 + 4 * 4 + 8 /* HT PLCP */
+                   + 4 * ((8 * len + 16 + 6) / (athn_rates[ridx].rate * 2));
+               if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bss->ni_chan))
+                       txtime += 6; /* aSignalExtension */
+       } else if (athn_rates[ridx].phy == IEEE80211_T_OFDM) {
                txtime = divround(8 + 4 * len + 3, athn_rates[ridx].rate);
                /* SIFS is 10us for 11g but Signal Extension adds 6us. */
                txtime = 16 + 4 + 4 * txtime + 16;
@@ -2306,7 +2334,12 @@ athn_hw_reset(struct athn_softc *sc, str
 struct ieee80211_node *
 athn_node_alloc(struct ieee80211com *ic)
 {
-       return (malloc(sizeof(struct athn_node), M_DEVBUF, M_NOWAIT | M_ZERO));
+       struct athn_node *an;
+
+       an = malloc(sizeof(struct athn_node), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (ic->ic_flags & IEEE80211_F_HTON)
+               ieee80211_mira_node_init(&an->mn);
+       return (struct ieee80211_node *)an;
 }
 
 void
@@ -2318,7 +2351,11 @@ athn_newassoc(struct ieee80211com *ic, s
        uint8_t rate;
        int ridx, i, j;
 
-       ieee80211_amrr_node_init(&sc->amrr, &an->amn);
+       if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
+               ieee80211_amrr_node_init(&sc->amrr, &an->amn);
+       else if (ic->ic_opmode == IEEE80211_M_STA)
+               ieee80211_mira_node_init(&an->mn);
+
        /* Start at lowest available bit-rate, AMRR will raise. */
        ni->ni_txrate = 0;
 
@@ -2343,8 +2380,47 @@ athn_newassoc(struct ieee80211com *ic, s
                }
                DPRINTFN(2, ("%d fallbacks to %d\n", i, an->fallback[i]));
        }
+
+       /* In 11n mode, start at lowest available bit-rate, MiRA will raise. */
+       ni->ni_txmcs = 0;
+
+       for (i = 0; i <= ATHN_MCS_MAX; i++) {
+               /* Map MCS index to HW rate index. */
+               ridx = ATHN_NUM_LEGACY_RATES + i;
+               an->ridx[ridx] = ATHN_RIDX_MCS0 + i;
+
+               DPRINTFN(2, ("mcs %d index %d ", i, ridx));
+               /* Compute fallback rate for retries. */
+               if (i == 0 || i == 8) {
+                       /* MCS 0 and 8 fall back to the lowest legacy rate. */
+                       if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+                               an->fallback[ridx] = ATHN_RIDX_OFDM6;
+                       else
+                               an->fallback[ridx] = ATHN_RIDX_CCK1;
+               } else {
+                       /* Other MCS fall back to next supported lower MCS. */
+                       an->fallback[ridx] = ATHN_NUM_LEGACY_RATES + i;
+                       for (j = i - 1; j >= 0; j--) {
+                               if (!isset(ni->ni_rxmcs, j))
+                                       continue;
+                               an->fallback[ridx] = ATHN_NUM_LEGACY_RATES + j;
+                               break;
+                       }
+               }
+               DPRINTFN(2, (" fallback to %d\n", an->fallback[ridx]));
+       }
 }
 
+#ifndef IEEE80211_STA_ONLY
+void
+athn_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+       struct athn_node *an = (void *)ni;
+       if (ic->ic_flags & IEEE80211_F_HTON)
+               ieee80211_mira_node_destroy(&an->mn);
+}
+#endif
+
 int
 athn_media_change(struct ifnet *ifp)
 {
@@ -2387,6 +2463,26 @@ athn_next_scan(void *arg)
        splx(s);
 }
 
+void
+athn_iter_mira_delete(void *arg, struct ieee80211_node *ni)
+{
+       struct athn_node *an = (struct athn_node *)ni;
+       ieee80211_mira_node_destroy(&an->mn);
+}
+
+/* Delete pending timeouts managed by MiRA. */
+void
+athn_delete_mira_nodes(struct athn_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       if (ic->ic_opmode == IEEE80211_M_STA) {
+               struct athn_node *an = (struct athn_node *)ic->ic_bss;
+               ieee80211_mira_node_destroy(&an->mn);
+       } else
+               ieee80211_iterate_nodes(ic, athn_iter_mira_delete, sc);
+}
+
 int
 athn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
@@ -2397,6 +2493,10 @@ athn_newstate(struct ieee80211com *ic, e
 
        timeout_del(&sc->calib_to);
 
+       if ((ic->ic_flags & IEEE80211_F_HTON) &&
+           ic->ic_state == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN)
+               athn_delete_mira_nodes(sc);
+
        switch (nstate) {
        case IEEE80211_S_INIT:
                athn_set_led(sc, 0);
@@ -2497,7 +2597,7 @@ athn_clock_rate(struct athn_softc *sc)
        struct ieee80211com *ic = &sc->sc_ic;
        int clockrate;  /* MHz. */
 
-       if (ic->ic_curmode == IEEE80211_MODE_11A) {
+       if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
                if (sc->flags & ATHN_FLAG_FAST_PLL_CLOCK)
                        clockrate = AR_CLOCK_RATE_FAST_5GHZ_OFDM;
                else
Index: dev/ic/athnvar.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/athnvar.h,v
retrieving revision 1.36
diff -u -p -r1.36 athnvar.h
--- dev/ic/athnvar.h    5 Jan 2016 18:41:15 -0000       1.36
+++ dev/ic/athnvar.h    9 Jan 2017 22:32:56 -0000
@@ -120,14 +120,18 @@ struct athn_rxq {
 #define ATHN_RIDX_CCK2 1
 #define ATHN_RIDX_OFDM6        4
 #define ATHN_RIDX_MCS0 12
+#define ATHN_RIDX_MCS8 (ATHN_RIDX_MCS0 + 8)
 #define ATHN_RIDX_MCS15        27
 #define ATHN_RIDX_MAX  27
+#define ATHN_MCS_MAX   15
+#define ATHN_NUM_MCS   (ATHN_MCS_MAX + 1)
 #define ATHN_IS_HT_RIDX(ridx)  ((ridx) >= ATHN_RIDX_MCS0)
+#define ATHN_IS_MIMO_RIDX(ridx)        ((ridx) >= ATHN_RIDX_MCS8)
 
 static const struct athn_rate {
-       uint8_t rate;           /* Rate in 500Kbps unit or MCS if 0x80. */
-       uint8_t hwrate;         /* HW representation. */
-       uint8_t rspridx;        /* Control Response Frame rate index. */
+       uint16_t        rate;           /* Rate in 500Kbps unit. */
+       uint8_t         hwrate;         /* HW representation. */
+       uint8_t         rspridx;        /* Control Response Frame rate index. */
        enum    ieee80211_phytype phy;
 } athn_rates[] = {
        {    2, 0x1b, 0, IEEE80211_T_DS },
@@ -142,22 +146,22 @@ static const struct athn_rate {
        {   72, 0x0d, 8, IEEE80211_T_OFDM },
        {   96, 0x08, 8, IEEE80211_T_OFDM },
        {  108, 0x0c, 8, IEEE80211_T_OFDM },
-       { 0x80, 0x80, 8, IEEE80211_T_OFDM },
-       { 0x81, 0x81, 8, IEEE80211_T_OFDM },
-       { 0x82, 0x82, 8, IEEE80211_T_OFDM },
-       { 0x83, 0x83, 8, IEEE80211_T_OFDM },
-       { 0x84, 0x84, 8, IEEE80211_T_OFDM },
-       { 0x85, 0x85, 8, IEEE80211_T_OFDM },
-       { 0x86, 0x86, 8, IEEE80211_T_OFDM },
-       { 0x87, 0x87, 8, IEEE80211_T_OFDM },
-       { 0x88, 0x88, 8, IEEE80211_T_OFDM },
-       { 0x89, 0x89, 8, IEEE80211_T_OFDM },
-       { 0x8a, 0x8a, 8, IEEE80211_T_OFDM },
-       { 0x8b, 0x8b, 8, IEEE80211_T_OFDM },
-       { 0x8c, 0x8c, 8, IEEE80211_T_OFDM },
-       { 0x8d, 0x8d, 8, IEEE80211_T_OFDM },
-       { 0x8e, 0x8e, 8, IEEE80211_T_OFDM },
-       { 0x8f, 0x8f, 8, IEEE80211_T_OFDM }
+       {   13, 0x80, 4, IEEE80211_T_OFDM },
+       {   26, 0x81, 6, IEEE80211_T_OFDM },
+       {   39, 0x82, 6, IEEE80211_T_OFDM },
+       {   52, 0x83, 8, IEEE80211_T_OFDM },
+       {   78, 0x84, 8, IEEE80211_T_OFDM },
+       {  104, 0x85, 8, IEEE80211_T_OFDM },
+       {  117, 0x86, 8, IEEE80211_T_OFDM },
+       {  130, 0x87, 8, IEEE80211_T_OFDM },
+       {   26, 0x88, 4, IEEE80211_T_OFDM },
+       {   52, 0x89, 6, IEEE80211_T_OFDM },
+       {   78, 0x8a, 8, IEEE80211_T_OFDM },
+       {  104, 0x8b, 8, IEEE80211_T_OFDM },
+       {  156, 0x8c, 8, IEEE80211_T_OFDM },
+       {  208, 0x8d, 8, IEEE80211_T_OFDM },
+       {  234, 0x8e, 8, IEEE80211_T_OFDM },
+       {  260, 0x8f, 8, IEEE80211_T_OFDM }
 };
 
 struct athn_series {
@@ -288,11 +292,14 @@ static const uint16_t ar_mcs_ndbps[][2] 
 #define ATHN_POWER_OFDM_EXT    67
 #define ATHN_POWER_COUNT       68
 
+#define ATHN_NUM_LEGACY_RATES  IEEE80211_RATE_MAXSIZE
+#define ATHN_NUM_RATES         (ATHN_NUM_LEGACY_RATES + ATHN_NUM_MCS)
 struct athn_node {
        struct ieee80211_node           ni;
        struct ieee80211_amrr_node      amn;
-       uint8_t                         ridx[IEEE80211_RATE_MAXSIZE];
-       uint8_t                         fallback[IEEE80211_RATE_MAXSIZE];
+       struct ieee80211_mira_node      mn;
+       uint8_t                         ridx[ATHN_NUM_RATES];
+       uint8_t                         fallback[ATHN_NUM_RATES];
        uint8_t                         sta_index;
 };
 
Index: dev/pci/if_athn_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_athn_pci.c,v
retrieving revision 1.18
diff -u -p -r1.18 if_athn_pci.c
--- dev/pci/if_athn_pci.c       24 Nov 2015 17:11:39 -0000      1.18
+++ dev/pci/if_athn_pci.c       8 Jan 2017 09:31:15 -0000
@@ -43,6 +43,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
Index: dev/usb/if_athn_usb.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_athn_usb.c,v
retrieving revision 1.43
diff -u -p -r1.43 if_athn_usb.c
--- dev/usb/if_athn_usb.c       29 Nov 2016 10:22:30 -0000      1.43
+++ dev/usb/if_athn_usb.c       9 Jan 2017 23:21:57 -0000
@@ -48,6 +48,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_mira.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/ic/athnreg.h>
@@ -1023,9 +1024,7 @@ athn_usb_newstate_cb(struct athn_usb_sof
        struct ieee80211com *ic = &sc->sc_ic;
        enum ieee80211_state ostate;
        uint32_t reg, imask;
-#ifndef IEEE80211_STA_ONLY
        uint8_t sta_index;
-#endif
        int s, error;
 
        timeout_del(&sc->calib_to);
@@ -1035,14 +1034,10 @@ athn_usb_newstate_cb(struct athn_usb_sof
        DPRINTF(("newstate %d -> %d\n", ostate, cmd->state));
 
        if (ostate == IEEE80211_S_RUN) {
-#ifndef IEEE80211_STA_ONLY
-               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-                       /* XXX really needed? */
-                       sta_index = ((struct athn_node *)ic->ic_bss)->sta_index;
-                       (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
-                           &sta_index, sizeof(sta_index), NULL);
-               }
-#endif
+               sta_index = ((struct athn_node *)ic->ic_bss)->sta_index;
+               (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
+                   &sta_index, sizeof(sta_index), NULL);
+               usc->nnodes--;
                reg = AR_READ(sc, AR_RX_FILTER);
                reg = (reg & ~AR_RX_FILTER_MYBEACON) |
                    AR_RX_FILTER_BEACON;
@@ -1072,13 +1067,8 @@ athn_usb_newstate_cb(struct athn_usb_sof
                if (ic->ic_opmode == IEEE80211_M_MONITOR)
                        break;
 
-#ifndef IEEE80211_STA_ONLY
-               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-                       /* Create node entry for our BSS */
-                       /* XXX really needed? breaks station mode on down/up */
-                       error = athn_usb_create_node(usc, ic->ic_bss);
-               }
-#endif
+               /* Create node entry for our BSS */
+               error = athn_usb_create_node(usc, ic->ic_bss);
 
                athn_set_bss(sc, ic->ic_bss);
                athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
@@ -1132,7 +1122,7 @@ athn_usb_newassoc_cb(struct athn_usb_sof
 
        s = splnet();
        /* NB: Node may have left before we got scheduled. */
-       if (ni->ni_associd != 0)
+       if (ni->ni_associd != 0 && ni->ni_state == IEEE80211_STA_ASSOC)
                (void)athn_usb_create_node(usc, ni);
        ieee80211_release_node(ic, ni);
        splx(s);
@@ -1224,7 +1214,7 @@ athn_usb_create_node(struct athn_usb_sof
        struct athn_node *an = (struct athn_node *)ni;
        struct ar_htc_target_sta sta;
        struct ar_htc_target_rate rate;
-       int error;
+       int error, i, j;
 
        /* Firmware cannot handle more than 8 STAs. */
        if (usc->nnodes > AR_USB_MAX_STA)
@@ -1257,8 +1247,19 @@ athn_usb_create_node(struct athn_usb_sof
            ni->ni_rates.rs_nrates);
        if (ni->ni_flags & IEEE80211_NODE_HT) {
                rate.capflags |= htobe32(AR_RC_HT_FLAG);
+               /* Setup HT rates. */
+               for (i = 0, j = 0; i < IEEE80211_HT_NUM_MCS; i++) {
+                       if (!isset(ni->ni_rxmcs, i))
+                               continue;
+                       if (j >= AR_HTC_RATE_MAX)
+                               break;
+                       rate.ht_rates.rs_rates[j++] = i;
+               }
+               rate.ht_rates.rs_nrates = j;
+
+               if (ni->ni_rxmcs[1]) /* dual-stream MIMO rates */
+                       rate.capflags |= htobe32(AR_RC_DS_FLAG);
 #ifdef notyet
-               /* XXX setup HT rates */
                if (ni->ni_htcaps & IEEE80211_HTCAP_CBW20_40)
                        rate.capflags |= htobe32(AR_RC_40_FLAG);
                if (ni->ni_htcaps & IEEE80211_HTCAP_SGI40)
Index: dev/usb/if_athn_usb.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_athn_usb.h,v
retrieving revision 1.6
diff -u -p -r1.6 if_athn_usb.h
--- dev/usb/if_athn_usb.h       2 Mar 2015 14:46:02 -0000       1.6
+++ dev/usb/if_athn_usb.h       9 Jan 2017 22:59:18 -0000
@@ -141,10 +141,10 @@ struct ar_htc_target_rate {
        uint8_t                 isnew;
        uint32_t                capflags;
 #define AR_RC_DS_FLAG  0x00000001
-#define AR_RC_TS_FLAG  0x00000002
-#define AR_RC_40_FLAG  0x00000004
-#define AR_RC_SGI_FLAG 0x00000008
-#define AR_RC_HT_FLAG  0x00000010
+#define AR_RC_40_FLAG  0x00000002
+#define AR_RC_SGI_FLAG 0x00000004
+#define AR_RC_HT_FLAG  0x00000008
+#define AR_RC_STBC_FLAG        0x00000020
 
        struct ar_htc_rateset   lg_rates;
        struct ar_htc_rateset   ht_rates;


Reply via email to