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;