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.

Most notably this should fix a crash in hostap mode triggered by clients
joining and leaving in a loop. This is fixed by making sure timeout handlers
managed by mira aren't overwritten when a client rejoins, and by cancelling
these timeouts properly. I'd like to rename some mira API functions for
better clarity but that's left for later.

This also restores USB device firmware rate scaling in client mode which was
disabled by commits I made in 2015. I found a missing 'usc->nnodes--;' in
the code from before those commits, and I hope adding that is a proper
fix for the problems we were hunting back then.

Known issues (not blocking issues IMO):

 - The athn(4) driver selects low transmit rates relative to what iwn(4)
   and iwm(4) clients select.

 - USB client in 11n mode only sends with legacy rates (up to 54Mbit/s).
   Technically this is legal behaviour, and receiving MCS sent by the AP works.
   Rate selection is done in firmware so this isn't straightforward to debug.

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     9 Jan 2017 22:30:38 -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>
@@ -217,7 +218,7 @@ ar5008_attach(struct athn_softc *sc)
                sc->flags |= ATHN_FLAG_11A;
        if (base->opCapFlags & AR_OPFLAGS_11G)
                sc->flags |= ATHN_FLAG_11G;
-       if (base->opCapFlags & AR_OPFLAGS_11N)
+       if ((base->opCapFlags & AR_OPFLAGS_11N_DISABLED) == 0)
                sc->flags |= ATHN_FLAG_11N;
 
        IEEE80211_ADDR_COPY(ic->ic_myaddr, base->macAddr);
@@ -952,9 +953,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 +973,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 +993,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 +1132,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 +1337,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 +1369,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 +1490,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 +1565,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 +1582,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  8 Jan 2017 15:08:19 -0000
@@ -950,12 +950,13 @@ 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
+/* Shortcut for "all of 11n is disabled". */
+#define AR_OPFLAGS_11N_DISABLED                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       9 Jan 2017 22:35:29 -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