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); }