This prepares run(4) for adding basic .11n support.  In order to use
MiRA for rate selection, we need per-frame status reports.  In run(4),
this is done by setting a packet ID on each frame we transmit, and
reading the results from the tx status fifo shortly after (but not
immediately after) transmission completes.  To keep things simple, slots
on the tx ring are not freed up until the tx status has been read,
instead of when transmission completes.  In my testing this hasn't been
a problem.

We'll most likely want to change how retries and rate fallback work so
that the rate selection algorithms work better, but in this diff I'm
leaving things as they are.

This shouldn't make any noticeable difference, but if the fifo doesn't
work reliably on some models it may cause the interface to stall.
I've tested on this device:

run0 at uhub0 port 1 configuration 1 interface 0 "Ralink 802.11 n WLAN" rev 
2.00/1.01 addr 2
run0: MAC/BBP RT3070 (rev 0x0201), RF RT3020 (MIMO 1T1R), address .....

but I don't have any others.  In particular I think RT539x devices
might behave differently, so I'd like to hear whether it works there.

diff --git a/sys/dev/ic/rt2860reg.h b/sys/dev/ic/rt2860reg.h
index c3bddf6f098..ae1c1496e65 100644
--- a/sys/dev/ic/rt2860reg.h
+++ b/sys/dev/ic/rt2860reg.h
@@ -870,6 +870,8 @@ struct rt2860_txwi {
 #define RT2860_TX_TXOP_PIFS    1
 #define RT2860_TX_TXOP_SIFS    2
 #define RT2860_TX_TXOP_BACKOFF 3
+#define RT5390_TX_TXBFK                (1 << 5)
+#define RT5390_TX_TXRPT                (1 << 6)
 
        uint16_t        phy;
 #define RT2860_PHY_MODE                0xc000
diff --git a/sys/dev/usb/if_run.c b/sys/dev/usb/if_run.c
index fc6ba52f271..b608aa9af6a 100644
--- a/sys/dev/usb/if_run.c
+++ b/sys/dev/usb/if_run.c
@@ -372,8 +372,8 @@ void                run_set_key_cb(struct run_softc *, void 
*);
 void           run_delete_key(struct ieee80211com *, struct ieee80211_node *,
                    struct ieee80211_key *);
 void           run_delete_key_cb(struct run_softc *, void *);
-void           run_calibrate_to(void *);
-void           run_calibrate_cb(struct run_softc *, void *);
+void           run_tx_status_to(void *);
+void           run_tx_status_cb(struct run_softc *, void *);
 void           run_newassoc(struct ieee80211com *, struct ieee80211_node *,
                    int);
 void           run_rx_frame(struct run_softc *, uint8_t *, int);
@@ -571,7 +571,7 @@ run_attach(struct device *parent, struct device *self, void 
*aux)
 
        usb_init_task(&sc->sc_task, run_task, sc, USB_TASK_TYPE_GENERIC);
        timeout_set(&sc->scan_to, run_next_scan, sc);
-       timeout_set(&sc->calib_to, run_calibrate_to, sc);
+       timeout_set(&sc->tx_sta_to, run_tx_status_to, sc);
 
        sc->amrr.amrr_min_success_threshold =  1;
        sc->amrr.amrr_max_success_threshold = 10;
@@ -688,8 +688,8 @@ run_detach(struct device *self, int flags)
 
        if (timeout_initialized(&sc->scan_to))
                timeout_del(&sc->scan_to);
-       if (timeout_initialized(&sc->calib_to))
-               timeout_del(&sc->calib_to);
+       if (timeout_initialized(&sc->tx_sta_to))
+               timeout_del(&sc->tx_sta_to);
 
        /* wait for all queued asynchronous commands to complete */
        usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
@@ -772,7 +772,7 @@ run_alloc_tx_ring(struct run_softc *sc, int qid)
        if (sc->mac_ver == 0x5592)
                txwisize += sizeof(uint32_t);
 
-       txq->cur = txq->queued = 0;
+       txq->cur = txq->cur_sta = txq->queued = 0;
 
        error = usbd_open_pipe(sc->sc_iface, txq->pipe_no, 0, &txq->pipeh);
        if (error != 0)
@@ -1784,7 +1784,7 @@ run_newstate_cb(struct run_softc *sc, void *arg)
        struct ieee80211com *ic = &sc->sc_ic;
        enum ieee80211_state ostate;
        struct ieee80211_node *ni;
-       uint32_t tmp, sta[3];
+       uint32_t tmp;
        uint8_t wcid;
        int s;
 
@@ -1841,13 +1841,6 @@ run_newstate_cb(struct run_softc *sc, void *arg)
                }
                if (ic->ic_opmode != IEEE80211_M_MONITOR) {
                        run_enable_tsf_sync(sc);
-
-                       /* clear statistic registers used by AMRR */
-                       run_read_region_1(sc, RT2860_TX_STA_CNT0,
-                           (uint8_t *)sta, sizeof sta);
-                       /* start calibration timer */
-                       if (!usbd_is_dying(sc->sc_udev))
-                               timeout_add_sec(&sc->calib_to, 1);
                }
 
                /* turn link LED on */
@@ -2052,52 +2045,6 @@ run_delete_key_cb(struct run_softc *sc, void *arg)
        }
 }
 
-void
-run_calibrate_to(void *arg)
-{
-       /* do it in a process context */
-       run_do_async(arg, run_calibrate_cb, NULL, 0);
-       /* next timeout will be rescheduled in the calibration task */
-}
-
-/* ARGSUSED */
-void
-run_calibrate_cb(struct run_softc *sc, void *arg)
-{
-       struct ifnet *ifp = &sc->sc_ic.ic_if;
-       uint32_t sta[3];
-       int s, error;
-
-       /* read statistic counters (clear on read) and update AMRR state */
-       error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta,
-           sizeof sta);
-       if (error != 0)
-               goto skip;
-
-       DPRINTF(("retrycnt=%d txcnt=%d failcnt=%d\n",
-           letoh32(sta[1]) >> 16, letoh32(sta[1]) & 0xffff,
-           letoh32(sta[0]) & 0xffff));
-
-       s = splnet();
-       /* count failed TX as errors */
-       ifp->if_oerrors += letoh32(sta[0]) & 0xffff;
-
-       sc->amn.amn_retrycnt =
-           (letoh32(sta[0]) & 0xffff) +        /* failed TX count */
-           (letoh32(sta[1]) >> 16);            /* TX retransmission count */
-
-       sc->amn.amn_txcnt =
-           sc->amn.amn_retrycnt +
-           (letoh32(sta[1]) & 0xffff);         /* successful TX count */
-
-       ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
-       splx(s);
-
-skip:
-       if (!usbd_is_dying(sc->sc_udev))
-               timeout_add_sec(&sc->calib_to, 1);
-}
-
 void
 run_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
 {
@@ -2110,7 +2057,7 @@ run_newassoc(struct ieee80211com *ic, struct 
ieee80211_node *ni, int isnew)
        DPRINTF(("new assoc isnew=%d addr=%s\n",
            isnew, ether_sprintf(ni->ni_macaddr)));
 
-       ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
+       ieee80211_amrr_node_init(&sc->amrr, &rn->amn);
        /* start at lowest available bit-rate, AMRR will raise */
        ni->ni_txrate = 0;
 
@@ -2359,6 +2306,84 @@ skip:    /* setup a new transfer */
        (void)usbd_transfer(data->xfer);
 }
 
+void
+run_tx_status_to(void *arg)
+{
+       run_do_async(arg, run_tx_status_cb, NULL, 0);
+}
+
+void
+run_tx_status_cb(struct run_softc *sc, void *arg)
+{
+       struct ieee80211_node *ni;
+       struct run_node *rn;
+       struct run_tx_ring *txq;
+       struct run_tx_data *data;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t tx_sta;
+       int s, error, qid, tx_mcs;
+       int done = 0;
+       int attempts = 0;
+
+       s = splnet();
+       ni = sc->sc_ic.ic_bss;
+       rn = (struct run_node *)ni;
+       while (sc->sta_queued > 0) {
+               tx_sta = 0;
+               error = run_read_region_1(sc, RT2860_TX_STAT_FIFO,
+                   (uint8_t *)&tx_sta, sizeof(tx_sta));
+               if (error != 0) {
+                       printf("tx stat fifo error %d\n",error);
+                       break;
+               }
+
+               if ((tx_sta & RT2860_TXQ_VLD) == 0) {
+                       /* don't spin here forever */
+                       if (attempts++ > 100)
+                               break;
+                       continue;
+               }
+
+               tx_mcs = (tx_sta >> RT2860_TXQ_MCS_SHIFT) & RT2860_PHY_MCS;
+               qid = (tx_sta >> RT2860_TXQ_PID_SHIFT) & 0x03;
+
+               txq = &sc->txq[qid];
+               data = &txq->data[txq->cur_sta];
+               txq->cur_sta++;
+               if (txq->cur_sta == RUN_TX_RING_COUNT)
+                       txq->cur_sta = 0;
+
+               if (((tx_sta >> RT2860_TXQ_MCS_SHIFT) & RT2860_PHY_MODE) ==
+                   RT2860_PHY_CCK) {
+                       tx_mcs &= ~RT2860_PHY_SHPRE;
+               }
+
+               if (tx_sta & RT2860_TXQ_ACKREQ) {
+                       if (tx_sta & RT2860_TXQ_OK)
+                               rn->amn.amn_txcnt++;
+                       if (tx_mcs < data->mcs) {
+                               rn->amn.amn_retrycnt +=
+                                   (data->mcs - tx_mcs);
+                       }
+               }
+
+               done++;
+               txq->queued--;
+               sc->qfullmsk &= ~(1 << qid);
+               sc->sta_queued--;
+       }
+
+       if (sc->sta_queued > 0)
+               timeout_add_msec(&sc->tx_sta_to, 2);
+
+       if (done > 0)
+               ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &rn->amn);
+
+       ifq_clr_oactive(&ifp->if_snd);
+       run_start(ifp);
+       splx(s);
+}
+
 void
 run_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 {
@@ -2369,8 +2394,6 @@ run_txeof(struct usbd_xfer *xfer, void *priv, usbd_status 
status)
        int s;
 
        s = splnet();
-       txq->queued--;
-       sc->qfullmsk &= ~(1 << data->qid);
 
        if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
                DPRINTF(("TX status=%d\n", status));
@@ -2381,9 +2404,18 @@ run_txeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
                return;
        }
 
+       sc->sta_queued++;
+       /*
+        * the tx status fifo is pretty small, so poll immediately if
+        * we have multiple entries outstanding
+        */
+       if (sc->sta_queued > 3) {
+               timeout_del(&sc->tx_sta_to);
+               run_tx_status_to(sc);
+       } else if (!timeout_initialized(&sc->tx_sta_to))
+               timeout_add_msec(&sc->tx_sta_to, 2);
+
        sc->sc_tx_timer = 0;
-       ifq_clr_oactive(&ifp->if_snd);
-       run_start(ifp);
        splx(s);
 }
 
@@ -2397,9 +2429,9 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct 
ieee80211_node *ni)
        struct run_tx_data *data;
        struct rt2870_txd *txd;
        struct rt2860_txwi *txwi;
-       uint16_t qos, dur;
+       uint16_t qos, dur, pid;
        uint16_t txwisize;
-       uint8_t type, mcs, tid, qid;
+       uint8_t type, tid, qid;
        int error, hasqos, ridx, ctl_ridx, xferlen;
 
        wh = mtod(m, struct ieee80211_frame *);
@@ -2431,8 +2463,7 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct 
ieee80211_node *ni)
                ctl_ridx = rn->ctl_ridx[ni->ni_txrate];
        }
 
-       /* get MCS code from rate index */
-       mcs = rt2860_rates[ridx].mcs;
+       pid = (((ring->cur % 3) + 1) << 2) | (qid & 3);
 
        txwisize = sizeof(struct rt2860_txwi);
        if (sc->mac_ver == 0x5592)
@@ -2452,17 +2483,24 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct 
ieee80211_node *ni)
        txwi->xflags = hasqos ? 0 : RT2860_TX_NSEQ;
        txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ?
            RUN_AID2WCID(ni->ni_associd) : 0xff;
-       txwi->len = htole16(m->m_pkthdr.len);
+       txwi->len = htole16(m->m_pkthdr.len | (pid << RT2860_TX_PID_SHIFT));
+       txwi->txop = RT2860_TX_TXOP_BACKOFF;
+       if (sc->mac_ver >= 0x5390)
+               txwi->txop |= RT5390_TX_TXRPT;
+       data->wcid = txwi->wcid;
        if (rt2860_rates[ridx].phy == IEEE80211_T_DS) {
-               txwi->phy = htole16(RT2860_PHY_CCK);
+               txwi->phy = htole16(RT2860_PHY_CCK |
+                   rt2860_rates[ridx].mcs);
                if (ridx != RT2860_RIDX_CCK1 &&
-                   (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
-                       mcs |= RT2860_PHY_SHPRE;
-       } else
-               txwi->phy = htole16(RT2860_PHY_OFDM);
-       txwi->phy |= htole16(mcs);
-
-       txwi->txop = RT2860_TX_TXOP_BACKOFF;
+                   (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) {
+                       txwi->phy |= htole16(RT2860_PHY_SHPRE);
+               }
+       } else {
+               txwi->phy = htole16(RT2860_PHY_OFDM |
+                   rt2860_rates[ridx].mcs);
+       }
+       data->mcs = rt2860_rates[ridx].mcs;
+       data->length = m->m_pkthdr.len;
 
        if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
            (!hasqos || (qos & IEEE80211_QOS_ACK_POLICY_MASK) !=
@@ -2485,7 +2523,7 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct 
ieee80211_node *ni)
                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;
-               if (mcs & RT2860_PHY_SHPRE)
+               if (rt2860_rates[ridx].mcs & RT2860_PHY_SHPRE)
                        tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
 
                mb.m_data = (caddr_t)tap;
@@ -4680,6 +4718,12 @@ run_init(struct ifnet *ifp)
        /* turn radio LED on */
        run_set_leds(sc, RT2860_LED_RADIO);
 
+       /* ensure TX status fifo is empty */
+       do {
+               error = run_read_region_1(sc, RT2860_TX_STAT_FIFO,
+                   (uint8_t *)&tmp, sizeof(tmp));
+       } while ((tmp & RT2860_TXQ_VLD) && (error == 0));
+
        for (i = 0; i < RUN_RX_RING_COUNT; i++) {
                struct run_rx_data *data = &sc->rxq.data[i];
 
@@ -4730,7 +4774,7 @@ run_stop(struct ifnet *ifp, int disable)
        ifq_clr_oactive(&ifp->if_snd);
 
        timeout_del(&sc->scan_to);
-       timeout_del(&sc->calib_to);
+       timeout_del(&sc->tx_sta_to);
 
        s = splusb();
        ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
diff --git a/sys/dev/usb/if_runvar.h b/sys/dev/usb/if_runvar.h
index aad304cee16..6b316cd0d70 100644
--- a/sys/dev/usb/if_runvar.h
+++ b/sys/dev/usb/if_runvar.h
@@ -79,7 +79,10 @@ struct run_tx_data {
        struct run_softc        *sc;
        struct usbd_xfer        *xfer;
        uint8_t                 *buf;
+       uint16_t                length;
        uint8_t                 qid;
+       uint8_t                 mcs;
+       uint8_t                 wcid;
 };
 
 struct run_rx_data {
@@ -92,6 +95,7 @@ struct run_tx_ring {
        struct run_tx_data      data[RUN_TX_RING_COUNT];
        struct usbd_pipe        *pipeh;
        int                     cur;
+       int                     cur_sta;
        int                     queued;
        uint8_t                 pipe_no;
 };
@@ -126,9 +130,10 @@ struct run_host_cmd_ring {
 };
 
 struct run_node {
-       struct ieee80211_node   ni;
-       uint8_t                 ridx[IEEE80211_RATE_MAXSIZE];
-       uint8_t                 ctl_ridx[IEEE80211_RATE_MAXSIZE];
+       struct ieee80211_node           ni;
+       uint8_t                         ridx[IEEE80211_RATE_MAXSIZE];
+       uint8_t                         ctl_ridx[IEEE80211_RATE_MAXSIZE];
+       struct ieee80211_amrr_node      amn;
 };
 
 struct run_softc {
@@ -182,16 +187,16 @@ struct run_softc {
        struct usb_task                 sc_task;
 
        struct ieee80211_amrr           amrr;
-       struct ieee80211_amrr_node      amn;
 
        struct timeout                  scan_to;
-       struct timeout                  calib_to;
+       struct timeout                  tx_sta_to;
 
        struct run_rx_ring              rxq;
        struct run_tx_ring              txq[4];
        struct run_host_cmd_ring        cmdq;
        uint8_t                         qfullmsk;
        int                             sc_tx_timer;
+       int                             sta_queued;
 
 #if NBPFILTER > 0
        caddr_t                         sc_drvbpf;

Reply via email to