diff options
-rw-r--r-- | sys/dev/ath/if_ath_tx_edma.c | 201 |
1 files changed, 195 insertions, 6 deletions
diff --git a/sys/dev/ath/if_ath_tx_edma.c b/sys/dev/ath/if_ath_tx_edma.c index 5ae7b8c..da147ac 100644 --- a/sys/dev/ath/if_ath_tx_edma.c +++ b/sys/dev/ath/if_ath_tx_edma.c @@ -130,6 +130,24 @@ __FBSDID("$FreeBSD$"); MALLOC_DECLARE(M_ATHDEV); +static void +ath_edma_tx_fifo_fill(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_buf *bf; + + ATH_TXQ_LOCK_ASSERT(txq); + + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called\n", __func__); + + TAILQ_FOREACH(bf, &txq->axq_q, bf_list) { + if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) + break; + ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); + txq->axq_fifo_depth++; + } + ath_hal_txstart(sc->sc_ah, txq->axq_qnum); +} + /* * Re-initialise the DMA FIFO with the current contents of * said TXQ. @@ -149,6 +167,10 @@ ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq) __func__, txq, txq->axq_qnum); + + ATH_TXQ_LOCK_ASSERT(txq); + ath_edma_tx_fifo_fill(sc, txq); + } /* @@ -186,9 +208,15 @@ ath_edma_xmit_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, /* Push and update frame stats */ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) + ath_printtxbuf(sc, bf, txq->axq_qnum, 0, 0); +#endif /* ATH_DEBUG */ + /* Only schedule to the FIFO if there's space */ if (txq->axq_fifo_depth < HAL_TXFIFO_DEPTH) { ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); + txq->axq_fifo_depth++; ath_hal_txstart(ah, txq->axq_qnum); } } @@ -259,7 +287,8 @@ ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, ATH_TXQ_LOCK_ASSERT(txq); - device_printf(sc->sc_dev, "%s: called; bf=%p, txq=%p, qnum=%d\n", + DPRINTF(sc, ATH_DEBUG_XMIT_DESC, + "%s: called; bf=%p, txq=%p, qnum=%d\n", __func__, bf, txq, @@ -355,8 +384,34 @@ ath_edma_dma_txteardown(struct ath_softc *sc) static void ath_edma_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { + struct ifnet *ifp = sc->sc_ifp; + int i; device_printf(sc->sc_dev, "%s: called\n", __func__); + + (void) ath_stoptxdma(sc); + + /* + * If reset type is noloss, the TX FIFO needs to be serviced + * and those frames need to be handled. + * + * Otherwise, just toss everything in each TX queue. + */ + + /* XXX dump out the TX completion FIFO contents */ + + /* XXX dump out the frames */ + + /* XXX for now, just drain */ + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_draintxq(sc, &sc->sc_txq[i]); + } + + IF_LOCK(&ifp->if_snd); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + IF_UNLOCK(&ifp->if_snd); + sc->sc_wd_timer = 0; } /* @@ -370,19 +425,36 @@ ath_edma_tx_proc(void *arg, int npending) HAL_STATUS status; struct ath_tx_status ts; struct ath_txq *txq; + struct ath_buf *bf; + struct ieee80211_node *ni; + int nacked; - device_printf(sc->sc_dev, "%s: called, npending=%d\n", + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called, npending=%d\n", __func__, npending); for (;;) { + bzero(&ts, sizeof(ts)); + ATH_TXSTATUS_LOCK(sc); status = ath_hal_txprocdesc(ah, NULL, (void *) &ts); ATH_TXSTATUS_UNLOCK(sc); - if (status != HAL_OK) + if (status == HAL_EINPROGRESS) break; /* + * If there is an error with this descriptor, continue + * processing. + * + * XXX TBD: log some statistics? + */ + if (status == HAL_EIO) { + device_printf(sc->sc_dev, "%s: invalid TX status?\n", + __func__); + continue; + } + + /* * At this point we have a valid status descriptor. * The QID and descriptor ID (which currently isn't set) * is part of the status. @@ -391,12 +463,129 @@ ath_edma_tx_proc(void *arg, int npending) * -head- of the given QID. Eventually we should verify * this by using the descriptor ID. */ - device_printf(sc->sc_dev, "%s: qcuid=%d\n", - __func__, - ts.ts_queue_id); + + /* + * The beacon queue is not currently a "real" queue. + * Frames aren't pushed onto it and the lock isn't setup. + * So skip it for now; the beacon handling code will + * free and alloc more beacon buffers as appropriate. + */ + if (ts.ts_queue_id == sc->sc_bhalq) + continue; txq = &sc->sc_txq[ts.ts_queue_id]; + + ATH_TXQ_LOCK(txq); + bf = TAILQ_FIRST(&txq->axq_q); + + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: qcuid=%d, bf=%p\n", + __func__, + ts.ts_queue_id, bf); + +#if 0 + /* XXX assert the buffer/descriptor matches the status descid */ + if (ts.ts_desc_id != bf->bf_descid) { + device_printf(sc->sc_dev, + "%s: mismatched descid (qid=%d, tsdescid=%d, " + "bfdescid=%d\n", + __func__, + ts.ts_queue_id, + ts.ts_desc_id, + bf->bf_descid); + } +#endif + + /* This removes the buffer and decrements the queue depth */ + ATH_TXQ_REMOVE(txq, bf, bf_list); + if (bf->bf_state.bfs_aggr) + txq->axq_aggr_depth--; + txq->axq_fifo_depth --; + /* XXX assert FIFO depth >= 0 */ + ATH_TXQ_UNLOCK(txq); + + /* + * First we need to make sure ts_rate is valid. + * + * Pre-EDMA chips pass the whole TX descriptor to + * the proctxdesc function which will then fill out + * ts_rate based on the ts_finaltsi (final TX index) + * in the TX descriptor. However the TX completion + * FIFO doesn't have this information. So here we + * do a separate HAL call to populate that information. + */ + + /* XXX TODO */ + /* XXX faked for now. Ew. */ + if (ts.ts_finaltsi < 4) { + ts.ts_rate = + bf->bf_state.bfs_rc[ts.ts_finaltsi].ratecode; + } else { + device_printf(sc->sc_dev, "%s: finaltsi=%d\n", + __func__, + ts.ts_finaltsi); + ts.ts_rate = bf->bf_state.bfs_rc[0].ratecode; + } + + /* + * XXX This is terrible. + * + * Right now, some code uses the TX status that is + * passed in here, but the completion handlers in the + * software TX path also use bf_status.ds_txstat. + * Ew. That should all go away. + * + * XXX It's also possible the rate control completion + * routine is called twice. + */ + memcpy(&bf->bf_status, &ts, sizeof(ts)); + + ni = bf->bf_node; + + /* Update RSSI */ + /* XXX duplicate from ath_tx_processq */ + if (ni != NULL && ts.ts_status == 0 && + ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { + nacked++; + sc->sc_stats.ast_tx_rssi = ts.ts_rssi; + ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, + ts.ts_rssi); + } + + /* Handle frame completion and rate control update */ + ath_tx_process_buf_completion(sc, txq, &ts, bf); + + /* bf is invalid at this point */ + + /* + * Now that there's space in the FIFO, let's push some + * more frames into it. + * + * Unfortunately for now, the txq has FIFO and non-FIFO + * frames in the same linked list, so there's no way + * to quickly/easily populate frames without walking + * the queue and skipping 'axq_fifo_depth' frames. + * + * So for now, let's only repopulate the FIFO once it + * is empty. It's sucky for performance but it's enough + * to begin validating that things are somewhat + * working. + */ + ATH_TXQ_LOCK(txq); + if (txq->axq_fifo_depth == 0) { + ath_edma_tx_fifo_fill(sc, txq); + } + ATH_TXQ_UNLOCK(txq); } + + sc->sc_wd_timer = 0; + + /* Kick software scheduler */ + /* + * XXX It's inefficient to do this if the FIFO queue is full, + * but there's no easy way right now to only populate + * the txq task for _one_ TXQ. This should be fixed. + */ + taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask); } static void |