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 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 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 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 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 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 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 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 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 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 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