11n support for athn(4)

classic Classic list List threaded Threaded
53 messages Options
123
Reply | Threaded
Open this post in threaded view
|

11n support for athn(4)

Stefan Sperling-5
This diff adds 11n support to the athn(4) driver.
Requires -current net80211 code from today.

Tested in hostap mode and client mode with:
athn0 at pci1 dev 0 function 0 "Atheros AR9281" rev 0x01: apic 2 int 16
athn0: AR9280 rev 2 (2T2R), ROM rev 22, adddress xx:xx:xx:xx:xx:xx

And in client mode with:
athn0 at uhub1 port 2 configuration 1 interface 0 "ATHEROS USB2.0 WLAN" rev 2.00/1.08 addr 2
athn0: AR9271 rev 1 (1T1R), ROM rev 13, address xx:xx:xx:xx:xx:xx

Hostap performance is not perfect yet but should be no worse than
11a/b/g modes in the same environment.

For Linux clients a fix for WME params is needed which I also posted to tech@.

This diff does not modify the known-broken and disabled ar9003 code,
apart from making sure it still builds.

I'm looking for both tests and OKs.

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 10:14:41 -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,27 @@ 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_opmode == IEEE80211_M_STA &&
+    ic->ic_state == IEEE80211_S_RUN)
+#ifndef IEEE80211_STA_ONLY
+    || (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 +1133,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 +1338,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 +1370,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 +1491,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)
@@ -1523,9 +1564,10 @@ ar5008_tx(struct athn_softc *sc, struct
     SM(AR_TXC7_CHAIN_SEL1, sc->txchainmask) |
     SM(AR_TXC7_CHAIN_SEL2, sc->txchainmask) |
     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,7 +1584,7 @@ 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) ?
+ protridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
     ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK2;
  if (ds->ds_ctl0 & AR_TXC0_RTS_ENABLE) {
  /* Account for CTS duration. */
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 10:01:20 -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 *);
@@ -120,10 +121,12 @@ void athn_init_qos(struct athn_softc *)
 int athn_hw_reset(struct athn_softc *, struct ieee80211_channel *,
     struct ieee80211_channel *, int);
 struct ieee80211_node *athn_node_alloc(struct ieee80211com *);
+void athn_node_free(struct ieee80211com *, struct ieee80211_node *);
 void athn_newassoc(struct ieee80211com *, struct ieee80211_node *,
     int);
 int athn_media_change(struct ifnet *);
 void athn_next_scan(void *);
+void athn_iter_newstate(void *, struct ieee80211_node *);
 int athn_newstate(struct ieee80211com *, enum ieee80211_state,
     int);
 void athn_updateedca(struct ieee80211com *);
@@ -289,11 +292,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 +309,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)) {
@@ -316,6 +324,11 @@ athn_attach(struct athn_softc *sc)
  ic->ic_tx_mcs_set |= IEEE80211_TX_RX_MCS_NOT_EQUAL;
  ic->ic_tx_mcs_set |= (ntxstreams - 1) << 2;
  }
+
+ DPRINTF(("%s: htcaps=0x%x, MCS 0x%x%x%x%x\n",
+    sc->sc_dev.dv_xname, ic->ic_htcaps,
+    ic->ic_sup_mcs[0], ic->ic_sup_mcs[1],
+    ic->ic_sup_mcs[2], ic->ic_sup_mcs[3]));
  }
 
  /* Set supported rates. */
@@ -346,6 +359,8 @@ athn_attach(struct athn_softc *sc)
  if_attach(ifp);
  ieee80211_ifattach(ifp);
  ic->ic_node_alloc = athn_node_alloc;
+ sc->sc_node_free = ic->ic_node_free;
+ ic->ic_node_free = athn_node_free;
  ic->ic_newassoc = athn_newassoc;
  ic->ic_updateslot = athn_updateslot;
  ic->ic_updateedca = athn_updateedca;
@@ -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;
@@ -2310,6 +2338,19 @@ athn_node_alloc(struct ieee80211com *ic)
 }
 
 void
+athn_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct athn_softc *sc = ic->ic_softc;
+ struct athn_node *an = (void *)ni;
+
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+    ic->ic_state == IEEE80211_S_RUN)
+ ieee80211_mira_node_destroy(&an->mn);
+
+ sc->sc_node_free(ic, ni);
+}
+
+void
 athn_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
 {
  struct athn_softc *sc = ic->ic_softc;
@@ -2318,7 +2359,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)
+ ieee80211_mira_node_init(&an->mn);
+ else
+ ieee80211_amrr_node_init(&sc->amrr, &an->amn);
+
  /* Start at lowest available bit-rate, AMRR will raise. */
  ni->ni_txrate = 0;
 
@@ -2343,6 +2388,35 @@ 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]));
+ }
 }
 
 int
@@ -2387,6 +2461,19 @@ athn_next_scan(void *arg)
  splx(s);
 }
 
+void
+athn_iter_newstate(void *arg, struct ieee80211_node *ni)
+{
+ struct athn_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct athn_node *an = (struct athn_node *)ni;
+
+ /* Destroy MiRA node when hopping out of RUN state. */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+    ic->ic_state == IEEE80211_S_RUN)
+ ieee80211_mira_node_destroy(&an->mn);
+}
+
 int
 athn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
@@ -2397,6 +2484,11 @@ athn_newstate(struct ieee80211com *ic, e
 
  timeout_del(&sc->calib_to);
 
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ athn_iter_newstate(sc, ic->ic_bss);
+ else
+ ieee80211_iterate_nodes(ic, athn_iter_newstate, sc);
+
  switch (nstate) {
  case IEEE80211_S_INIT:
  athn_set_led(sc, 0);
@@ -2497,7 +2589,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 8 Jan 2017 22:41:47 -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;
 };
 
@@ -429,6 +436,8 @@ struct athn_softc {
 
  int (*sc_newstate)(struct ieee80211com *,
     enum ieee80211_state, int);
+ void (*sc_node_free)(struct ieee80211com *,
+    struct ieee80211_node *);
 
  bus_dma_tag_t sc_dmat;
 
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 10:57:15 -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>
@@ -1132,7 +1133,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 +1225,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 +1258,16 @@ 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;
 #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)

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
On Mon, Jan 09, 2017 at 01:54:55PM +0100, Stefan Sperling wrote:
> For Linux clients a fix for WME params is needed which I also posted to tech@.

That fix is now committed.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
In reply to this post by Stefan Sperling-5
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 | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Vadim Vygonets-2
Quoth Stefan Sperling on Tue, Jan 10, 2017:
> 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.

After applying the diff below, it compiled.

And it seems to work, although my testing was by no means
exhaustive.

Thank you,
Vadik.

Index: sys/conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.620
diff -u -p -u -r1.620 files
--- sys/conf/files 17 Jun 2016 19:20:19 -0000 1.620
+++ sys/conf/files 10 Jan 2017 17:57:50 -0000
@@ -800,6 +800,7 @@ file net80211/ieee80211_crypto_wep.c wla
 file net80211/ieee80211_input.c wlan
 file net80211/ieee80211_ioctl.c wlan
 file net80211/ieee80211_node.c wlan
+file net80211/ieee80211_mira.c wlan
 file net80211/ieee80211_output.c wlan
 file net80211/ieee80211_pae_input.c wlan
 file net80211/ieee80211_pae_output.c wlan

--
The good Christian should beware of mathematicians and all those who make
empty prophecies.  The danger already exists that mathematicians have made
a covenant with the devil to darken the spirit and confine man in the
bonds of Hell.  -- Saint Augustine

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Vadim Vygonets-2
Quoth Vadim Vygonets on Tue, Jan 10, 2017:
> Quoth Stefan Sperling on Tue, Jan 10, 2017:
> > 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.
>
> After applying the diff below, it compiled.

Please disregard the diff; my source update to -current wasn't
complete.

Vadik.

--
"Always apply the latest updates" and "If it ain't broke, don't
fix it" are the two rules of system administration.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
In reply to this post by Vadim Vygonets-2
On Tue, Jan 10, 2017 at 07:14:40PM +0100, Vadim Vygonets wrote:
> And it seems to work, although my testing was by no means
> exhaustive.

Thank you for testing!

Are you testing athn in hostap or in client mode?
If hostap, which kinds of clients are you testing with?

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Vadim Vygonets-2
Quoth Stefan Sperling on Tue, Jan 10, 2017:
> On Tue, Jan 10, 2017 at 07:14:40PM +0100, Vadim Vygonets wrote:
> > And it seems to work, although my testing was by no means
> > exhaustive.
>
> Thank you for testing!
>
> Are you testing athn in hostap or in client mode?

In client mode.  I can do hostap as well.  I have access to the
OpenBSD box with athn until Friday, and I'll be glad to run any
specific tests you want me to run.

> If hostap, which kinds of clients are you testing with?

I'm testing against LEDE (a fork of OpenWRT, i.e. Linux) with
some Atheros 9K card.  I can tell you the specifics tomorrow.

Vadik.

--
The best way to preserve a right is to exercise it, and the right to
smoke is a right worth dying for.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Vincent Gross-4
In reply to this post by Stefan Sperling-5
On Tue, 10 Jan 2017 00:27:47 +0100
Stefan Sperling <[hidden email]> 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.
>
> 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.
>

I just rebuilt a bsd.mp with your diff on my home router, so far so
good, works with my -current laptop iwn(4) card and the BCM4339 from my
android phone.

I REALLY wonder where can I find lots of heavy content on
the internet to stress this diff ... or not.

[snip]

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Brandon Mercer-3
On Tue, Jan 10, 2017 at 11:08:40PM +0100, Vincent Gross wrote:

> On Tue, 10 Jan 2017 00:27:47 +0100
> Stefan Sperling <[hidden email]> 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.
> >
> > 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.

I've thrown this on my apu2 and done some tests with my ipad, android
phones and some openbsd laptops. Streamed 1080p from youtube works on
one device but when I try with 2x it starts to buffer a bit.

Tested on 2.4Ghz:
athn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        index 4 priority 4 llprio 3
        groups: wlan
        media: IEEE802.11 autoselect hostap
        status: active
        ieee80211: nwid mynwid chan 7 bssid xxxxxxxxxxxxxxxxx wpakey 0x5324766a441a60e61749083489bcea5df6e73deb1ddbcbe6b1054270787bb4fe wpaprotos wpa2 wpaakms psk wpaciphers ccmp wpagroupcipher ccmp
        inet 192.168.1.1 netmask 0xffffff00 broadcast 192.168.1.255

OpenBSD shows:
media: IEEE802.11 autoselect (HT-MCS5 mode 11n)

I'm seeing some decent throughput on DL:
$ ftp
http://ftp5.usa.openbsd.org/pub/OpenBSD/snapshots/amd64/install60.fs 
Trying 129.21.208.172...
Requesting
http://ftp5.usa.openbsd.org/pub/OpenBSD/snapshots/amd64/install60.fs
100% |*******************************************************|   280 MB
08:53    
294092800 bytes received in 533.82 seconds (538.00 KB/s)

(That is about half what I see on my ubiquity)

With 5Ghz I'm only able to test on my android/ipad, and I'm able to stream a couple 1080p videos at the same time without any issues:
athn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        index 4 priority 4 llprio 3
        groups: wlan
        media: IEEE802.11 autoselect hostap (autoselect mode 11n hostap)
        status: active
        ieee80211: nwid mynwid chan 140 bssid xxxxxxxxxxxxxxxxx wpakey 0x5324766a441a60e61749083489bcea5df6e73deb1ddbcbe6b1054270787bb4fe wpaprotos wpa2 wpaakms psk wpaciphers ccmp wpagroupcipher ccmp
        inet 192.168.1.1 netmask 0xffffff00 broadcast 192.168.1.255
enc0: flags=0<>
        index 5 priority 0 llprio 3
        groups: enc
        status: active

I'll apply your other diffs and test more.
Cheers!

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
On Tue, Jan 10, 2017 at 08:34:32PM -0500, Brandon Mercer wrote:

> I've thrown this on my apu2 and done some tests with my ipad, android
> phones and some openbsd laptops. Streamed 1080p from youtube works on
> one device but when I try with 2x it starts to buffer a bit.
>
> Tested on 2.4Ghz:
> athn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
>         index 4 priority 4 llprio 3
>         groups: wlan
>         media: IEEE802.11 autoselect hostap
>         status: active
>         ieee80211: nwid mynwid chan 7 bssid xxxxxxxxxxxxxxxxx wpakey 0x5324766a441a60e61749083489bcea5df6e73deb1ddbcbe6b1054270787bb4fe wpaprotos wpa2 wpaakms psk wpaciphers ccmp wpagroupcipher ccmp
>         inet 192.168.1.1 netmask 0xffffff00 broadcast 192.168.1.255
>
> OpenBSD shows:
> media: IEEE802.11 autoselect (HT-MCS5 mode 11n)

You can see the Tx rate currently selected by the AP for each client
like this: ifconfig athn0 scan | grep assoc

In my case a 2GHz AP rarely selects more than MCS 3 since that rate
already triggers retries. If this is a common problem we could play
around a bit and allow a retry or two where the code currently has:
                if (failcnt > 0)
                        an->mn.retries++;
That might make things faster, or it might not.

Perhaps it will get better with Tx aggregation (not yet supported).

It is also possible that some other parameters are set up improperly
by the driver. The performance mismatch between 2 and 5 GHz is quite
staggering.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Vadim Vygonets-2
In reply to this post by Vadim Vygonets-2
Quoth Vadim Vygonets on Tue, Jan 10, 2017:

> Quoth Stefan Sperling on Tue, Jan 10, 2017:
> > On Tue, Jan 10, 2017 at 07:14:40PM +0100, Vadim Vygonets wrote:
> > > And it seems to work, although my testing was by no means
> > > exhaustive.
> >
> > Thank you for testing!
> >
> > Are you testing athn in hostap or in client mode?
>
> In client mode.  I can do hostap as well.

Tested hostap; it works.

The speed varies, but the drivers and/or wireless subsystem in
-current with your athn(4) patches are much more stable than in
6.0-stable.

> I'm testing against LEDE (a fork of OpenWRT, i.e. Linux) with
> some Atheros 9K card.  I can tell you the specifics tomorrow.

On OpenBSD side:
athn0 at pci0 dev 12 function 0 "Atheros AR9280" rev 0x01: irq 10
athn0: AR9280 rev 2 (2T2R), ROM rev 22, address xx:xx:xx:xx:xx:xx

On Linux side: Atheros AR9531 Rev:2 and Atheros AR9300 Rev:4.

Vadik.

--
You cannot achieve the impossible without attempting the absurd.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Peter Kay-5
In reply to this post by Stefan Sperling-5
On 9 January 2017 at 23:27, Stefan Sperling <[hidden email]> 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.
>
I'm getting

undefined reference to ieee80211_mira_node_init

ieee80211_mira_node_destroy and
ieee80211_mira_choose

i386, GENERIC. My firewall is not multi core. Running current with
updated patch.

PK

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
On Wed, Jan 11, 2017 at 10:00:50PM +0000, Peter Kay wrote:

> On 9 January 2017 at 23:27, Stefan Sperling <[hidden email]> 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.
> >
> I'm getting
>
> undefined reference to ieee80211_mira_node_init
>
> ieee80211_mira_node_destroy and
> ieee80211_mira_choose
>
> i386, GENERIC. My firewall is not multi core. Running current with
> updated patch.
>
> PK
>

You haven't updated all of src/sys.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Peter Kay-5
On 11 January 2017 at 22:05, Stefan Sperling <[hidden email]> wrote:

>
> You haven't updated all of src/sys.

I'm obviously being really dumb, but what is wrong with
cd /usr
tar zxf sys.tar.gz (from 6.0)
cvs -qd [hidden email]:/cvs update -dP sys
cd sys
patch -p0 < /path/to/wlan.diff
cd arch/i386/conf
config GENERIC
cd ../compile/GENERIC
make

? I see reference in current.html to
make obj (this does not work (don't know how to make obj).

Note : I am building this as root, because it's a VM (firewall is too
slow to build) and building releases is all it is doing

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Peter Kay-5
OK, got it working. Pulled the kernel sources direct from cvs without
pre-loading and it compiled.

Haven't had time for a proper test, but there are a couple of points.

Hardware MAC AR5416 rev 2, RF AR2133 (3T2R), ROM rev 5 (PCI adapter)

Blackberry Priv - connects between 100 and 130M at 2.4GHz 802.11n ,
throughput not tested yet

FreeBSD - iwn4965. Connects at 11ng, but if you start in 11g mode,
change the hostap to 11n, then reassociate the client it uses 11b. May
be a FreeBSD issue..?

Vista - same machine, connects at 130Mb.

Throughput testing, more machines later. Basic connectivity is working.

On 11 January 2017 at 23:28, Peter Kay <[hidden email]> wrote:

> On 11 January 2017 at 22:05, Stefan Sperling <[hidden email]> wrote:
>
>>
>> You haven't updated all of src/sys.
>
> I'm obviously being really dumb, but what is wrong with
> cd /usr
> tar zxf sys.tar.gz (from 6.0)
> cvs -qd [hidden email]:/cvs update -dP sys
> cd sys
> patch -p0 < /path/to/wlan.diff
> cd arch/i386/conf
> config GENERIC
> cd ../compile/GENERIC
> make
>
> ? I see reference in current.html to
> make obj (this does not work (don't know how to make obj).
>
> Note : I am building this as root, because it's a VM (firewall is too
> slow to build) and building releases is all it is doing

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
On Thu, Jan 12, 2017 at 06:19:06AM +0000, Peter Kay wrote:
> OK, got it working. Pulled the kernel sources direct from cvs without
> pre-loading and it compiled.

Thank you for testing!
 
> Haven't had time for a proper test, but there are a couple of points.
>
> Hardware MAC AR5416 rev 2, RF AR2133 (3T2R), ROM rev 5 (PCI adapter)
>
> Blackberry Priv - connects between 100 and 130M at 2.4GHz 802.11n ,
> throughput not tested yet

These data rates are all 'nominal'. The client uses 130Mbit/s only for
the very brief period of time when actual data is transmitted to the AP.
But a lot of time is spent on reserving the medium for beforehand, and
waiting for other clients and other APs to finish their transmissions.
So the actual throughput depends on environmental conditions such as how
loaded the wifi channel is.
 
> FreeBSD - iwn4965. Connects at 11ng, but if you start in 11g mode,
> change the hostap to 11n, then reassociate the client it uses 11b. May
> be a FreeBSD issue..?

I am not sure where the problem could be. The 4965 hardware does support 11g.

> Vista - same machine, connects at 130Mb.
>
> Throughput testing, more machines later. Basic connectivity is working.

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
In reply to this post by Stefan Sperling-5
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 | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Tom Murphy-7
In reply to this post by Stefan Sperling-5
Hi Stefan,

  Thanks very much for the 11n support in athn(4)! Here's mine:

athn0 at pci5 dev 0 function 0 "Atheros AR9281" rev 0x01: apic 2 int 16
athn0: AR9280 rev 2 (2T2R), ROM rev 22, address 04:f0:21:24:c8:4f

  I did some netperf tests between OpenBSD athn(4) in hostap mode (11n)
and a Linux wifi client (using an iwn(4) type chipset) and couldn't
get up to 4 MBps:

# netperf -4 -H 10.13.0.2 -t TCP_STREAM -C -c -l 60
MIGRATED TCP STREAM TEST from (null) (0.0.0.0) port 0 AF_INET to (null) () port 0 AF_INET : demo
Recv   Send    Send                          Utilization       Service Demand
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv
Size   Size    Size     Time     Throughput  local    remote   local   remote
bytes  bytes   bytes    secs.    10^6bits/s  % C      % ?      us/KB   us/KB

 87380  16384  16384    60.18         3.92   7.94     3.61     331.990  -151.019
# netperf -4 -H 10.13.0.2 -t TCP_STREAM -C -c -l 60
MIGRATED TCP STREAM TEST from (null) (0.0.0.0) port 0 AF_INET to (null) () port 0 AF_INET : demo
Recv   Send    Send                          Utilization       Service Demand
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv
Size   Size    Size     Time     Throughput  local    remote   local   remote
bytes  bytes   bytes    secs.    10^6bits/s  % C      % ?      us/KB   us/KB

 87380  16384  16384    61.11         3.95   7.41     2.11     307.442  -87.524

Linux iwconfig client shows Bit rate of 1 MB/s which doesn't look right.

Any ideas?

Thanks,
Tom

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Peter Kay-5
In reply to this post by Stefan Sperling-5
On 12 January 2017 at 15:06, Stefan Sperling <[hidden email]> wrote:
> 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.
>>
I'm now getting some very odd issues that did not previously exist
with my hostap before upgrading to the snapshot and applying the
patch.

Phone complains about an 'authentication problem', iwn laptop with
issues about buffer space and then associates with a different ap.
There's nothing in the logs and a reboot is required.

At first I thought it was a clock problem, because I found my firewall
clock was somehow a month ahead, but after correcting that the second
time it occurred the time was ok.

Going to have to revert to snapshot without the 11n patches and see if
the issue persists. Unfortunately it takes a while to manifest itself.

PK

Reply | Threaded
Open this post in threaded view
|

Re: 11n support for athn(4)

Stefan Sperling-5
On Sat, Jan 14, 2017 at 11:03:24AM +0000, Peter Kay wrote:
> On 12 January 2017 at 15:06, Stefan Sperling <[hidden email]> wrote:
> > 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.
> >>
> I'm now getting some very odd issues that did not previously exist
> with my hostap before upgrading to the snapshot and applying the
> patch.

It seems you have a 2GHz only card according to
https://wikidevi.com/wiki/Atheros
and what you wrote earlier:

 "Hardware MAC AR5416 rev 2, RF AR2133 (3T2R), ROM rev 5 (PCI adapter)"

So your first test might not have had 11n enabled since the first two
versions of my patch had a bug where 11n was left disabled on 2GHz only
devices. Please check with 'ifconfig media athn0' whether 11n mode is
actually supported.

> Phone complains about an 'authentication problem', iwn laptop with
> issues about buffer space and then associates with a different ap.
> There's nothing in the logs and a reboot is required.
>
> At first I thought it was a clock problem, because I found my firewall
> clock was somehow a month ahead, but after correcting that the second
> time it occurred the time was ok.
>
> Going to have to revert to snapshot without the 11n patches and see if
> the issue persists. Unfortunately it takes a while to manifest itself.

What does your /etc/hostname.athn0 file look like?
Please specify 'mode 11n' in your /etc/hostname.athn0, and also fix the
channel with a line such as 'chan 1'. There are known problems otherwise.
For reference, this is what I am using:

  media autoselect mode 11n mediaopt hostap chan 10
  nwid stsp.name wpakey xxxxxxx
  up

(There is no line configuring an IP since my athn0 is part of a bridge.)

Apart from that there are known performance issues on 2GHz.
I have always had bad performance from athn(4) on 2GHz, even in 11g mode.
Currently my 2GHz AP effectively transmits no more than 500Kbit/s.
But is runs stable apart from that.

It is unclear where this problem is coming from. Until now I wasn't sure
whether this is problem is specific to my environment or not (there are
a lot of 2GHz networks around here). It does not seems to be the case
since by now several people have reported this problem.
It seems the driver works a lot better with 5 GHz devices and lacks
something specific to the 2GHz ones.

123