summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ath/if_ath_tx_edma.c201
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
OpenPOWER on IntegriCloud