This diff adds AMRR support to rtwn(4) (not urtwn(4) yet! PCI devices only).

This improves Tx performance with 8188CE hardware a lot. The firmware's
builtin rate scaling rarely selects rates above 1Mbit/s in my environment.
With this diff and tcpbench I've seen the driver crank it up to 54mbit/s.

Getting feedback on Tx status on this hardware is quirky.
The driver has to set a special bit to request Tx status reports, and then
the firmware will provide this information in "cpu to host" events.
This approach is based on how FreeBSD's rtwn driver does it, except we
poll "cpu to host" events directly from the tx_done interrupt handler.

Index: ic/r92creg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/r92creg.h,v
retrieving revision 1.5
diff -u -p -r1.5 r92creg.h
--- ic/r92creg.h        31 Jan 2017 09:21:46 -0000      1.5
+++ ic/r92creg.h        31 Jan 2017 10:43:28 -0000
@@ -95,9 +95,9 @@
 #define R92C_MBIST_START               0x174
 #define R92C_MBIST_DONE                        0x178
 #define R92C_MBIST_FAIL                        0x17c
-#define R92C_C2HEVT_MSG_NORMAL         0x1a0
+#define R92C_C2HEVT_MSG                        0x1a0
+#define R92C_C2HEVT_CLEAR              0x1af
 #define R92C_C2HEVT_MSG_TEST           0x1b8
-#define R92C_C2HEVT_CLEAR              0x1bf
 #define R92C_MCUTST_1                  0x1c0
 #define R92C_FMETHR                    0x1c8
 #define R92C_HMETFR                    0x1cc
@@ -1268,6 +1268,71 @@ struct r92c_tx_desc_usb {
 #define R92C_TXDW5_DATA_RETRY_LIMIT_S  18
 #define R92C_TXDW5_AGGNUM_M            0xff000000
 #define R92C_TXDW5_AGGNUM_S            24
+
+/*
+ * C2H event structure.
+ */
+#define R92C_C2H_MSG_MAX_LEN           16
+
+struct r92c_c2h_evt {
+       uint8_t         evtb0;
+#define R92C_C2H_EVTB0_ID_M            0x0f
+#define R92C_C2H_EVTB0_ID_S            0
+#define R92C_C2H_EVTB0_LEN_M           0xf0
+#define R92C_C2H_EVTB0_LEN_S           4
+
+       uint8_t         seq;
+
+       /* Followed by payload (see below). */
+} __packed;
+
+/* Bits for R92C_C2HEVT_CLEAR. */
+#define R92C_C2HEVT_HOST_CLOSE         0x00
+#define R92C_C2HEVT_FW_CLOSE           0xff
+
+/*
+ * C2H event types.
+ */
+#define R92C_C2HEVT_DEBUG              0
+#define R92C_C2HEVT_TX_REPORT          3
+#define R92C_C2HEVT_EXT_RA_RPT         6
+
+/* Structure for R92C_C2H_EVT_TX_REPORT event. */
+struct r92c_c2h_tx_rpt {
+       uint8_t         rptb0;
+#define R92C_RPTB0_RETRY_CNT_M         0x3f
+#define R92C_RPTB0_RETRY_CNT_S         0
+
+       uint8_t         rptb1;          /* XXX junk */
+#define R92C_RPTB1_RTS_RETRY_CNT_M     0x3f
+#define R92C_RPTB1_RTS_RETRY_CNT_S     0
+
+       uint8_t         queue_time_low;
+       uint8_t         queue_time_high;
+       uint8_t         rptb4;
+#define R92C_RPTB4_MISSED_PKT_NUM_M    0x1f
+#define R92C_RPTB4_MISSED_PKT_NUM_S    0
+
+       uint8_t         rptb5;
+#define R92C_RPTB5_MACID_M             0x1f
+#define R92C_RPTB5_MACID_S             0
+#define R92C_RPTB5_DES1_FRAGSSN_M      0xe0
+#define R92C_RPTB5_DES1_FRAGSSN_S      5
+
+       uint8_t         rptb6;
+#define R92C_RPTB6_RPT_PKT_NUM_M       0x1f
+#define R92C_RPTB6_RPT_PKT_NUM_S       0
+#define R92C_RPTB6_PKT_DROP            0x20
+#define R92C_RPTB6_LIFE_EXPIRE         0x40
+#define R92C_RPTB6_RETRY_OVER          0x80
+
+       uint8_t         rptb7;
+#define R92C_RPTB7_EDCA_M              0x0f
+#define R92C_RPTB7_EDCA_S              0
+#define R92C_RPTB7_BMC                 0x20
+#define R92C_RPTB7_PKT_OK              0x40
+#define R92C_RPTB7_INT_CCX             0x80
+} __packed;
 
 /*
  * MAC initialization values.
Index: ic/rtwn.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/rtwn.c,v
retrieving revision 1.17
diff -u -p -r1.17 rtwn.c
--- ic/rtwn.c   31 Jan 2017 15:15:13 -0000      1.17
+++ ic/rtwn.c   31 Jan 2017 15:32:05 -0000
@@ -642,7 +642,7 @@ rtwn_media_change(struct ifnet *ifp)
 }
 
 /*
- * Initialize rate adaptation in firmware.
+ * Initialize rate adaptation.
  */
 int
 rtwn_ra_init(struct rtwn_softc *sc)
@@ -686,11 +686,11 @@ rtwn_ra_init(struct rtwn_softc *sc)
                /* Configure Automatic Rate Fallback Register. */
                if (ic->ic_curmode == IEEE80211_MODE_11B) {
                        if (rates & 0x0c)
-                               rtwn_write_4(sc, R92C_ARFR(0), rates & 0x0d);
+                               rtwn_write_4(sc, R92C_ARFR(0), rates & 0x05);
                        else
-                               rtwn_write_4(sc, R92C_ARFR(0), rates & 0x0f);
+                               rtwn_write_4(sc, R92C_ARFR(0), rates & 0x07);
                } else
-                       rtwn_write_4(sc, R92C_ARFR(0), rates & 0x0ff5);
+                       rtwn_write_4(sc, R92C_ARFR(0), rates & 0x07f5);
        }
 
        if (sc->chip & RTWN_CHIP_88E) {
@@ -699,14 +699,26 @@ rtwn_ra_init(struct rtwn_softc *sc)
                /* We use AMRR with this chip. Start with the lowest rate. */
                ni->ni_txrate = 0;
        } else {
-               error = rtwn_r92c_ra_init(sc, mode, rates, maxrate,
-                   basicrates, maxbasicrate);
-               /* No AMRR support yet. Indicate highest supported rate. */
-               ni->ni_txrate = rs->rs_nrates - 1;
+               if (sc->chip & RTWN_CHIP_PCI) {
+                       ni->ni_txrate = 0; /* AMRR will raise. */
+                       /* Set initial MRR rates. */
+                       rtwn_write_1(sc,
+                           R92C_INIDATA_RATE_SEL(R92C_MACID_BC), maxbasicrate);
+                       rtwn_write_1(sc,
+                           R92C_INIDATA_RATE_SEL(R92C_MACID_BSS), 0);
+               } else {
+                       error = rtwn_r92c_ra_init(sc, mode, rates, maxrate,
+                           basicrates, maxbasicrate);
+                       /* No AMRR support. Indicate highest supported rate. */
+                       ni->ni_txrate = rs->rs_nrates - 1;
+               }
        }
        return (error);
 }
 
+/*
+ * Initialize rate adaptation in firmware.
+ */
 int rtwn_r92c_ra_init(struct rtwn_softc *sc, u_int8_t mode, u_int32_t rates,
     int maxrate, uint32_t basicrates, int maxbasicrate)
 {
Index: pci/if_rtwn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_rtwn.c,v
retrieving revision 1.25
diff -u -p -r1.25 if_rtwn.c
--- pci/if_rtwn.c       22 Jan 2017 10:17:38 -0000      1.25
+++ pci/if_rtwn.c       31 Jan 2017 15:44:33 -0000
@@ -3,6 +3,7 @@
 /*-
  * Copyright (c) 2010 Damien Bergamini <damien.bergam...@free.fr>
  * Copyright (c) 2015 Stefan Sperling <s...@openbsd.org>
+ * Copyright (c) 2015-2016 Andriy Voskoboinyk <a...@freebsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -49,6 +50,7 @@
 #include <netinet/if_ether.h>
 
 #include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
 #include <net80211/ieee80211_radiotap.h>
 
 #include <dev/pci/pcireg.h>
@@ -161,6 +163,9 @@ struct rtwn_pci_softc {
        bus_size_t              sc_mapsize;
        int                     sc_cap_off;
 
+       struct ieee80211_amrr           amrr;
+       struct ieee80211_amrr_node      amn;
+
 #if NBPFILTER > 0
        caddr_t                         sc_drvbpf;
 
@@ -241,6 +246,8 @@ void                rtwn_scan_to(void *);
 void           rtwn_pci_next_scan(void *);
 void           rtwn_cancel_scan(void *);
 void           rtwn_wait_async(void *);
+void           rtwn_poll_c2h_events(struct rtwn_pci_softc *);
+void           rtwn_tx_report(struct rtwn_pci_softc *, uint8_t *, int);
 
 /* Aliases. */
 #define        rtwn_bb_write   rtwn_pci_write_4
@@ -337,6 +344,9 @@ rtwn_pci_attach(struct device *parent, s
                }
        }
 
+       sc->amrr.amrr_min_success_threshold = 1;
+       sc->amrr.amrr_max_success_threshold = 15;
+
        /* Attach the bus-agnostic driver. */
        sc->sc_sc.sc_ops.cookie = sc;
        sc->sc_sc.sc_ops.write_1 = rtwn_pci_write_1;
@@ -989,6 +999,9 @@ rtwn_tx(void *cookie, struct mbuf *m, st
                    SM(R92C_TXDW1_RAID, raid) |
                    R92C_TXDW1_AGGBK);
 
+               /* Request TX status report for AMRR. */
+               txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT);
+
                if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
                        txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
                            R92C_TXDW4_HWRTSEN);
@@ -1001,13 +1014,17 @@ rtwn_tx(void *cookie, struct mbuf *m, st
                                    R92C_TXDW4_HWRTSEN);
                        }
                }
-               /* Send RTS at OFDM24. */
-               txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 8));
+
+               if (ic->ic_curmode == IEEE80211_MODE_11B)
+                       txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 0));
+               else
+                       txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 3));
                txd->txdw5 |= htole32(SM(R92C_TXDW5_RTSRATE_FBLIMIT, 0xf));
-               /* Send data at OFDM54. */
-               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11));
-               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FBLIMIT, 0x1f));
 
+               /* Use AMMR rate for data. */
+               txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE);
+               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ni->ni_txrate));
+               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FBLIMIT, 0x1f));
        } else {
                txd->txdw1 |= htole32(
                    SM(R92C_TXDW1_MACID, 0) |
@@ -1128,6 +1145,8 @@ rtwn_tx_done(struct rtwn_pci_softc *sc, 
 
                sc->sc_sc.sc_tx_timer = 0;
                tx_ring->queued--;
+
+               rtwn_poll_c2h_events(sc);
        }
 
        if (tx_ring->queued < (RTWN_TX_LIST_COUNT - 1))
@@ -1149,7 +1168,8 @@ rtwn_alloc_buffers(void *cookie)
 int
 rtwn_pci_init(void *cookie)
 {
-       /* nothing to do */
+       struct rtwn_pci_softc *sc = cookie;
+       ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
        return (0);
 }
 
@@ -1661,6 +1681,12 @@ void
 rtwn_calib_to(void *arg)
 {
        struct rtwn_pci_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_sc.sc_ic;
+       int s;
+
+       s = splnet();
+       ieee80211_amrr_choose(&sc->amrr, ic->ic_bss, &sc->amn);
+       splx(s);
 
        rtwn_calib(&sc->sc_sc);
 }
@@ -1711,4 +1737,60 @@ void
 rtwn_wait_async(void *cookie)
 {
        /* nothing to do */
+}
+
+void
+rtwn_tx_report(struct rtwn_pci_softc *sc, uint8_t *buf, int len)
+{
+       struct r92c_c2h_tx_rpt *rpt = (struct r92c_c2h_tx_rpt *)buf;
+       int packets, tries, tx_ok, drop, expire, over;
+
+       if (len != sizeof(*rpt))
+               return;
+
+       packets = MS(rpt->rptb6, R92C_RPTB6_RPT_PKT_NUM);
+       tries = MS(rpt->rptb0, R92C_RPTB0_RETRY_CNT);
+       tx_ok = (rpt->rptb7 & R92C_RPTB7_PKT_OK);
+       drop = (rpt->rptb6 & R92C_RPTB6_PKT_DROP);
+       expire = (rpt->rptb6 & R92C_RPTB6_LIFE_EXPIRE);
+       over =(rpt->rptb6 & R92C_RPTB6_RETRY_OVER);
+
+       if (packets > 0) {
+               if (tx_ok)
+                       sc->amn.amn_txcnt += packets;
+               if (tries > 1 || drop || expire || over)
+                       sc->amn.amn_retrycnt++;
+       }
+}
+
+void
+rtwn_poll_c2h_events(struct rtwn_pci_softc *sc)
+{
+       const uint16_t off = R92C_C2HEVT_MSG + sizeof(struct r92c_c2h_evt);
+       uint8_t buf[R92C_C2H_MSG_MAX_LEN];
+       uint8_t id, len, status;
+       int i;
+
+       /* Read current status. */
+       status = rtwn_pci_read_1(sc, R92C_C2HEVT_CLEAR);
+       if (status == R92C_C2HEVT_HOST_CLOSE)
+               return; /* nothing to do */
+
+       if (status == R92C_C2HEVT_FW_CLOSE) {
+               len = rtwn_pci_read_1(sc, R92C_C2HEVT_MSG);
+               id = MS(len, R92C_C2H_EVTB0_ID);
+               len = MS(len, R92C_C2H_EVTB0_LEN);
+
+               if (id == R92C_C2HEVT_TX_REPORT && len <= sizeof(buf)) {
+                       memset(buf, 0, sizeof(buf));
+                       for (i = 0; i < len; i++)
+                               buf[i] = rtwn_pci_read_1(sc, off + i);
+                       rtwn_tx_report(sc, buf, len);
+               } else
+                       DPRINTF(("unhandled C2H event %d (%d bytes)\n",
+                           id, len));
+       }
+
+       /* Prepare for next event. */
+       rtwn_pci_write_1(sc, R92C_C2HEVT_CLEAR, R92C_C2HEVT_HOST_CLOSE);
 }

Reply via email to