13fdfc330SAdrian Chadd /*- 23fdfc330SAdrian Chadd * Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org> 33fdfc330SAdrian Chadd * All rights reserved. 43fdfc330SAdrian Chadd * 53fdfc330SAdrian Chadd * Redistribution and use in source and binary forms, with or without 63fdfc330SAdrian Chadd * modification, are permitted provided that the following conditions 73fdfc330SAdrian Chadd * are met: 83fdfc330SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 93fdfc330SAdrian Chadd * notice, this list of conditions and the following disclaimer, 103fdfc330SAdrian Chadd * without modification. 113fdfc330SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 123fdfc330SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 133fdfc330SAdrian Chadd * redistribution must be conditioned upon including a substantially 143fdfc330SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 153fdfc330SAdrian Chadd * 163fdfc330SAdrian Chadd * NO WARRANTY 173fdfc330SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 183fdfc330SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 193fdfc330SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 203fdfc330SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 213fdfc330SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 223fdfc330SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 233fdfc330SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 243fdfc330SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 253fdfc330SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 263fdfc330SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 273fdfc330SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 283fdfc330SAdrian Chadd */ 293fdfc330SAdrian Chadd 303fdfc330SAdrian Chadd #include <sys/cdefs.h> 313fdfc330SAdrian Chadd __FBSDID("$FreeBSD$"); 323fdfc330SAdrian Chadd 333fdfc330SAdrian Chadd /* 343fdfc330SAdrian Chadd * Driver for the Atheros Wireless LAN controller. 353fdfc330SAdrian Chadd * 363fdfc330SAdrian Chadd * This software is derived from work of Atsushi Onoe; his contribution 373fdfc330SAdrian Chadd * is greatly appreciated. 383fdfc330SAdrian Chadd */ 393fdfc330SAdrian Chadd 403fdfc330SAdrian Chadd #include "opt_inet.h" 413fdfc330SAdrian Chadd #include "opt_ath.h" 423fdfc330SAdrian Chadd /* 433fdfc330SAdrian Chadd * This is needed for register operations which are performed 443fdfc330SAdrian Chadd * by the driver - eg, calls to ath_hal_gettsf32(). 453fdfc330SAdrian Chadd * 463fdfc330SAdrian Chadd * It's also required for any AH_DEBUG checks in here, eg the 473fdfc330SAdrian Chadd * module dependencies. 483fdfc330SAdrian Chadd */ 493fdfc330SAdrian Chadd #include "opt_ah.h" 503fdfc330SAdrian Chadd #include "opt_wlan.h" 513fdfc330SAdrian Chadd 523fdfc330SAdrian Chadd #include <sys/param.h> 533fdfc330SAdrian Chadd #include <sys/systm.h> 543fdfc330SAdrian Chadd #include <sys/sysctl.h> 553fdfc330SAdrian Chadd #include <sys/mbuf.h> 563fdfc330SAdrian Chadd #include <sys/malloc.h> 573fdfc330SAdrian Chadd #include <sys/lock.h> 583fdfc330SAdrian Chadd #include <sys/mutex.h> 593fdfc330SAdrian Chadd #include <sys/kernel.h> 603fdfc330SAdrian Chadd #include <sys/socket.h> 613fdfc330SAdrian Chadd #include <sys/sockio.h> 623fdfc330SAdrian Chadd #include <sys/errno.h> 633fdfc330SAdrian Chadd #include <sys/callout.h> 643fdfc330SAdrian Chadd #include <sys/bus.h> 653fdfc330SAdrian Chadd #include <sys/endian.h> 663fdfc330SAdrian Chadd #include <sys/kthread.h> 673fdfc330SAdrian Chadd #include <sys/taskqueue.h> 683fdfc330SAdrian Chadd #include <sys/priv.h> 693fdfc330SAdrian Chadd #include <sys/module.h> 703fdfc330SAdrian Chadd #include <sys/ktr.h> 713fdfc330SAdrian Chadd #include <sys/smp.h> /* for mp_ncpus */ 723fdfc330SAdrian Chadd 733fdfc330SAdrian Chadd #include <machine/bus.h> 743fdfc330SAdrian Chadd 753fdfc330SAdrian Chadd #include <net/if.h> 763fdfc330SAdrian Chadd #include <net/if_dl.h> 773fdfc330SAdrian Chadd #include <net/if_media.h> 783fdfc330SAdrian Chadd #include <net/if_types.h> 793fdfc330SAdrian Chadd #include <net/if_arp.h> 803fdfc330SAdrian Chadd #include <net/ethernet.h> 813fdfc330SAdrian Chadd #include <net/if_llc.h> 823fdfc330SAdrian Chadd 833fdfc330SAdrian Chadd #include <net80211/ieee80211_var.h> 843fdfc330SAdrian Chadd #include <net80211/ieee80211_regdomain.h> 853fdfc330SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG 863fdfc330SAdrian Chadd #include <net80211/ieee80211_superg.h> 873fdfc330SAdrian Chadd #endif 883fdfc330SAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA 893fdfc330SAdrian Chadd #include <net80211/ieee80211_tdma.h> 903fdfc330SAdrian Chadd #endif 913fdfc330SAdrian Chadd 923fdfc330SAdrian Chadd #include <net/bpf.h> 933fdfc330SAdrian Chadd 943fdfc330SAdrian Chadd #ifdef INET 953fdfc330SAdrian Chadd #include <netinet/in.h> 963fdfc330SAdrian Chadd #include <netinet/if_ether.h> 973fdfc330SAdrian Chadd #endif 983fdfc330SAdrian Chadd 993fdfc330SAdrian Chadd #include <dev/ath/if_athvar.h> 1003fdfc330SAdrian Chadd #include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 1013fdfc330SAdrian Chadd #include <dev/ath/ath_hal/ah_diagcodes.h> 1023fdfc330SAdrian Chadd 1033fdfc330SAdrian Chadd #include <dev/ath/if_ath_debug.h> 1043fdfc330SAdrian Chadd #include <dev/ath/if_ath_misc.h> 1053fdfc330SAdrian Chadd #include <dev/ath/if_ath_tsf.h> 1063fdfc330SAdrian Chadd #include <dev/ath/if_ath_tx.h> 1073fdfc330SAdrian Chadd #include <dev/ath/if_ath_sysctl.h> 1083fdfc330SAdrian Chadd #include <dev/ath/if_ath_led.h> 1093fdfc330SAdrian Chadd #include <dev/ath/if_ath_keycache.h> 1103fdfc330SAdrian Chadd #include <dev/ath/if_ath_rx.h> 1113fdfc330SAdrian Chadd #include <dev/ath/if_ath_beacon.h> 1123fdfc330SAdrian Chadd #include <dev/ath/if_athdfs.h> 1133fdfc330SAdrian Chadd 1143fdfc330SAdrian Chadd #ifdef ATH_TX99_DIAG 1153fdfc330SAdrian Chadd #include <dev/ath/ath_tx99/ath_tx99.h> 1163fdfc330SAdrian Chadd #endif 1173fdfc330SAdrian Chadd 1183fdfc330SAdrian Chadd #include <dev/ath/if_ath_tx_edma.h> 1193fdfc330SAdrian Chadd 120b69b0dccSAdrian Chadd #ifdef ATH_DEBUG_ALQ 121b69b0dccSAdrian Chadd #include <dev/ath/if_ath_alq.h> 122b69b0dccSAdrian Chadd #endif 123b69b0dccSAdrian Chadd 1243fdfc330SAdrian Chadd /* 1253fdfc330SAdrian Chadd * some general macros 1263fdfc330SAdrian Chadd */ 1273fdfc330SAdrian Chadd #define INCR(_l, _sz) (_l) ++; (_l) &= ((_sz) - 1) 1283fdfc330SAdrian Chadd #define DECR(_l, _sz) (_l) --; (_l) &= ((_sz) - 1) 1293fdfc330SAdrian Chadd 130ba3fd9d8SAdrian Chadd /* 131ba3fd9d8SAdrian Chadd * XXX doesn't belong here, and should be tunable 132ba3fd9d8SAdrian Chadd */ 133ba3fd9d8SAdrian Chadd #define ATH_TXSTATUS_RING_SIZE 512 134ba3fd9d8SAdrian Chadd 1353fdfc330SAdrian Chadd MALLOC_DECLARE(M_ATHDEV); 1363fdfc330SAdrian Chadd 137ae3815fdSAdrian Chadd static void ath_edma_tx_processq(struct ath_softc *sc, int dosched); 138ae3815fdSAdrian Chadd 1394aa8818bSAdrian Chadd static void 1404aa8818bSAdrian Chadd ath_edma_tx_fifo_fill(struct ath_softc *sc, struct ath_txq *txq) 1414aa8818bSAdrian Chadd { 1424aa8818bSAdrian Chadd struct ath_buf *bf; 143d40c846aSAdrian Chadd int i = 0; 1444aa8818bSAdrian Chadd 145375307d4SAdrian Chadd ATH_TX_LOCK_ASSERT(sc); 1464aa8818bSAdrian Chadd 1474aa8818bSAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called\n", __func__); 1484aa8818bSAdrian Chadd 1494aa8818bSAdrian Chadd TAILQ_FOREACH(bf, &txq->axq_q, bf_list) { 1504aa8818bSAdrian Chadd if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) 1514aa8818bSAdrian Chadd break; 1524aa8818bSAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); 153d40c846aSAdrian Chadd #ifdef ATH_DEBUG 154d40c846aSAdrian Chadd if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 155d40c846aSAdrian Chadd ath_printtxbuf(sc, bf, txq->axq_qnum, i, 0); 156b69b0dccSAdrian Chadd #endif/* ATH_DEBUG */ 157b69b0dccSAdrian Chadd #ifdef ATH_DEBUG_ALQ 158b69b0dccSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) 159e3f06688SAdrian Chadd ath_tx_alq_post(sc, bf); 160b69b0dccSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 1614aa8818bSAdrian Chadd txq->axq_fifo_depth++; 162d40c846aSAdrian Chadd i++; 1634aa8818bSAdrian Chadd } 164d40c846aSAdrian Chadd if (i > 0) 1654aa8818bSAdrian Chadd ath_hal_txstart(sc->sc_ah, txq->axq_qnum); 1664aa8818bSAdrian Chadd } 1674aa8818bSAdrian Chadd 168746bab5bSAdrian Chadd /* 169746bab5bSAdrian Chadd * Re-initialise the DMA FIFO with the current contents of 1703ae723d4SAdrian Chadd * said TXQ. 171746bab5bSAdrian Chadd * 172746bab5bSAdrian Chadd * This should only be called as part of the chip reset path, as it 173746bab5bSAdrian Chadd * assumes the FIFO is currently empty. 174746bab5bSAdrian Chadd */ 175746bab5bSAdrian Chadd static void 176746bab5bSAdrian Chadd ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq) 177746bab5bSAdrian Chadd { 178746bab5bSAdrian Chadd 179ae3815fdSAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: called: txq=%p, qnum=%d\n", 180746bab5bSAdrian Chadd __func__, 181746bab5bSAdrian Chadd txq, 182746bab5bSAdrian Chadd txq->axq_qnum); 1834aa8818bSAdrian Chadd 184375307d4SAdrian Chadd ATH_TX_LOCK_ASSERT(sc); 1854aa8818bSAdrian Chadd ath_edma_tx_fifo_fill(sc, txq); 1864aa8818bSAdrian Chadd 187746bab5bSAdrian Chadd } 188746bab5bSAdrian Chadd 189746bab5bSAdrian Chadd /* 1903ae723d4SAdrian Chadd * Hand off this frame to a hardware queue. 1913ae723d4SAdrian Chadd * 1923ae723d4SAdrian Chadd * Things are a bit hairy in the EDMA world. The TX FIFO is only 1933ae723d4SAdrian Chadd * 8 entries deep, so we need to keep track of exactly what we've 1943ae723d4SAdrian Chadd * pushed into the FIFO and what's just sitting in the TX queue, 1953ae723d4SAdrian Chadd * waiting to go out. 1963ae723d4SAdrian Chadd * 1973ae723d4SAdrian Chadd * So this is split into two halves - frames get appended to the 1983ae723d4SAdrian Chadd * TXQ; then a scheduler is called to push some frames into the 1993ae723d4SAdrian Chadd * actual TX FIFO. 2003ae723d4SAdrian Chadd */ 2013ae723d4SAdrian Chadd static void 2023ae723d4SAdrian Chadd ath_edma_xmit_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, 2033ae723d4SAdrian Chadd struct ath_buf *bf) 2043ae723d4SAdrian Chadd { 2053ae723d4SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 2063ae723d4SAdrian Chadd 207375307d4SAdrian Chadd ATH_TX_LOCK_ASSERT(sc); 2083ae723d4SAdrian Chadd 2093ae723d4SAdrian Chadd KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, 2103ae723d4SAdrian Chadd ("%s: busy status 0x%x", __func__, bf->bf_flags)); 2113ae723d4SAdrian Chadd 2123ae723d4SAdrian Chadd /* 2133ae723d4SAdrian Chadd * XXX TODO: write a hard-coded check to ensure that 2143ae723d4SAdrian Chadd * the queue id in the TX descriptor matches txq->axq_qnum. 2153ae723d4SAdrian Chadd */ 2163ae723d4SAdrian Chadd 2173ae723d4SAdrian Chadd /* Update aggr stats */ 2183ae723d4SAdrian Chadd if (bf->bf_state.bfs_aggr) 2193ae723d4SAdrian Chadd txq->axq_aggr_depth++; 2203ae723d4SAdrian Chadd 2213ae723d4SAdrian Chadd /* Push and update frame stats */ 2223ae723d4SAdrian Chadd ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 2233ae723d4SAdrian Chadd 224d40c846aSAdrian Chadd /* Only schedule to the FIFO if there's space */ 225d40c846aSAdrian Chadd if (txq->axq_fifo_depth < HAL_TXFIFO_DEPTH) { 2264aa8818bSAdrian Chadd #ifdef ATH_DEBUG 2274aa8818bSAdrian Chadd if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 2284aa8818bSAdrian Chadd ath_printtxbuf(sc, bf, txq->axq_qnum, 0, 0); 2294aa8818bSAdrian Chadd #endif /* ATH_DEBUG */ 230b69b0dccSAdrian Chadd #ifdef ATH_DEBUG_ALQ 231b69b0dccSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) 232e3f06688SAdrian Chadd ath_tx_alq_post(sc, bf); 233b69b0dccSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 2343ae723d4SAdrian Chadd ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); 2354aa8818bSAdrian Chadd txq->axq_fifo_depth++; 2363ae723d4SAdrian Chadd ath_hal_txstart(ah, txq->axq_qnum); 2373ae723d4SAdrian Chadd } 2383ae723d4SAdrian Chadd } 2393ae723d4SAdrian Chadd 2403ae723d4SAdrian Chadd /* 2413ae723d4SAdrian Chadd * Hand off this frame to a multicast software queue. 2423ae723d4SAdrian Chadd * 243e3f06688SAdrian Chadd * The EDMA TX CABQ will get a list of chained frames, chained 244e3f06688SAdrian Chadd * together using the next pointer. The single head of that 245e3f06688SAdrian Chadd * particular queue is pushed to the hardware CABQ. 2463ae723d4SAdrian Chadd */ 2473ae723d4SAdrian Chadd static void 2483ae723d4SAdrian Chadd ath_edma_xmit_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq, 2493ae723d4SAdrian Chadd struct ath_buf *bf) 2503ae723d4SAdrian Chadd { 2513ae723d4SAdrian Chadd 252375307d4SAdrian Chadd ATH_TX_LOCK_ASSERT(sc); 2533ae723d4SAdrian Chadd KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, 2543ae723d4SAdrian Chadd ("%s: busy status 0x%x", __func__, bf->bf_flags)); 2553ae723d4SAdrian Chadd 2563ae723d4SAdrian Chadd /* 2573ae723d4SAdrian Chadd * XXX this is mostly duplicated in ath_tx_handoff_mcast(). 2583ae723d4SAdrian Chadd */ 2593ae723d4SAdrian Chadd if (ATH_TXQ_FIRST(txq) != NULL) { 2603ae723d4SAdrian Chadd struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s); 2613ae723d4SAdrian Chadd struct ieee80211_frame *wh; 2623ae723d4SAdrian Chadd 2633ae723d4SAdrian Chadd /* mark previous frame */ 2643ae723d4SAdrian Chadd wh = mtod(bf_last->bf_m, struct ieee80211_frame *); 2653ae723d4SAdrian Chadd wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 2663ae723d4SAdrian Chadd 2673ae723d4SAdrian Chadd /* sync descriptor to memory */ 2683ae723d4SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap, 2693ae723d4SAdrian Chadd BUS_DMASYNC_PREWRITE); 270*9cda8c80SAdrian Chadd 271*9cda8c80SAdrian Chadd /* link descriptor */ 272*9cda8c80SAdrian Chadd *txq->axq_link = bf->bf_daddr; 2733ae723d4SAdrian Chadd } 2743ae723d4SAdrian Chadd 275e3f06688SAdrian Chadd #ifdef ATH_DEBUG_ALQ 276e3f06688SAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) 277e3f06688SAdrian Chadd ath_tx_alq_post(sc, bf); 278e3f06688SAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 279e3f06688SAdrian Chadd 2803ae723d4SAdrian Chadd ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 281e3f06688SAdrian Chadd ath_hal_gettxdesclinkptr(sc->sc_ah, bf->bf_lastds, &txq->axq_link); 2823ae723d4SAdrian Chadd } 2833ae723d4SAdrian Chadd 2843ae723d4SAdrian Chadd /* 285746bab5bSAdrian Chadd * Handoff this frame to the hardware. 286746bab5bSAdrian Chadd * 287746bab5bSAdrian Chadd * For the multicast queue, this will treat it as a software queue 288746bab5bSAdrian Chadd * and append it to the list, after updating the MORE_DATA flag 289746bab5bSAdrian Chadd * in the previous frame. The cabq processing code will ensure 290746bab5bSAdrian Chadd * that the queue contents gets transferred over. 291746bab5bSAdrian Chadd * 292746bab5bSAdrian Chadd * For the hardware queues, this will queue a frame to the queue 293746bab5bSAdrian Chadd * like before, then populate the FIFO from that. Since the 294746bab5bSAdrian Chadd * EDMA hardware has 8 FIFO slots per TXQ, this ensures that 295746bab5bSAdrian Chadd * frames such as management frames don't get prematurely dropped. 296746bab5bSAdrian Chadd * 297746bab5bSAdrian Chadd * This does imply that a similar flush-hwq-to-fifoq method will 298746bab5bSAdrian Chadd * need to be called from the processq function, before the 299746bab5bSAdrian Chadd * per-node software scheduler is called. 300746bab5bSAdrian Chadd */ 301746bab5bSAdrian Chadd static void 302746bab5bSAdrian Chadd ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, 303746bab5bSAdrian Chadd struct ath_buf *bf) 304746bab5bSAdrian Chadd { 305746bab5bSAdrian Chadd 306375307d4SAdrian Chadd ATH_TX_LOCK_ASSERT(sc); 3073ae723d4SAdrian Chadd 3084aa8818bSAdrian Chadd DPRINTF(sc, ATH_DEBUG_XMIT_DESC, 3094aa8818bSAdrian Chadd "%s: called; bf=%p, txq=%p, qnum=%d\n", 310746bab5bSAdrian Chadd __func__, 311746bab5bSAdrian Chadd bf, 312746bab5bSAdrian Chadd txq, 313746bab5bSAdrian Chadd txq->axq_qnum); 314746bab5bSAdrian Chadd 3153ae723d4SAdrian Chadd if (txq->axq_qnum == ATH_TXQ_SWQ) 3163ae723d4SAdrian Chadd ath_edma_xmit_handoff_mcast(sc, txq, bf); 3173ae723d4SAdrian Chadd else 3183ae723d4SAdrian Chadd ath_edma_xmit_handoff_hw(sc, txq, bf); 3193ae723d4SAdrian Chadd 3203ae723d4SAdrian Chadd #if 0 321746bab5bSAdrian Chadd /* 322746bab5bSAdrian Chadd * XXX For now this is a placeholder; free the buffer 323746bab5bSAdrian Chadd * and inform the stack that the TX failed. 324746bab5bSAdrian Chadd */ 325746bab5bSAdrian Chadd ath_tx_default_comp(sc, bf, 1); 3263ae723d4SAdrian Chadd #endif 327746bab5bSAdrian Chadd } 328746bab5bSAdrian Chadd 3293fdfc330SAdrian Chadd static int 33079607afeSAdrian Chadd ath_edma_setup_txfifo(struct ath_softc *sc, int qnum) 33179607afeSAdrian Chadd { 33279607afeSAdrian Chadd struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; 33379607afeSAdrian Chadd 33479607afeSAdrian Chadd te->m_fifo = malloc(sizeof(struct ath_buf *) * HAL_TXFIFO_DEPTH, 33579607afeSAdrian Chadd M_ATHDEV, 33679607afeSAdrian Chadd M_NOWAIT | M_ZERO); 33779607afeSAdrian Chadd if (te->m_fifo == NULL) { 33879607afeSAdrian Chadd device_printf(sc->sc_dev, "%s: malloc failed\n", 33979607afeSAdrian Chadd __func__); 34079607afeSAdrian Chadd return (-ENOMEM); 34179607afeSAdrian Chadd } 34279607afeSAdrian Chadd 34379607afeSAdrian Chadd /* 34479607afeSAdrian Chadd * Set initial "empty" state. 34579607afeSAdrian Chadd */ 34679607afeSAdrian Chadd te->m_fifo_head = te->m_fifo_tail = te->m_fifo_depth = 0; 34779607afeSAdrian Chadd 34879607afeSAdrian Chadd return (0); 34979607afeSAdrian Chadd } 35079607afeSAdrian Chadd 35179607afeSAdrian Chadd static int 35279607afeSAdrian Chadd ath_edma_free_txfifo(struct ath_softc *sc, int qnum) 35379607afeSAdrian Chadd { 35479607afeSAdrian Chadd struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; 35579607afeSAdrian Chadd 35679607afeSAdrian Chadd /* XXX TODO: actually deref the ath_buf entries? */ 35779607afeSAdrian Chadd free(te->m_fifo, M_ATHDEV); 35879607afeSAdrian Chadd return (0); 35979607afeSAdrian Chadd } 36079607afeSAdrian Chadd 36179607afeSAdrian Chadd static int 3623fdfc330SAdrian Chadd ath_edma_dma_txsetup(struct ath_softc *sc) 3633fdfc330SAdrian Chadd { 364ba3fd9d8SAdrian Chadd int error; 36579607afeSAdrian Chadd int i; 3663fdfc330SAdrian Chadd 367ba3fd9d8SAdrian Chadd error = ath_descdma_alloc_desc(sc, &sc->sc_txsdma, 368ba3fd9d8SAdrian Chadd NULL, "txcomp", sc->sc_tx_statuslen, ATH_TXSTATUS_RING_SIZE); 369ba3fd9d8SAdrian Chadd if (error != 0) 370ba3fd9d8SAdrian Chadd return (error); 371ba3fd9d8SAdrian Chadd 372ba3fd9d8SAdrian Chadd ath_hal_setuptxstatusring(sc->sc_ah, 373ba3fd9d8SAdrian Chadd (void *) sc->sc_txsdma.dd_desc, 374ba3fd9d8SAdrian Chadd sc->sc_txsdma.dd_desc_paddr, 375ba3fd9d8SAdrian Chadd ATH_TXSTATUS_RING_SIZE); 376ba3fd9d8SAdrian Chadd 37779607afeSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 37879607afeSAdrian Chadd ath_edma_setup_txfifo(sc, i); 37979607afeSAdrian Chadd } 38079607afeSAdrian Chadd 3813fdfc330SAdrian Chadd return (0); 3823fdfc330SAdrian Chadd } 3833fdfc330SAdrian Chadd 3843fdfc330SAdrian Chadd static int 3853fdfc330SAdrian Chadd ath_edma_dma_txteardown(struct ath_softc *sc) 3863fdfc330SAdrian Chadd { 38779607afeSAdrian Chadd int i; 38879607afeSAdrian Chadd 38979607afeSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 39079607afeSAdrian Chadd ath_edma_free_txfifo(sc, i); 39179607afeSAdrian Chadd } 3923fdfc330SAdrian Chadd 393ba3fd9d8SAdrian Chadd ath_descdma_cleanup(sc, &sc->sc_txsdma, NULL); 3943fdfc330SAdrian Chadd return (0); 3953fdfc330SAdrian Chadd } 3963fdfc330SAdrian Chadd 3973ae723d4SAdrian Chadd /* 398788e6aa9SAdrian Chadd * Drain all TXQs, potentially after completing the existing completed 399788e6aa9SAdrian Chadd * frames. 4003ae723d4SAdrian Chadd */ 401788e6aa9SAdrian Chadd static void 402788e6aa9SAdrian Chadd ath_edma_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) 403f8418db5SAdrian Chadd { 4044aa8818bSAdrian Chadd struct ifnet *ifp = sc->sc_ifp; 4054aa8818bSAdrian Chadd int i; 406f8418db5SAdrian Chadd 407ae3815fdSAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); 4084aa8818bSAdrian Chadd 4094aa8818bSAdrian Chadd (void) ath_stoptxdma(sc); 4104aa8818bSAdrian Chadd 4114aa8818bSAdrian Chadd /* 4124aa8818bSAdrian Chadd * If reset type is noloss, the TX FIFO needs to be serviced 4134aa8818bSAdrian Chadd * and those frames need to be handled. 4144aa8818bSAdrian Chadd * 4154aa8818bSAdrian Chadd * Otherwise, just toss everything in each TX queue. 4164aa8818bSAdrian Chadd */ 417ae3815fdSAdrian Chadd if (reset_type == ATH_RESET_NOLOSS) { 418ae3815fdSAdrian Chadd ath_edma_tx_processq(sc, 0); 419ae3815fdSAdrian Chadd } else { 4204aa8818bSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 4214aa8818bSAdrian Chadd if (ATH_TXQ_SETUP(sc, i)) 4224aa8818bSAdrian Chadd ath_tx_draintxq(sc, &sc->sc_txq[i]); 4234aa8818bSAdrian Chadd } 424ae3815fdSAdrian Chadd } 425ae3815fdSAdrian Chadd 426ae3815fdSAdrian Chadd /* XXX dump out the TX completion FIFO contents */ 427ae3815fdSAdrian Chadd 428ae3815fdSAdrian Chadd /* XXX dump out the frames */ 4294aa8818bSAdrian Chadd 4304aa8818bSAdrian Chadd IF_LOCK(&ifp->if_snd); 4314aa8818bSAdrian Chadd ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 4324aa8818bSAdrian Chadd IF_UNLOCK(&ifp->if_snd); 4334aa8818bSAdrian Chadd sc->sc_wd_timer = 0; 434f8418db5SAdrian Chadd } 435f8418db5SAdrian Chadd 4363ae723d4SAdrian Chadd /* 437ae3815fdSAdrian Chadd * TX completion tasklet. 4383ae723d4SAdrian Chadd */ 439ae3815fdSAdrian Chadd 440f8418db5SAdrian Chadd static void 441f8418db5SAdrian Chadd ath_edma_tx_proc(void *arg, int npending) 442f8418db5SAdrian Chadd { 443f8418db5SAdrian Chadd struct ath_softc *sc = (struct ath_softc *) arg; 444ae3815fdSAdrian Chadd 445ae3815fdSAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called, npending=%d\n", 446ae3815fdSAdrian Chadd __func__, npending); 447ae3815fdSAdrian Chadd ath_edma_tx_processq(sc, 1); 448ae3815fdSAdrian Chadd } 449ae3815fdSAdrian Chadd 450ae3815fdSAdrian Chadd /* 451ae3815fdSAdrian Chadd * Process the TX status queue. 452ae3815fdSAdrian Chadd */ 453ae3815fdSAdrian Chadd static void 454ae3815fdSAdrian Chadd ath_edma_tx_processq(struct ath_softc *sc, int dosched) 455ae3815fdSAdrian Chadd { 4563ae723d4SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 4573ae723d4SAdrian Chadd HAL_STATUS status; 4583ae723d4SAdrian Chadd struct ath_tx_status ts; 4593ae723d4SAdrian Chadd struct ath_txq *txq; 4604aa8818bSAdrian Chadd struct ath_buf *bf; 4614aa8818bSAdrian Chadd struct ieee80211_node *ni; 462208be709SAdrian Chadd int nacked = 0; 463d40c846aSAdrian Chadd int idx; 464d40c846aSAdrian Chadd 465d40c846aSAdrian Chadd #ifdef ATH_DEBUG 466d40c846aSAdrian Chadd /* XXX */ 467d40c846aSAdrian Chadd uint32_t txstatus[32]; 468d40c846aSAdrian Chadd #endif 469f8418db5SAdrian Chadd 470d40c846aSAdrian Chadd for (idx = 0; ; idx++) { 4714aa8818bSAdrian Chadd bzero(&ts, sizeof(ts)); 4724aa8818bSAdrian Chadd 4733ae723d4SAdrian Chadd ATH_TXSTATUS_LOCK(sc); 4744c5038c7SAdrian Chadd #ifdef ATH_DEBUG 475d40c846aSAdrian Chadd ath_hal_gettxrawtxdesc(ah, txstatus); 4764c5038c7SAdrian Chadd #endif 477ae3815fdSAdrian Chadd status = ath_hal_txprocdesc(ah, NULL, (void *) &ts); 4783ae723d4SAdrian Chadd ATH_TXSTATUS_UNLOCK(sc); 4793ae723d4SAdrian Chadd 480d40c846aSAdrian Chadd #ifdef ATH_DEBUG 481d40c846aSAdrian Chadd if (sc->sc_debug & ATH_DEBUG_TX_PROC) 482d40c846aSAdrian Chadd ath_printtxstatbuf(sc, NULL, txstatus, ts.ts_queue_id, 483d40c846aSAdrian Chadd idx, (status == HAL_OK)); 484d40c846aSAdrian Chadd #endif 485d40c846aSAdrian Chadd 4864aa8818bSAdrian Chadd if (status == HAL_EINPROGRESS) 4873ae723d4SAdrian Chadd break; 4883ae723d4SAdrian Chadd 4893ae723d4SAdrian Chadd /* 4904aa8818bSAdrian Chadd * If there is an error with this descriptor, continue 4914aa8818bSAdrian Chadd * processing. 4924aa8818bSAdrian Chadd * 4934aa8818bSAdrian Chadd * XXX TBD: log some statistics? 4944aa8818bSAdrian Chadd */ 4954aa8818bSAdrian Chadd if (status == HAL_EIO) { 4964aa8818bSAdrian Chadd device_printf(sc->sc_dev, "%s: invalid TX status?\n", 4974aa8818bSAdrian Chadd __func__); 4984aa8818bSAdrian Chadd continue; 4994aa8818bSAdrian Chadd } 5004aa8818bSAdrian Chadd 501b69b0dccSAdrian Chadd #ifdef ATH_DEBUG_ALQ 502b69b0dccSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS)) 503b69b0dccSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS, 504b69b0dccSAdrian Chadd sc->sc_tx_statuslen, 505b69b0dccSAdrian Chadd (char *) txstatus); 506b69b0dccSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 507b69b0dccSAdrian Chadd 5084aa8818bSAdrian Chadd /* 5093ae723d4SAdrian Chadd * At this point we have a valid status descriptor. 5103ae723d4SAdrian Chadd * The QID and descriptor ID (which currently isn't set) 5113ae723d4SAdrian Chadd * is part of the status. 5123ae723d4SAdrian Chadd * 5133ae723d4SAdrian Chadd * We then assume that the descriptor in question is the 5143ae723d4SAdrian Chadd * -head- of the given QID. Eventually we should verify 5153ae723d4SAdrian Chadd * this by using the descriptor ID. 5163ae723d4SAdrian Chadd */ 5174aa8818bSAdrian Chadd 5184aa8818bSAdrian Chadd /* 5194aa8818bSAdrian Chadd * The beacon queue is not currently a "real" queue. 5204aa8818bSAdrian Chadd * Frames aren't pushed onto it and the lock isn't setup. 5214aa8818bSAdrian Chadd * So skip it for now; the beacon handling code will 5224aa8818bSAdrian Chadd * free and alloc more beacon buffers as appropriate. 5234aa8818bSAdrian Chadd */ 5244aa8818bSAdrian Chadd if (ts.ts_queue_id == sc->sc_bhalq) 5254aa8818bSAdrian Chadd continue; 5263ae723d4SAdrian Chadd 5273ae723d4SAdrian Chadd txq = &sc->sc_txq[ts.ts_queue_id]; 5284aa8818bSAdrian Chadd 529375307d4SAdrian Chadd ATH_TX_LOCK(sc); 5304aa8818bSAdrian Chadd bf = TAILQ_FIRST(&txq->axq_q); 5314aa8818bSAdrian Chadd 5324aa8818bSAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: qcuid=%d, bf=%p\n", 5334aa8818bSAdrian Chadd __func__, 5344aa8818bSAdrian Chadd ts.ts_queue_id, bf); 5354aa8818bSAdrian Chadd 536d40c846aSAdrian Chadd /* XXX TODO: actually output debugging info about this */ 537d40c846aSAdrian Chadd 5384aa8818bSAdrian Chadd #if 0 5394aa8818bSAdrian Chadd /* XXX assert the buffer/descriptor matches the status descid */ 5404aa8818bSAdrian Chadd if (ts.ts_desc_id != bf->bf_descid) { 5414aa8818bSAdrian Chadd device_printf(sc->sc_dev, 5424aa8818bSAdrian Chadd "%s: mismatched descid (qid=%d, tsdescid=%d, " 5434aa8818bSAdrian Chadd "bfdescid=%d\n", 5444aa8818bSAdrian Chadd __func__, 5454aa8818bSAdrian Chadd ts.ts_queue_id, 5464aa8818bSAdrian Chadd ts.ts_desc_id, 5474aa8818bSAdrian Chadd bf->bf_descid); 5483ae723d4SAdrian Chadd } 5494aa8818bSAdrian Chadd #endif 5504aa8818bSAdrian Chadd 5514aa8818bSAdrian Chadd /* This removes the buffer and decrements the queue depth */ 5524aa8818bSAdrian Chadd ATH_TXQ_REMOVE(txq, bf, bf_list); 5534aa8818bSAdrian Chadd if (bf->bf_state.bfs_aggr) 5544aa8818bSAdrian Chadd txq->axq_aggr_depth--; 5554aa8818bSAdrian Chadd txq->axq_fifo_depth --; 5564aa8818bSAdrian Chadd /* XXX assert FIFO depth >= 0 */ 557375307d4SAdrian Chadd ATH_TX_UNLOCK(sc); 5584aa8818bSAdrian Chadd 5594aa8818bSAdrian Chadd /* 5604aa8818bSAdrian Chadd * First we need to make sure ts_rate is valid. 5614aa8818bSAdrian Chadd * 5624aa8818bSAdrian Chadd * Pre-EDMA chips pass the whole TX descriptor to 5634aa8818bSAdrian Chadd * the proctxdesc function which will then fill out 5644aa8818bSAdrian Chadd * ts_rate based on the ts_finaltsi (final TX index) 5654aa8818bSAdrian Chadd * in the TX descriptor. However the TX completion 5664aa8818bSAdrian Chadd * FIFO doesn't have this information. So here we 5674aa8818bSAdrian Chadd * do a separate HAL call to populate that information. 5683345c65bSAdrian Chadd * 5693345c65bSAdrian Chadd * The same problem exists with ts_longretry. 5703345c65bSAdrian Chadd * The FreeBSD HAL corrects ts_longretry in the HAL layer; 5713345c65bSAdrian Chadd * the AR9380 HAL currently doesn't. So until the HAL 5723345c65bSAdrian Chadd * is imported and this can be added, we correct for it 5733345c65bSAdrian Chadd * here. 5744aa8818bSAdrian Chadd */ 5754aa8818bSAdrian Chadd /* XXX TODO */ 5764aa8818bSAdrian Chadd /* XXX faked for now. Ew. */ 5774aa8818bSAdrian Chadd if (ts.ts_finaltsi < 4) { 5784aa8818bSAdrian Chadd ts.ts_rate = 5794aa8818bSAdrian Chadd bf->bf_state.bfs_rc[ts.ts_finaltsi].ratecode; 5803345c65bSAdrian Chadd switch (ts.ts_finaltsi) { 5813345c65bSAdrian Chadd case 3: ts.ts_longretry += 5823345c65bSAdrian Chadd bf->bf_state.bfs_rc[2].tries; 5833345c65bSAdrian Chadd case 2: ts.ts_longretry += 5843345c65bSAdrian Chadd bf->bf_state.bfs_rc[1].tries; 5853345c65bSAdrian Chadd case 1: ts.ts_longretry += 5863345c65bSAdrian Chadd bf->bf_state.bfs_rc[0].tries; 5873345c65bSAdrian Chadd } 5884aa8818bSAdrian Chadd } else { 5894aa8818bSAdrian Chadd device_printf(sc->sc_dev, "%s: finaltsi=%d\n", 5904aa8818bSAdrian Chadd __func__, 5914aa8818bSAdrian Chadd ts.ts_finaltsi); 5924aa8818bSAdrian Chadd ts.ts_rate = bf->bf_state.bfs_rc[0].ratecode; 5934aa8818bSAdrian Chadd } 5944aa8818bSAdrian Chadd 5954aa8818bSAdrian Chadd /* 5964aa8818bSAdrian Chadd * XXX This is terrible. 5974aa8818bSAdrian Chadd * 5984aa8818bSAdrian Chadd * Right now, some code uses the TX status that is 5994aa8818bSAdrian Chadd * passed in here, but the completion handlers in the 6004aa8818bSAdrian Chadd * software TX path also use bf_status.ds_txstat. 6014aa8818bSAdrian Chadd * Ew. That should all go away. 6024aa8818bSAdrian Chadd * 6034aa8818bSAdrian Chadd * XXX It's also possible the rate control completion 6044aa8818bSAdrian Chadd * routine is called twice. 6054aa8818bSAdrian Chadd */ 6064aa8818bSAdrian Chadd memcpy(&bf->bf_status, &ts, sizeof(ts)); 6074aa8818bSAdrian Chadd 6084aa8818bSAdrian Chadd ni = bf->bf_node; 6094aa8818bSAdrian Chadd 6104aa8818bSAdrian Chadd /* Update RSSI */ 6114aa8818bSAdrian Chadd /* XXX duplicate from ath_tx_processq */ 6124aa8818bSAdrian Chadd if (ni != NULL && ts.ts_status == 0 && 6134aa8818bSAdrian Chadd ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { 6144aa8818bSAdrian Chadd nacked++; 6154aa8818bSAdrian Chadd sc->sc_stats.ast_tx_rssi = ts.ts_rssi; 6164aa8818bSAdrian Chadd ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, 6174aa8818bSAdrian Chadd ts.ts_rssi); 6184aa8818bSAdrian Chadd } 6194aa8818bSAdrian Chadd 6204aa8818bSAdrian Chadd /* Handle frame completion and rate control update */ 6214aa8818bSAdrian Chadd ath_tx_process_buf_completion(sc, txq, &ts, bf); 6224aa8818bSAdrian Chadd 6234aa8818bSAdrian Chadd /* bf is invalid at this point */ 6244aa8818bSAdrian Chadd 6254aa8818bSAdrian Chadd /* 6264aa8818bSAdrian Chadd * Now that there's space in the FIFO, let's push some 6274aa8818bSAdrian Chadd * more frames into it. 6284aa8818bSAdrian Chadd * 6294aa8818bSAdrian Chadd * Unfortunately for now, the txq has FIFO and non-FIFO 6304aa8818bSAdrian Chadd * frames in the same linked list, so there's no way 6314aa8818bSAdrian Chadd * to quickly/easily populate frames without walking 6324aa8818bSAdrian Chadd * the queue and skipping 'axq_fifo_depth' frames. 6334aa8818bSAdrian Chadd * 6344aa8818bSAdrian Chadd * So for now, let's only repopulate the FIFO once it 6354aa8818bSAdrian Chadd * is empty. It's sucky for performance but it's enough 6364aa8818bSAdrian Chadd * to begin validating that things are somewhat 6374aa8818bSAdrian Chadd * working. 6384aa8818bSAdrian Chadd */ 639375307d4SAdrian Chadd ATH_TX_LOCK(sc); 640ae3815fdSAdrian Chadd if (dosched && txq->axq_fifo_depth == 0) { 6414aa8818bSAdrian Chadd ath_edma_tx_fifo_fill(sc, txq); 6424aa8818bSAdrian Chadd } 643375307d4SAdrian Chadd ATH_TX_UNLOCK(sc); 6444aa8818bSAdrian Chadd } 6454aa8818bSAdrian Chadd 6464aa8818bSAdrian Chadd sc->sc_wd_timer = 0; 6474aa8818bSAdrian Chadd 648c19a2a1aSAdrian Chadd if (idx > 0) { 649c19a2a1aSAdrian Chadd IF_LOCK(&sc->sc_ifp->if_snd); 650c19a2a1aSAdrian Chadd sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 651c19a2a1aSAdrian Chadd IF_UNLOCK(&sc->sc_ifp->if_snd); 652c19a2a1aSAdrian Chadd } 653c19a2a1aSAdrian Chadd 6544aa8818bSAdrian Chadd /* Kick software scheduler */ 6554aa8818bSAdrian Chadd /* 6564aa8818bSAdrian Chadd * XXX It's inefficient to do this if the FIFO queue is full, 6574aa8818bSAdrian Chadd * but there's no easy way right now to only populate 6584aa8818bSAdrian Chadd * the txq task for _one_ TXQ. This should be fixed. 6594aa8818bSAdrian Chadd */ 660ae3815fdSAdrian Chadd if (dosched) 66121bca442SAdrian Chadd ath_tx_swq_kick(sc); 662f8418db5SAdrian Chadd } 663f8418db5SAdrian Chadd 664f8418db5SAdrian Chadd static void 665f8418db5SAdrian Chadd ath_edma_attach_comp_func(struct ath_softc *sc) 666f8418db5SAdrian Chadd { 667f8418db5SAdrian Chadd 668f8418db5SAdrian Chadd TASK_INIT(&sc->sc_txtask, 0, ath_edma_tx_proc, sc); 669f8418db5SAdrian Chadd } 670f8418db5SAdrian Chadd 6713fdfc330SAdrian Chadd void 6723fdfc330SAdrian Chadd ath_xmit_setup_edma(struct ath_softc *sc) 6733fdfc330SAdrian Chadd { 6743fdfc330SAdrian Chadd 6753fdfc330SAdrian Chadd /* Fetch EDMA field and buffer sizes */ 6763fdfc330SAdrian Chadd (void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen); 6773fdfc330SAdrian Chadd (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); 6783fdfc330SAdrian Chadd (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); 6793fdfc330SAdrian Chadd 6803fdfc330SAdrian Chadd device_printf(sc->sc_dev, "TX descriptor length: %d\n", 6813fdfc330SAdrian Chadd sc->sc_tx_desclen); 6823fdfc330SAdrian Chadd device_printf(sc->sc_dev, "TX status length: %d\n", 6833fdfc330SAdrian Chadd sc->sc_tx_statuslen); 6843fdfc330SAdrian Chadd device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", 6853fdfc330SAdrian Chadd sc->sc_tx_nmaps); 6863fdfc330SAdrian Chadd 6873fdfc330SAdrian Chadd sc->sc_tx.xmit_setup = ath_edma_dma_txsetup; 6883fdfc330SAdrian Chadd sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown; 689f8418db5SAdrian Chadd sc->sc_tx.xmit_attach_comp_func = ath_edma_attach_comp_func; 690746bab5bSAdrian Chadd 691746bab5bSAdrian Chadd sc->sc_tx.xmit_dma_restart = ath_edma_dma_restart; 692746bab5bSAdrian Chadd sc->sc_tx.xmit_handoff = ath_edma_xmit_handoff; 693788e6aa9SAdrian Chadd sc->sc_tx.xmit_drain = ath_edma_tx_drain; 6943fdfc330SAdrian Chadd } 695