17453645fSAndriy Voskoboinyk /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */
27453645fSAndriy Voskoboinyk
37453645fSAndriy Voskoboinyk /*-
47453645fSAndriy Voskoboinyk * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
57453645fSAndriy Voskoboinyk * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
67453645fSAndriy Voskoboinyk * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org>
77453645fSAndriy Voskoboinyk *
87453645fSAndriy Voskoboinyk * Permission to use, copy, modify, and distribute this software for any
97453645fSAndriy Voskoboinyk * purpose with or without fee is hereby granted, provided that the above
107453645fSAndriy Voskoboinyk * copyright notice and this permission notice appear in all copies.
117453645fSAndriy Voskoboinyk *
127453645fSAndriy Voskoboinyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
137453645fSAndriy Voskoboinyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
147453645fSAndriy Voskoboinyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
157453645fSAndriy Voskoboinyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
167453645fSAndriy Voskoboinyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
177453645fSAndriy Voskoboinyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
187453645fSAndriy Voskoboinyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
197453645fSAndriy Voskoboinyk */
207453645fSAndriy Voskoboinyk
217453645fSAndriy Voskoboinyk #include <sys/cdefs.h>
227453645fSAndriy Voskoboinyk #include "opt_wlan.h"
237453645fSAndriy Voskoboinyk
247453645fSAndriy Voskoboinyk #include <sys/param.h>
257453645fSAndriy Voskoboinyk #include <sys/lock.h>
267453645fSAndriy Voskoboinyk #include <sys/mutex.h>
277453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
287453645fSAndriy Voskoboinyk #include <sys/kernel.h>
297453645fSAndriy Voskoboinyk #include <sys/socket.h>
307453645fSAndriy Voskoboinyk #include <sys/systm.h>
317453645fSAndriy Voskoboinyk #include <sys/malloc.h>
327453645fSAndriy Voskoboinyk #include <sys/queue.h>
337453645fSAndriy Voskoboinyk #include <sys/taskqueue.h>
347453645fSAndriy Voskoboinyk #include <sys/bus.h>
357453645fSAndriy Voskoboinyk #include <sys/endian.h>
367453645fSAndriy Voskoboinyk
377453645fSAndriy Voskoboinyk #include <machine/bus.h>
387453645fSAndriy Voskoboinyk #include <machine/resource.h>
397453645fSAndriy Voskoboinyk #include <sys/rman.h>
407453645fSAndriy Voskoboinyk
417453645fSAndriy Voskoboinyk #include <net/if.h>
427453645fSAndriy Voskoboinyk #include <net/ethernet.h>
437453645fSAndriy Voskoboinyk #include <net/if_media.h>
447453645fSAndriy Voskoboinyk
457453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
467453645fSAndriy Voskoboinyk
477453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
487453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_rx.h>
51a14f9888SAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_task.h>
527453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_tx.h>
537453645fSAndriy Voskoboinyk
547453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_var.h>
557453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_rx.h>
567453645fSAndriy Voskoboinyk
577453645fSAndriy Voskoboinyk void
rtwn_pci_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nsegs,int error)587453645fSAndriy Voskoboinyk rtwn_pci_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs,
597453645fSAndriy Voskoboinyk int error)
607453645fSAndriy Voskoboinyk {
617453645fSAndriy Voskoboinyk
627453645fSAndriy Voskoboinyk if (error != 0)
637453645fSAndriy Voskoboinyk return;
647453645fSAndriy Voskoboinyk KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
657453645fSAndriy Voskoboinyk *(bus_addr_t *)arg = segs[0].ds_addr;
667453645fSAndriy Voskoboinyk }
677453645fSAndriy Voskoboinyk
687453645fSAndriy Voskoboinyk void
rtwn_pci_setup_rx_desc(struct rtwn_pci_softc * pc,struct rtwn_rx_stat_pci * desc,bus_addr_t addr,size_t len,int idx)69c5ad99fcSAndriy Voskoboinyk rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *pc,
70c5ad99fcSAndriy Voskoboinyk struct rtwn_rx_stat_pci *desc, bus_addr_t addr, size_t len, int idx)
717453645fSAndriy Voskoboinyk {
727453645fSAndriy Voskoboinyk
737453645fSAndriy Voskoboinyk memset(desc, 0, sizeof(*desc));
74c5ad99fcSAndriy Voskoboinyk desc->rxdw0 = htole32(SM(RTWN_RXDW0_PKTLEN, len) |
75c5ad99fcSAndriy Voskoboinyk ((idx == RTWN_PCI_RX_LIST_COUNT - 1) ? RTWN_RXDW0_EOR : 0));
767453645fSAndriy Voskoboinyk desc->rxbufaddr = htole32(addr);
777453645fSAndriy Voskoboinyk bus_space_barrier(pc->pc_st, pc->pc_sh, 0, pc->pc_mapsize,
787453645fSAndriy Voskoboinyk BUS_SPACE_BARRIER_WRITE);
79c5ad99fcSAndriy Voskoboinyk desc->rxdw0 |= htole32(RTWN_RXDW0_OWN);
807453645fSAndriy Voskoboinyk }
817453645fSAndriy Voskoboinyk
827453645fSAndriy Voskoboinyk static void
rtwn_pci_rx_frame(struct rtwn_pci_softc * pc)8317d5fbf2SAndriy Voskoboinyk rtwn_pci_rx_frame(struct rtwn_pci_softc *pc)
847453645fSAndriy Voskoboinyk {
8517d5fbf2SAndriy Voskoboinyk struct rtwn_softc *sc = &pc->pc_sc;
867453645fSAndriy Voskoboinyk struct rtwn_rx_ring *ring = &pc->rx_ring;
8717d5fbf2SAndriy Voskoboinyk struct rtwn_rx_stat_pci *rx_desc = &ring->desc[ring->cur];
8817d5fbf2SAndriy Voskoboinyk struct rtwn_rx_data *rx_data = &ring->rx_data[ring->cur];
897453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic;
907453645fSAndriy Voskoboinyk struct ieee80211_node *ni;
917453645fSAndriy Voskoboinyk uint32_t rxdw0;
927453645fSAndriy Voskoboinyk struct mbuf *m, *m1;
937453645fSAndriy Voskoboinyk int infosz, pktlen, shift, error;
947453645fSAndriy Voskoboinyk
957453645fSAndriy Voskoboinyk /* Dump Rx descriptor. */
967453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC,
977453645fSAndriy Voskoboinyk "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X, "
987453645fSAndriy Voskoboinyk "addr: %08X (64: %08X)\n",
997453645fSAndriy Voskoboinyk __func__, le32toh(rx_desc->rxdw0), le32toh(rx_desc->rxdw1),
1007453645fSAndriy Voskoboinyk le32toh(rx_desc->rxdw2), le32toh(rx_desc->rxdw3),
1017453645fSAndriy Voskoboinyk le32toh(rx_desc->rxdw4), le32toh(rx_desc->tsf_low),
1027453645fSAndriy Voskoboinyk le32toh(rx_desc->rxbufaddr), le32toh(rx_desc->rxbufaddr64));
1037453645fSAndriy Voskoboinyk
1047453645fSAndriy Voskoboinyk rxdw0 = le32toh(rx_desc->rxdw0);
105c5ad99fcSAndriy Voskoboinyk if (__predict_false(rxdw0 & (RTWN_RXDW0_CRCERR | RTWN_RXDW0_ICVERR))) {
1067453645fSAndriy Voskoboinyk /*
1077453645fSAndriy Voskoboinyk * This should not happen since we setup our Rx filter
1087453645fSAndriy Voskoboinyk * to not receive these frames.
1097453645fSAndriy Voskoboinyk */
1107453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
1117453645fSAndriy Voskoboinyk "%s: RX flags error (%s)\n", __func__,
112c5ad99fcSAndriy Voskoboinyk rxdw0 & RTWN_RXDW0_CRCERR ? "CRC" : "ICV");
1137453645fSAndriy Voskoboinyk goto fail;
1147453645fSAndriy Voskoboinyk }
1157453645fSAndriy Voskoboinyk
116c5ad99fcSAndriy Voskoboinyk pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN);
1177453645fSAndriy Voskoboinyk if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack) ||
118a14f9888SAndriy Voskoboinyk pktlen > MJUMPAGESIZE)) {
1197453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
1207453645fSAndriy Voskoboinyk "%s: frame is too short/long: %d\n", __func__, pktlen);
1217453645fSAndriy Voskoboinyk goto fail;
1227453645fSAndriy Voskoboinyk }
1237453645fSAndriy Voskoboinyk
124c5ad99fcSAndriy Voskoboinyk infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8;
125c5ad99fcSAndriy Voskoboinyk shift = MS(rxdw0, RTWN_RXDW0_SHIFT);
1267453645fSAndriy Voskoboinyk
127a14f9888SAndriy Voskoboinyk m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
1287453645fSAndriy Voskoboinyk if (__predict_false(m1 == NULL)) {
1297453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n",
1307453645fSAndriy Voskoboinyk __func__);
1317453645fSAndriy Voskoboinyk goto fail;
1327453645fSAndriy Voskoboinyk }
1337453645fSAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, rx_data->map, BUS_DMASYNC_POSTREAD);
1347453645fSAndriy Voskoboinyk bus_dmamap_unload(ring->data_dmat, rx_data->map);
1357453645fSAndriy Voskoboinyk
1367453645fSAndriy Voskoboinyk error = bus_dmamap_load(ring->data_dmat, rx_data->map, mtod(m1, void *),
137a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, rtwn_pci_dma_map_addr, &rx_data->paddr, 0);
1387453645fSAndriy Voskoboinyk if (error != 0) {
1397453645fSAndriy Voskoboinyk m_freem(m1);
1407453645fSAndriy Voskoboinyk
1417453645fSAndriy Voskoboinyk error = bus_dmamap_load(ring->data_dmat, rx_data->map,
142a14f9888SAndriy Voskoboinyk mtod(rx_data->m, void *), MJUMPAGESIZE,
143a14f9888SAndriy Voskoboinyk rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT);
1447453645fSAndriy Voskoboinyk if (error != 0)
1457453645fSAndriy Voskoboinyk panic("%s: could not load old RX mbuf",
1467453645fSAndriy Voskoboinyk device_get_name(sc->sc_dev));
1477453645fSAndriy Voskoboinyk
1487453645fSAndriy Voskoboinyk goto fail;
1497453645fSAndriy Voskoboinyk }
1507453645fSAndriy Voskoboinyk
1517453645fSAndriy Voskoboinyk /* Finalize mbuf. */
1527453645fSAndriy Voskoboinyk m = rx_data->m;
1537453645fSAndriy Voskoboinyk rx_data->m = m1;
1547453645fSAndriy Voskoboinyk m->m_pkthdr.len = m->m_len = pktlen + infosz + shift;
1557453645fSAndriy Voskoboinyk
15609606165SAndriy Voskoboinyk ni = rtwn_rx_common(sc, m, rx_desc);
1577453645fSAndriy Voskoboinyk
1587453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
15909606165SAndriy Voskoboinyk "%s: Rx frame len %d, infosz %d, shift %d\n",
16009606165SAndriy Voskoboinyk __func__, pktlen, infosz, shift);
1617453645fSAndriy Voskoboinyk
1627453645fSAndriy Voskoboinyk /* Send the frame to the 802.11 layer. */
1637453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc);
1647453645fSAndriy Voskoboinyk if (ni != NULL) {
16509606165SAndriy Voskoboinyk (void)ieee80211_input_mimo(ni, m);
1667453645fSAndriy Voskoboinyk /* Node is no longer needed. */
1677453645fSAndriy Voskoboinyk ieee80211_free_node(ni);
1687453645fSAndriy Voskoboinyk } else
16909606165SAndriy Voskoboinyk (void)ieee80211_input_mimo_all(ic, m);
1707453645fSAndriy Voskoboinyk
1717453645fSAndriy Voskoboinyk RTWN_LOCK(sc);
1727453645fSAndriy Voskoboinyk
1737453645fSAndriy Voskoboinyk return;
1747453645fSAndriy Voskoboinyk
1757453645fSAndriy Voskoboinyk fail:
1767453645fSAndriy Voskoboinyk counter_u64_add(ic->ic_ierrors, 1);
1777453645fSAndriy Voskoboinyk }
1787453645fSAndriy Voskoboinyk
17917d5fbf2SAndriy Voskoboinyk static int
rtwn_pci_rx_buf_copy(struct rtwn_pci_softc * pc)18017d5fbf2SAndriy Voskoboinyk rtwn_pci_rx_buf_copy(struct rtwn_pci_softc *pc)
18117d5fbf2SAndriy Voskoboinyk {
18217d5fbf2SAndriy Voskoboinyk struct rtwn_rx_ring *ring = &pc->rx_ring;
18317d5fbf2SAndriy Voskoboinyk struct rtwn_rx_stat_pci *rx_desc = &ring->desc[ring->cur];
18417d5fbf2SAndriy Voskoboinyk struct rtwn_rx_data *rx_data = &ring->rx_data[ring->cur];
18517d5fbf2SAndriy Voskoboinyk uint32_t rxdw0;
18617d5fbf2SAndriy Voskoboinyk int desc_size, pktlen;
18717d5fbf2SAndriy Voskoboinyk
18817d5fbf2SAndriy Voskoboinyk /*
18917d5fbf2SAndriy Voskoboinyk * NB: tx_report() / c2h_report() expects to see USB Rx
19017d5fbf2SAndriy Voskoboinyk * descriptor - same as for PCIe, but without rxbufaddr* fields.
19117d5fbf2SAndriy Voskoboinyk */
19217d5fbf2SAndriy Voskoboinyk desc_size = sizeof(struct rtwn_rx_stat_common);
1930fe1808cSAndriy Voskoboinyk KASSERT(sizeof(pc->pc_rx_buf) >= desc_size,
19417d5fbf2SAndriy Voskoboinyk ("adjust size for PCIe Rx buffer!"));
19517d5fbf2SAndriy Voskoboinyk
19617d5fbf2SAndriy Voskoboinyk memcpy(pc->pc_rx_buf, rx_desc, desc_size);
19717d5fbf2SAndriy Voskoboinyk
19817d5fbf2SAndriy Voskoboinyk rxdw0 = le32toh(rx_desc->rxdw0);
19917d5fbf2SAndriy Voskoboinyk pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN);
20017d5fbf2SAndriy Voskoboinyk
20117d5fbf2SAndriy Voskoboinyk if (pktlen > sizeof(pc->pc_rx_buf) - desc_size)
20217d5fbf2SAndriy Voskoboinyk {
20317d5fbf2SAndriy Voskoboinyk /* Looks like an ordinary Rx frame. */
20417d5fbf2SAndriy Voskoboinyk return (desc_size);
20517d5fbf2SAndriy Voskoboinyk }
20617d5fbf2SAndriy Voskoboinyk
20717d5fbf2SAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, rx_data->map, BUS_DMASYNC_POSTREAD);
20817d5fbf2SAndriy Voskoboinyk memcpy(pc->pc_rx_buf + desc_size, mtod(rx_data->m, void *), pktlen);
20917d5fbf2SAndriy Voskoboinyk
21017d5fbf2SAndriy Voskoboinyk return (desc_size + pktlen);
21117d5fbf2SAndriy Voskoboinyk }
21217d5fbf2SAndriy Voskoboinyk
21317d5fbf2SAndriy Voskoboinyk static void
rtwn_pci_tx_report(struct rtwn_pci_softc * pc,int len)21417d5fbf2SAndriy Voskoboinyk rtwn_pci_tx_report(struct rtwn_pci_softc *pc, int len)
21517d5fbf2SAndriy Voskoboinyk {
21617d5fbf2SAndriy Voskoboinyk struct rtwn_softc *sc = &pc->pc_sc;
21717d5fbf2SAndriy Voskoboinyk
21817d5fbf2SAndriy Voskoboinyk if (sc->sc_ratectl != RTWN_RATECTL_NET80211) {
21917d5fbf2SAndriy Voskoboinyk /* shouldn't happen */
22017d5fbf2SAndriy Voskoboinyk device_printf(sc->sc_dev,
22117d5fbf2SAndriy Voskoboinyk "%s called while ratectl = %d!\n",
22217d5fbf2SAndriy Voskoboinyk __func__, sc->sc_ratectl);
22317d5fbf2SAndriy Voskoboinyk return;
22417d5fbf2SAndriy Voskoboinyk }
22517d5fbf2SAndriy Voskoboinyk
22617d5fbf2SAndriy Voskoboinyk RTWN_NT_LOCK(sc);
22717d5fbf2SAndriy Voskoboinyk rtwn_handle_tx_report(sc, pc->pc_rx_buf, len);
22817d5fbf2SAndriy Voskoboinyk RTWN_NT_UNLOCK(sc);
22917d5fbf2SAndriy Voskoboinyk
23017d5fbf2SAndriy Voskoboinyk #ifdef IEEE80211_SUPPORT_SUPERG
23117d5fbf2SAndriy Voskoboinyk /*
23217d5fbf2SAndriy Voskoboinyk * NB: this will executed only when 'report' bit is set.
23317d5fbf2SAndriy Voskoboinyk */
23417d5fbf2SAndriy Voskoboinyk if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1)
23517d5fbf2SAndriy Voskoboinyk rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all);
23617d5fbf2SAndriy Voskoboinyk #endif
23717d5fbf2SAndriy Voskoboinyk }
23817d5fbf2SAndriy Voskoboinyk
23917d5fbf2SAndriy Voskoboinyk static void
rtwn_pci_tx_report2(struct rtwn_pci_softc * pc,int len)240*b59017c5SAdrian Chadd rtwn_pci_tx_report2(struct rtwn_pci_softc *pc, int len)
241*b59017c5SAdrian Chadd {
242*b59017c5SAdrian Chadd struct rtwn_softc *sc = &pc->pc_sc;
243*b59017c5SAdrian Chadd
244*b59017c5SAdrian Chadd if (sc->sc_ratectl != RTWN_RATECTL_NET80211) {
245*b59017c5SAdrian Chadd /* shouldn't happen */
246*b59017c5SAdrian Chadd device_printf(sc->sc_dev,
247*b59017c5SAdrian Chadd "%s called while ratectl = %d!\n",
248*b59017c5SAdrian Chadd __func__, sc->sc_ratectl);
249*b59017c5SAdrian Chadd return;
250*b59017c5SAdrian Chadd }
251*b59017c5SAdrian Chadd
252*b59017c5SAdrian Chadd RTWN_NT_LOCK(sc);
253*b59017c5SAdrian Chadd rtwn_handle_tx_report2(sc, pc->pc_rx_buf, len);
254*b59017c5SAdrian Chadd RTWN_NT_UNLOCK(sc);
255*b59017c5SAdrian Chadd
256*b59017c5SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG
257*b59017c5SAdrian Chadd /*
258*b59017c5SAdrian Chadd * NB: this will executed only when 'report' bit is set.
259*b59017c5SAdrian Chadd */
260*b59017c5SAdrian Chadd if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1)
261*b59017c5SAdrian Chadd rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all);
262*b59017c5SAdrian Chadd #endif
263*b59017c5SAdrian Chadd }
264*b59017c5SAdrian Chadd
265*b59017c5SAdrian Chadd
266*b59017c5SAdrian Chadd static void
rtwn_pci_c2h_report(struct rtwn_pci_softc * pc,int len)26717d5fbf2SAndriy Voskoboinyk rtwn_pci_c2h_report(struct rtwn_pci_softc *pc, int len)
26817d5fbf2SAndriy Voskoboinyk {
26917d5fbf2SAndriy Voskoboinyk rtwn_handle_c2h_report(&pc->pc_sc, pc->pc_rx_buf, len);
27017d5fbf2SAndriy Voskoboinyk }
27117d5fbf2SAndriy Voskoboinyk
2727453645fSAndriy Voskoboinyk static void
rtwn_pci_tx_done(struct rtwn_softc * sc,int qid)2737453645fSAndriy Voskoboinyk rtwn_pci_tx_done(struct rtwn_softc *sc, int qid)
2747453645fSAndriy Voskoboinyk {
2757453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
2767453645fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[qid];
2777453645fSAndriy Voskoboinyk struct rtwn_tx_desc_common *desc;
2787453645fSAndriy Voskoboinyk struct rtwn_tx_data *data;
2797453645fSAndriy Voskoboinyk
2807453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: qid %d, last %d, cur %d\n",
2817453645fSAndriy Voskoboinyk __func__, qid, ring->last, ring->cur);
2827453645fSAndriy Voskoboinyk
283b5a81dd4SAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
284b5a81dd4SAndriy Voskoboinyk BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2857453645fSAndriy Voskoboinyk
2867453645fSAndriy Voskoboinyk while(ring->last != ring->cur) {
2877453645fSAndriy Voskoboinyk data = &ring->tx_data[ring->last];
2887453645fSAndriy Voskoboinyk desc = (struct rtwn_tx_desc_common *)
2897453645fSAndriy Voskoboinyk ((uint8_t *)ring->desc + sc->txdesc_len * ring->last);
2907453645fSAndriy Voskoboinyk
2917453645fSAndriy Voskoboinyk KASSERT(data->m != NULL, ("no mbuf"));
2927453645fSAndriy Voskoboinyk
2937453645fSAndriy Voskoboinyk if (desc->flags0 & RTWN_FLAGS0_OWN)
2947453645fSAndriy Voskoboinyk break;
2957453645fSAndriy Voskoboinyk
2967453645fSAndriy Voskoboinyk /* Unmap and free mbuf. */
2977453645fSAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, data->map,
2987453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE);
2997453645fSAndriy Voskoboinyk bus_dmamap_unload(ring->data_dmat, data->map);
3007453645fSAndriy Voskoboinyk
3017453645fSAndriy Voskoboinyk if (data->ni != NULL) { /* not a beacon frame */
3027453645fSAndriy Voskoboinyk ieee80211_tx_complete(data->ni, data->m, 0);
3037453645fSAndriy Voskoboinyk
3047453645fSAndriy Voskoboinyk data->ni = NULL;
3057453645fSAndriy Voskoboinyk ring->queued--;
306a14f9888SAndriy Voskoboinyk KASSERT(ring->queued >= 0,
307a14f9888SAndriy Voskoboinyk ("ring->queued (qid %d) underflow!\n", qid));
3087453645fSAndriy Voskoboinyk } else
3097453645fSAndriy Voskoboinyk m_freem(data->m);
3107453645fSAndriy Voskoboinyk
3117453645fSAndriy Voskoboinyk data->m = NULL;
3127453645fSAndriy Voskoboinyk ring->last = (ring->last + 1) % RTWN_PCI_TX_LIST_COUNT;
3137453645fSAndriy Voskoboinyk #ifndef D4054
3147453645fSAndriy Voskoboinyk if (ring->queued > 0)
3157453645fSAndriy Voskoboinyk sc->sc_tx_timer = 5;
3167453645fSAndriy Voskoboinyk else
3177453645fSAndriy Voskoboinyk sc->sc_tx_timer = 0;
3187453645fSAndriy Voskoboinyk #endif
3197453645fSAndriy Voskoboinyk }
3207453645fSAndriy Voskoboinyk
321a14f9888SAndriy Voskoboinyk if ((sc->qfullmsk & (1 << qid)) != 0 &&
322a14f9888SAndriy Voskoboinyk ring->queued < (RTWN_PCI_TX_LIST_COUNT - 1)) {
3237453645fSAndriy Voskoboinyk sc->qfullmsk &= ~(1 << qid);
3247453645fSAndriy Voskoboinyk rtwn_start(sc);
3257453645fSAndriy Voskoboinyk }
3267453645fSAndriy Voskoboinyk
327a14f9888SAndriy Voskoboinyk #ifdef IEEE80211_SUPPORT_SUPERG
328a14f9888SAndriy Voskoboinyk /*
329a14f9888SAndriy Voskoboinyk * If the TX active queue drops below a certain
330a14f9888SAndriy Voskoboinyk * threshold, ensure we age fast-frames out so they're
331a14f9888SAndriy Voskoboinyk * transmitted.
332a14f9888SAndriy Voskoboinyk */
333a14f9888SAndriy Voskoboinyk if (sc->sc_ratectl != RTWN_RATECTL_NET80211 && ring->queued <= 1) {
334a14f9888SAndriy Voskoboinyk /*
335a14f9888SAndriy Voskoboinyk * XXX TODO: just make this a callout timer schedule
336a14f9888SAndriy Voskoboinyk * so we can flush the FF staging queue if we're
337a14f9888SAndriy Voskoboinyk * approaching idle.
338a14f9888SAndriy Voskoboinyk */
339a14f9888SAndriy Voskoboinyk rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all);
340a14f9888SAndriy Voskoboinyk }
341a14f9888SAndriy Voskoboinyk #endif
342a14f9888SAndriy Voskoboinyk }
343a14f9888SAndriy Voskoboinyk
3447453645fSAndriy Voskoboinyk static void
rtwn_pci_rx_done(struct rtwn_softc * sc)3457453645fSAndriy Voskoboinyk rtwn_pci_rx_done(struct rtwn_softc *sc)
3467453645fSAndriy Voskoboinyk {
3477453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
3487453645fSAndriy Voskoboinyk struct rtwn_rx_ring *ring = &pc->rx_ring;
34917d5fbf2SAndriy Voskoboinyk struct rtwn_rx_stat_pci *rx_desc;
35017d5fbf2SAndriy Voskoboinyk struct rtwn_rx_data *rx_data;
35117d5fbf2SAndriy Voskoboinyk int len;
3527453645fSAndriy Voskoboinyk
3537453645fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD);
3547453645fSAndriy Voskoboinyk
3557453645fSAndriy Voskoboinyk for (;;) {
35617d5fbf2SAndriy Voskoboinyk rx_desc = &ring->desc[ring->cur];
35717d5fbf2SAndriy Voskoboinyk rx_data = &ring->rx_data[ring->cur];
3587453645fSAndriy Voskoboinyk
359c5ad99fcSAndriy Voskoboinyk if (le32toh(rx_desc->rxdw0) & RTWN_RXDW0_OWN)
3607453645fSAndriy Voskoboinyk break;
3617453645fSAndriy Voskoboinyk
36217d5fbf2SAndriy Voskoboinyk len = rtwn_pci_rx_buf_copy(pc);
36317d5fbf2SAndriy Voskoboinyk
36417d5fbf2SAndriy Voskoboinyk switch (rtwn_classify_intr(sc, pc->pc_rx_buf, len)) {
36517d5fbf2SAndriy Voskoboinyk case RTWN_RX_DATA:
36617d5fbf2SAndriy Voskoboinyk rtwn_pci_rx_frame(pc);
36717d5fbf2SAndriy Voskoboinyk break;
36817d5fbf2SAndriy Voskoboinyk case RTWN_RX_TX_REPORT:
36917d5fbf2SAndriy Voskoboinyk rtwn_pci_tx_report(pc, len);
37017d5fbf2SAndriy Voskoboinyk break;
371*b59017c5SAdrian Chadd case RTWN_RX_TX_REPORT2:
372*b59017c5SAdrian Chadd rtwn_pci_tx_report2(pc, len);
373*b59017c5SAdrian Chadd break;
37417d5fbf2SAndriy Voskoboinyk case RTWN_RX_OTHER:
37517d5fbf2SAndriy Voskoboinyk rtwn_pci_c2h_report(pc, len);
37617d5fbf2SAndriy Voskoboinyk break;
37717d5fbf2SAndriy Voskoboinyk default:
37817d5fbf2SAndriy Voskoboinyk /* NOTREACHED */
37917d5fbf2SAndriy Voskoboinyk KASSERT(0, ("unknown Rx classification code"));
38017d5fbf2SAndriy Voskoboinyk break;
38117d5fbf2SAndriy Voskoboinyk }
38217d5fbf2SAndriy Voskoboinyk
38317d5fbf2SAndriy Voskoboinyk /* Update / reset RX descriptor (and set OWN bit). */
38417d5fbf2SAndriy Voskoboinyk rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr,
38517d5fbf2SAndriy Voskoboinyk MJUMPAGESIZE, ring->cur);
3867453645fSAndriy Voskoboinyk
3877453645fSAndriy Voskoboinyk if (!(sc->sc_flags & RTWN_RUNNING))
3887453645fSAndriy Voskoboinyk return;
3897453645fSAndriy Voskoboinyk
39017d5fbf2SAndriy Voskoboinyk /* NB: device can reuse current descriptor. */
39117d5fbf2SAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
39217d5fbf2SAndriy Voskoboinyk BUS_DMASYNC_POSTREAD);
39317d5fbf2SAndriy Voskoboinyk
39417d5fbf2SAndriy Voskoboinyk if (le32toh(rx_desc->rxdw0) & RTWN_RXDW0_OWN)
3957453645fSAndriy Voskoboinyk ring->cur = (ring->cur + 1) % RTWN_PCI_RX_LIST_COUNT;
3967453645fSAndriy Voskoboinyk }
3977453645fSAndriy Voskoboinyk }
3987453645fSAndriy Voskoboinyk
3997453645fSAndriy Voskoboinyk void
rtwn_pci_intr(void * arg)4007453645fSAndriy Voskoboinyk rtwn_pci_intr(void *arg)
4017453645fSAndriy Voskoboinyk {
4027453645fSAndriy Voskoboinyk struct rtwn_softc *sc = arg;
4037453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
4047453645fSAndriy Voskoboinyk int i, status, tx_rings;
4057453645fSAndriy Voskoboinyk
4067453645fSAndriy Voskoboinyk RTWN_LOCK(sc);
40717d5fbf2SAndriy Voskoboinyk status = rtwn_pci_get_intr_status(pc, &tx_rings);
4087453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: status %08X, tx_rings %08X\n",
4097453645fSAndriy Voskoboinyk __func__, status, tx_rings);
41077cbc479SAndriy Voskoboinyk if (status == 0 && tx_rings == 0)
41177cbc479SAndriy Voskoboinyk goto unlock;
4127453645fSAndriy Voskoboinyk
41317d5fbf2SAndriy Voskoboinyk if (status & (RTWN_PCI_INTR_RX | RTWN_PCI_INTR_TX_REPORT)) {
4147453645fSAndriy Voskoboinyk rtwn_pci_rx_done(sc);
41577cbc479SAndriy Voskoboinyk if (!(sc->sc_flags & RTWN_RUNNING))
41677cbc479SAndriy Voskoboinyk goto unlock;
41777cbc479SAndriy Voskoboinyk }
4187453645fSAndriy Voskoboinyk
4197453645fSAndriy Voskoboinyk if (tx_rings != 0)
4207453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++)
4217453645fSAndriy Voskoboinyk if (tx_rings & (1 << i))
4227453645fSAndriy Voskoboinyk rtwn_pci_tx_done(sc, i);
4237453645fSAndriy Voskoboinyk
4247453645fSAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING)
4257453645fSAndriy Voskoboinyk rtwn_pci_enable_intr(pc);
42677cbc479SAndriy Voskoboinyk unlock:
4277453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc);
4287453645fSAndriy Voskoboinyk }
429