Index: head/share/man/man4/rtwn_pci.4 =================================================================== --- head/share/man/man4/rtwn_pci.4 (revision 308574) +++ head/share/man/man4/rtwn_pci.4 (revision 308575) @@ -1,63 +1,60 @@ .\"- .\" Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd .\" Copyright (c) 2016 Andriy Voskoboinyk .\" All rights reserved. .\"" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer, .\" without modification. .\" 2. Redistributions in binary form must reproduce at minimum a disclaimer .\" similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any .\" redistribution must be conditioned upon including a substantially .\" similar Disclaimer requirement for further binary redistribution. .\" .\" NO WARRANTY .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY .\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL .\" THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, .\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER .\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF .\" THE POSSIBILITY OF SUCH DAMAGES. .\" .\" $FreeBSD$ .\"/ -.Dd October 17, 2016 +.Dd November 12, 2016 .Dt RTWN_PCI 4 .Os .Sh NAME .Nm rtwn_pci .Nd "Realtek PCI device glue" .Sh SYNOPSIS To compile this driver into the kernel, place the following lines in your kernel configuration file: .Bd -ragged -offset indent .Cd "device rtwn_pci" .Cd "device pci" .Cd "device wlan" .Ed .Sh DESCRIPTION The .Nm driver supports PCIe wireless network devices based on the Realtek RTL8188CE chipset. .Pp The RTL8188CE is a highly integrated 802.11n adapter that combines a MAC, a 1T1R capable baseband and an RF in a single chip. It operates in the 2GHz spectrum only. .Sh SEE ALSO .Xr rtwn 4 , .Xr rtwnfw 4 , .Xr rtwn_usb 4 , .Xr pci 4 -.Sh CAVEATS -Most 802.11 capabilities were turned off; some more testing -is required to re-enable them. Index: head/sys/dev/rtwn/if_rtwn_tx.h =================================================================== --- head/sys/dev/rtwn/if_rtwn_tx.h (revision 308574) +++ head/sys/dev/rtwn/if_rtwn_tx.h (revision 308575) @@ -1,29 +1,31 @@ /*- * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef IF_RTWN_TX_H #define IF_RTWN_TX_H void rtwn_drain_mbufq(struct rtwn_softc *); +#ifdef IEEE80211_SUPPORT_SUPERG void rtwn_ff_flush_all(struct rtwn_softc *, union sec_param *); +#endif int rtwn_transmit(struct ieee80211com *, struct mbuf *); void rtwn_start(struct rtwn_softc *); int rtwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); #endif /* IF_RTWN_TX_H */ Index: head/sys/dev/rtwn/pci/rtwn_pci_attach.c =================================================================== --- head/sys/dev/rtwn/pci/rtwn_pci_attach.c (revision 308574) +++ head/sys/dev/rtwn/pci/rtwn_pci_attach.c (revision 308575) @@ -1,781 +1,782 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t rtwn_pci_probe; static device_attach_t rtwn_pci_attach; static device_detach_t rtwn_pci_detach; static device_shutdown_t rtwn_pci_shutdown; static device_suspend_t rtwn_pci_suspend; static device_resume_t rtwn_pci_resume; static int rtwn_pci_alloc_rx_list(struct rtwn_softc *); static void rtwn_pci_reset_rx_list(struct rtwn_softc *); static void rtwn_pci_free_rx_list(struct rtwn_softc *); static int rtwn_pci_alloc_tx_list(struct rtwn_softc *, int); static void rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *, int); static void rtwn_pci_reset_beacon_ring(struct rtwn_softc *, int); static void rtwn_pci_reset_tx_list(struct rtwn_softc *, struct ieee80211vap *, int); static void rtwn_pci_free_tx_list(struct rtwn_softc *, int); static void rtwn_pci_reset_lists(struct rtwn_softc *, struct ieee80211vap *); static int rtwn_pci_fw_write_block(struct rtwn_softc *, const uint8_t *, uint16_t, int); static uint16_t rtwn_pci_get_qmap(struct rtwn_softc *); static void rtwn_pci_set_desc_addr(struct rtwn_softc *); static void rtwn_pci_beacon_update_begin(struct rtwn_softc *, struct ieee80211vap *); static void rtwn_pci_beacon_update_end(struct rtwn_softc *, struct ieee80211vap *); static void rtwn_pci_attach_methods(struct rtwn_softc *); static int matched_chip = RTWN_CHIP_MAX_PCI; static int rtwn_pci_probe(device_t dev) { const struct rtwn_pci_ident *ident; for (ident = rtwn_pci_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { matched_chip = ident->chip; device_set_desc(dev, ident->name); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } static int rtwn_pci_alloc_rx_list(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *rx_ring = &pc->rx_ring; struct rtwn_rx_data *rx_data; bus_size_t size; int i, error; /* Allocate Rx descriptors. */ size = sizeof(struct r92ce_rx_stat) * RTWN_PCI_RX_LIST_COUNT; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create rx desc DMA tag\n"); goto fail; } error = bus_dmamem_alloc(rx_ring->desc_dmat, (void **)&rx_ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &rx_ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate rx desc\n"); goto fail; } error = bus_dmamap_load(rx_ring->desc_dmat, rx_ring->desc_map, rx_ring->desc, size, rtwn_pci_dma_map_addr, &rx_ring->paddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load rx desc DMA map\n"); goto fail; } bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map, BUS_DMASYNC_PREWRITE); /* Create RX buffer DMA tag. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, - 1, MCLBYTES, 0, NULL, NULL, &rx_ring->data_dmat); + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &rx_ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); goto fail; } /* Allocate Rx buffers. */ for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { rx_data = &rx_ring->rx_data[i]; error = bus_dmamap_create(rx_ring->data_dmat, 0, &rx_data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create rx buf DMA map\n"); goto fail; } - rx_data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + rx_data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, + MJUMPAGESIZE); if (rx_data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } error = bus_dmamap_load(rx_ring->data_dmat, rx_data->map, - mtod(rx_data->m, void *), MCLBYTES, rtwn_pci_dma_map_addr, - &rx_data->paddr, BUS_DMA_NOWAIT); + mtod(rx_data->m, void *), MJUMPAGESIZE, + rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not load rx buf DMA map"); goto fail; } rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], rx_data->paddr, - MCLBYTES, i); + MJUMPAGESIZE, i); } rx_ring->cur = 0; return (0); fail: rtwn_pci_free_rx_list(sc); return (error); } static void rtwn_pci_reset_rx_list(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *rx_ring = &pc->rx_ring; struct rtwn_rx_data *rx_data; int i; for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { rx_data = &rx_ring->rx_data[i]; rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], - rx_data->paddr, MCLBYTES, i); + rx_data->paddr, MJUMPAGESIZE, i); } rx_ring->cur = 0; } static void rtwn_pci_free_rx_list(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *rx_ring = &pc->rx_ring; struct rtwn_rx_data *rx_data; int i; if (rx_ring->desc_dmat != NULL) { if (rx_ring->desc != NULL) { bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(rx_ring->desc_dmat, rx_ring->desc_map); bus_dmamem_free(rx_ring->desc_dmat, rx_ring->desc, rx_ring->desc_map); rx_ring->desc = NULL; } bus_dma_tag_destroy(rx_ring->desc_dmat); rx_ring->desc_dmat = NULL; } for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { rx_data = &rx_ring->rx_data[i]; if (rx_data->m != NULL) { bus_dmamap_sync(rx_ring->data_dmat, rx_data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(rx_ring->data_dmat, rx_data->map); m_freem(rx_data->m); rx_data->m = NULL; } bus_dmamap_destroy(rx_ring->data_dmat, rx_data->map); rx_data->map = NULL; } if (rx_ring->data_dmat != NULL) { bus_dma_tag_destroy(rx_ring->data_dmat); rx_ring->data_dmat = NULL; } } static int rtwn_pci_alloc_tx_list(struct rtwn_softc *sc, int qid) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; bus_size_t size; int i, error; size = sc->txdesc_len * RTWN_PCI_TX_LIST_COUNT; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &tx_ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); goto fail; } error = bus_dmamem_alloc(tx_ring->desc_dmat, &tx_ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &tx_ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "can't map tx ring DMA memory\n"); goto fail; } error = bus_dmamap_load(tx_ring->desc_dmat, tx_ring->desc_map, tx_ring->desc, size, rtwn_pci_dma_map_addr, &tx_ring->paddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not load desc DMA map\n"); goto fail; } bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, BUS_DMASYNC_PREWRITE); error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, - 1, MCLBYTES, 0, NULL, NULL, &tx_ring->data_dmat); + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &tx_ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); goto fail; } for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; void *tx_desc = (uint8_t *)tx_ring->desc + sc->txdesc_len * i; uint32_t next_desc_addr = tx_ring->paddr + sc->txdesc_len * ((i + 1) % RTWN_PCI_TX_LIST_COUNT); rtwn_pci_setup_tx_desc(pc, tx_desc, next_desc_addr); error = bus_dmamap_create(tx_ring->data_dmat, 0, &tx_data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create tx buf DMA map\n"); return (error); } tx_data->m = NULL; tx_data->ni = NULL; } return (0); fail: rtwn_pci_free_tx_list(sc, qid); return (error); } static void rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *sc, int qid) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; int i; for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { struct rtwn_tx_data *data = &ring->tx_data[i]; void *desc = (uint8_t *)ring->desc + sc->txdesc_len * i; rtwn_pci_copy_tx_desc(pc, desc, NULL); if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); sc->qfullmsk &= ~(1 << qid); ring->queued = 0; ring->last = ring->cur = 0; } /* * Clear entry 0 (or 1) in the beacon queue (other are not used). */ static void rtwn_pci_reset_beacon_ring(struct rtwn_softc *sc, int id) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE]; struct rtwn_tx_data *data = &ring->tx_data[id]; struct rtwn_tx_desc_common *txd = (struct rtwn_tx_desc_common *) ((uint8_t *)ring->desc + id * sc->txdesc_len); bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); if (txd->flags0 & RTWN_FLAGS0_OWN) { /* Clear OWN bit. */ txd->flags0 &= ~RTWN_FLAGS0_OWN; bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); /* Unload mbuf. */ bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); } } /* * Drop stale entries from Tx ring before the vap will be deleted. * In case if vap is NULL just free everything and reset cur / last pointers. */ static void rtwn_pci_reset_tx_list(struct rtwn_softc *sc, struct ieee80211vap *vap, int qid) { int i; if (vap == NULL) { if (qid != RTWN_PCI_BEACON_QUEUE) { /* * Device was stopped; just clear all entries. */ rtwn_pci_reset_tx_ring_stopped(sc, qid); } else { for (i = 0; i < RTWN_PORT_COUNT; i++) rtwn_pci_reset_beacon_ring(sc, i); } } else if (qid == RTWN_PCI_BEACON_QUEUE && (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS)) { struct rtwn_vap *uvp = RTWN_VAP(vap); rtwn_pci_reset_beacon_ring(sc, uvp->id); } else { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { struct rtwn_tx_data *data = &ring->tx_data[i]; if (data->ni != NULL && data->ni->ni_vap == vap) { /* * NB: if some vap is still running * rtwn_pci_tx_done() will free the mbuf; * otherwise, rtwn_stop() will reset all rings * after device shutdown. */ ieee80211_free_node(data->ni); data->ni = NULL; } } } } static void rtwn_pci_free_tx_list(struct rtwn_softc *sc, int qid) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; struct rtwn_tx_data *tx_data; int i; if (tx_ring->desc_dmat != NULL) { if (tx_ring->desc != NULL) { bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(tx_ring->desc_dmat, tx_ring->desc_map); bus_dmamem_free(tx_ring->desc_dmat, tx_ring->desc, tx_ring->desc_map); } bus_dma_tag_destroy(tx_ring->desc_dmat); } for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { tx_data = &tx_ring->tx_data[i]; if (tx_data->m != NULL) { bus_dmamap_sync(tx_ring->data_dmat, tx_data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); m_freem(tx_data->m); tx_data->m = NULL; } } if (tx_ring->data_dmat != NULL) { bus_dma_tag_destroy(tx_ring->data_dmat); tx_ring->data_dmat = NULL; } sc->qfullmsk &= ~(1 << qid); tx_ring->queued = 0; tx_ring->last = tx_ring->cur = 0; } static void rtwn_pci_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap) { int i; for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) rtwn_pci_reset_tx_list(sc, vap, i); if (vap == NULL) { sc->qfullmsk = 0; rtwn_pci_reset_rx_list(sc); } } static int rtwn_pci_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf, uint16_t reg, int mlen) { int i; for (i = 0; i < mlen; i++) rtwn_pci_write_1(sc, reg++, buf[i]); /* NB: cannot fail */ return (0); } static uint16_t rtwn_pci_get_qmap(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); KASSERT(pc->pc_qmap != 0, ("%s: qmap is not set!\n", __func__)); return (pc->pc_qmap); } static void rtwn_pci_set_desc_addr(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); RTWN_DPRINTF(sc, RTWN_DEBUG_RESET, "%s: addresses:\n" "bk: %08jX, be: %08jX, vi: %08jX, vo: %08jX\n" "bcn: %08jX, mgt: %08jX, high: %08jX, rx: %08jX\n", __func__, (uintmax_t)pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr, (uintmax_t)pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr, (uintmax_t)pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr, (uintmax_t)pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr, (uintmax_t)pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr, (uintmax_t)pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr, (uintmax_t)pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr, (uintmax_t)pc->rx_ring.paddr); /* Set Tx Configuration Register. */ rtwn_pci_write_4(sc, R92C_TCR, pc->tcr); /* Configure Tx DMA. */ rtwn_pci_write_4(sc, R92C_BKQ_DESA, pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr); rtwn_pci_write_4(sc, R92C_BEQ_DESA, pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr); rtwn_pci_write_4(sc, R92C_VIQ_DESA, pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr); rtwn_pci_write_4(sc, R92C_VOQ_DESA, pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr); rtwn_pci_write_4(sc, R92C_BCNQ_DESA, pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr); rtwn_pci_write_4(sc, R92C_MGQ_DESA, pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr); rtwn_pci_write_4(sc, R92C_HQ_DESA, pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr); /* Configure Rx DMA. */ rtwn_pci_write_4(sc, R92C_RX_DESA, pc->rx_ring.paddr); } static void rtwn_pci_beacon_update_begin(struct rtwn_softc *sc, struct ieee80211vap *vap) { struct rtwn_vap *rvp = RTWN_VAP(vap); RTWN_ASSERT_LOCKED(sc); rtwn_beacon_enable(sc, rvp->id, 0); } static void rtwn_pci_beacon_update_end(struct rtwn_softc *sc, struct ieee80211vap *vap) { struct rtwn_vap *rvp = RTWN_VAP(vap); RTWN_ASSERT_LOCKED(sc); if (rvp->curr_mode != R92C_MSR_NOLINK) rtwn_beacon_enable(sc, rvp->id, 1); } static void rtwn_pci_attach_methods(struct rtwn_softc *sc) { sc->sc_write_1 = rtwn_pci_write_1; sc->sc_write_2 = rtwn_pci_write_2; sc->sc_write_4 = rtwn_pci_write_4; sc->sc_read_1 = rtwn_pci_read_1; sc->sc_read_2 = rtwn_pci_read_2; sc->sc_read_4 = rtwn_pci_read_4; sc->sc_delay = rtwn_pci_delay; sc->sc_tx_start = rtwn_pci_tx_start; sc->sc_reset_lists = rtwn_pci_reset_lists; sc->sc_abort_xfers = rtwn_nop_softc; sc->sc_fw_write_block = rtwn_pci_fw_write_block; sc->sc_get_qmap = rtwn_pci_get_qmap; sc->sc_set_desc_addr = rtwn_pci_set_desc_addr; sc->sc_drop_incorrect_tx = rtwn_nop_softc; sc->sc_beacon_update_begin = rtwn_pci_beacon_update_begin; sc->sc_beacon_update_end = rtwn_pci_beacon_update_end; sc->sc_beacon_unload = rtwn_pci_reset_beacon_ring; sc->bcn_check_interval = 25000; } static int rtwn_pci_attach(device_t dev) { struct rtwn_pci_softc *pc = device_get_softc(dev); struct rtwn_softc *sc = &pc->pc_sc; struct ieee80211com *ic = &sc->sc_ic; uint32_t lcsr; int cap_off, i, error, rid; if (matched_chip >= RTWN_CHIP_MAX_PCI) return (ENXIO); /* * Get the offset of the PCI Express Capability Structure in PCI * Configuration Space. */ error = pci_find_cap(dev, PCIY_EXPRESS, &cap_off); if (error != 0) { device_printf(dev, "PCIe capability structure not found!\n"); return (error); } /* Enable bus-mastering. */ pci_enable_busmaster(dev); rid = PCIR_BAR(2); pc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (pc->mem == NULL) { device_printf(dev, "can't map mem space\n"); return (ENOMEM); } pc->pc_st = rman_get_bustag(pc->mem); pc->pc_sh = rman_get_bushandle(pc->mem); /* Install interrupt handler. */ rid = 1; if (pci_alloc_msi(dev, &rid) == 0) rid = 1; else rid = 0; pc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); if (pc->irq == NULL) { device_printf(dev, "can't map interrupt\n"); goto detach; } /* Disable PCIe Active State Power Management (ASPM). */ lcsr = pci_read_config(dev, cap_off + PCIER_LINK_CTL, 4); lcsr &= ~PCIEM_LINK_CTL_ASPMC; pci_write_config(dev, cap_off + PCIER_LINK_CTL, lcsr, 4); sc->sc_dev = dev; ic->ic_name = device_get_nameunit(dev); /* Need to be initialized early. */ rtwn_sysctlattach(sc); mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF); rtwn_pci_attach_methods(sc); /* XXX something similar to USB_GET_DRIVER_INFO() */ rtwn_pci_attach_private(pc, matched_chip); /* Allocate Tx/Rx buffers. */ error = rtwn_pci_alloc_rx_list(sc); if (error != 0) { device_printf(dev, "could not allocate Rx buffers, error %d\n", error); goto detach; } for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) { error = rtwn_pci_alloc_tx_list(sc, i); if (error != 0) { device_printf(dev, "could not allocate Tx buffers, error %d\n", error); goto detach; } } /* Generic attach. */ error = rtwn_attach(sc); if (error != 0) goto detach; /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, pc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, rtwn_pci_intr, sc, &pc->pc_ih); if (error != 0) { device_printf(dev, "can't establish interrupt, error %d\n", error); goto detach; } return (0); detach: rtwn_pci_detach(dev); /* failure */ return (ENXIO); } static int rtwn_pci_detach(device_t dev) { struct rtwn_pci_softc *pc = device_get_softc(dev); struct rtwn_softc *sc = &pc->pc_sc; int i; /* Generic detach. */ rtwn_detach(sc); /* Uninstall interrupt handler. */ if (pc->irq != NULL) { bus_teardown_intr(dev, pc->irq, pc->pc_ih); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(pc->irq), pc->irq); pci_release_msi(dev); } /* Free Tx/Rx buffers. */ for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) rtwn_pci_free_tx_list(sc, i); rtwn_pci_free_rx_list(sc); if (pc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(pc->mem), pc->mem); rtwn_detach_private(sc); mtx_destroy(&sc->sc_mtx); return (0); } static int rtwn_pci_shutdown(device_t self) { struct rtwn_pci_softc *pc = device_get_softc(self); ieee80211_stop_all(&pc->pc_sc.sc_ic); return (0); } static int rtwn_pci_suspend(device_t self) { struct rtwn_pci_softc *pc = device_get_softc(self); rtwn_suspend(&pc->pc_sc); return (0); } static int rtwn_pci_resume(device_t self) { struct rtwn_pci_softc *pc = device_get_softc(self); rtwn_resume(&pc->pc_sc); return (0); } static device_method_t rtwn_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtwn_pci_probe), DEVMETHOD(device_attach, rtwn_pci_attach), DEVMETHOD(device_detach, rtwn_pci_detach), DEVMETHOD(device_shutdown, rtwn_pci_shutdown), DEVMETHOD(device_suspend, rtwn_pci_suspend), DEVMETHOD(device_resume, rtwn_pci_resume), DEVMETHOD_END }; static driver_t rtwn_pci_driver = { "rtwn", rtwn_pci_methods, sizeof(struct rtwn_pci_softc) }; static devclass_t rtwn_pci_devclass; DRIVER_MODULE(rtwn_pci, pci, rtwn_pci_driver, rtwn_pci_devclass, NULL, NULL); MODULE_VERSION(rtwn_pci, 1); MODULE_DEPEND(rtwn_pci, pci, 1, 1, 1); MODULE_DEPEND(rtwn_pci, wlan, 1, 1, 1); MODULE_DEPEND(rtwn_pci, rtwn, 2, 2, 2); Index: head/sys/dev/rtwn/pci/rtwn_pci_rx.c =================================================================== --- head/sys/dev/rtwn/pci/rtwn_pci_rx.c (revision 308574) +++ head/sys/dev/rtwn/pci/rtwn_pci_rx.c (revision 308575) @@ -1,295 +1,327 @@ /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2015 Stefan Sperling * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include void rtwn_pci_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } void rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *pc, struct r92ce_rx_stat *desc, bus_addr_t addr, size_t len, int idx) { memset(desc, 0, sizeof(*desc)); desc->rxdw0 = htole32(SM(R92C_RXDW0_PKTLEN, len) | ((idx == RTWN_PCI_RX_LIST_COUNT - 1) ? R92C_RXDW0_EOR : 0)); desc->rxbufaddr = htole32(addr); bus_space_barrier(pc->pc_st, pc->pc_sh, 0, pc->pc_mapsize, BUS_SPACE_BARRIER_WRITE); desc->rxdw0 |= htole32(R92C_RXDW0_OWN); } static void rtwn_pci_rx_frame(struct rtwn_softc *sc, struct r92ce_rx_stat *rx_desc, int desc_idx) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *ring = &pc->rx_ring; struct rtwn_rx_data *rx_data = &ring->rx_data[desc_idx]; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; uint32_t rxdw0; struct mbuf *m, *m1; int8_t rssi = 0, nf; int infosz, pktlen, shift, error; /* Dump Rx descriptor. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X, " "addr: %08X (64: %08X)\n", __func__, le32toh(rx_desc->rxdw0), le32toh(rx_desc->rxdw1), le32toh(rx_desc->rxdw2), le32toh(rx_desc->rxdw3), le32toh(rx_desc->rxdw4), le32toh(rx_desc->tsf_low), le32toh(rx_desc->rxbufaddr), le32toh(rx_desc->rxbufaddr64)); rxdw0 = le32toh(rx_desc->rxdw0); if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV"); goto fail; } pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack) || - pktlen > MCLBYTES)) { + pktlen > MJUMPAGESIZE)) { RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: frame is too short/long: %d\n", __func__, pktlen); goto fail; } infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; shift = MS(rxdw0, R92C_RXDW0_SHIFT); - m1 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (__predict_false(m1 == NULL)) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n", __func__); goto fail; } bus_dmamap_sync(ring->data_dmat, rx_data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, rx_data->map); error = bus_dmamap_load(ring->data_dmat, rx_data->map, mtod(m1, void *), - MCLBYTES, rtwn_pci_dma_map_addr, &rx_data->paddr, 0); + MJUMPAGESIZE, rtwn_pci_dma_map_addr, &rx_data->paddr, 0); if (error != 0) { m_freem(m1); error = bus_dmamap_load(ring->data_dmat, rx_data->map, - mtod(rx_data->m, void *), MCLBYTES, rtwn_pci_dma_map_addr, - &rx_data->paddr, BUS_DMA_NOWAIT); + mtod(rx_data->m, void *), MJUMPAGESIZE, + rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT); if (error != 0) panic("%s: could not load old RX mbuf", device_get_name(sc->sc_dev)); /* Physical address may have changed. */ - rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, MCLBYTES, - desc_idx); + rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, + MJUMPAGESIZE, desc_idx); goto fail; } /* Finalize mbuf. */ m = rx_data->m; rx_data->m = m1; m->m_pkthdr.len = m->m_len = pktlen + infosz + shift; nf = RTWN_NOISE_FLOOR; ni = rtwn_rx_common(sc, m, rx_desc, &rssi); RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: Rx frame len %d, infosz %d, shift %d, rssi %d\n", __func__, pktlen, infosz, shift, rssi); /* Update RX descriptor. */ - rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, MCLBYTES, + rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, MJUMPAGESIZE, desc_idx); /* Send the frame to the 802.11 layer. */ RTWN_UNLOCK(sc); if (ni != NULL) { (void)ieee80211_input(ni, m, rssi - nf, nf); /* Node is no longer needed. */ ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rssi - nf, nf); RTWN_LOCK(sc); return; fail: counter_u64_add(ic->ic_ierrors, 1); } static void rtwn_pci_tx_done(struct rtwn_softc *sc, int qid) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; struct rtwn_tx_desc_common *desc; struct rtwn_tx_data *data; RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: qid %d, last %d, cur %d\n", __func__, qid, ring->last, ring->cur); bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); while(ring->last != ring->cur) { data = &ring->tx_data[ring->last]; desc = (struct rtwn_tx_desc_common *) ((uint8_t *)ring->desc + sc->txdesc_len * ring->last); KASSERT(data->m != NULL, ("no mbuf")); if (desc->flags0 & RTWN_FLAGS0_OWN) break; /* Unmap and free mbuf. */ bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); if (data->ni != NULL) { /* not a beacon frame */ ieee80211_tx_complete(data->ni, data->m, 0); data->ni = NULL; ring->queued--; + KASSERT(ring->queued >= 0, + ("ring->queued (qid %d) underflow!\n", qid)); } else m_freem(data->m); data->m = NULL; ring->last = (ring->last + 1) % RTWN_PCI_TX_LIST_COUNT; #ifndef D4054 if (ring->queued > 0) sc->sc_tx_timer = 5; else sc->sc_tx_timer = 0; #endif } - if (ring->queued < (RTWN_PCI_TX_LIST_COUNT - 1)) + if ((sc->qfullmsk & (1 << qid)) != 0 && + ring->queued < (RTWN_PCI_TX_LIST_COUNT - 1)) { sc->qfullmsk &= ~(1 << qid); - rtwn_start(sc); + rtwn_start(sc); + } + +#ifdef IEEE80211_SUPPORT_SUPERG + /* + * If the TX active queue drops below a certain + * threshold, ensure we age fast-frames out so they're + * transmitted. + */ + if (sc->sc_ratectl != RTWN_RATECTL_NET80211 && ring->queued <= 1) { + /* + * XXX TODO: just make this a callout timer schedule + * so we can flush the FF staging queue if we're + * approaching idle. + */ + rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); + } +#endif } static void rtwn_pci_rx_done(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *ring = &pc->rx_ring; bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); for (;;) { struct r92ce_rx_stat *rx_desc = &ring->desc[ring->cur]; if (le32toh(rx_desc->rxdw0) & R92C_RXDW0_OWN) break; rtwn_pci_rx_frame(sc, rx_desc, ring->cur); if (!(sc->sc_flags & RTWN_RUNNING)) return; ring->cur = (ring->cur + 1) % RTWN_PCI_RX_LIST_COUNT; } + + /* Finished receive; age anything left on the FF queue by a little bump */ + /* + * XXX TODO: just make this a callout timer schedule so we can + * flush the FF staging queue if we're approaching idle. + */ +#ifdef IEEE80211_SUPPORT_SUPERG + if (!(sc->sc_flags & RTWN_FW_LOADED) || + sc->sc_ratectl != RTWN_RATECTL_NET80211) + rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); +#endif } void rtwn_pci_intr(void *arg) { struct rtwn_softc *sc = arg; struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); int i, status, tx_rings; RTWN_LOCK(sc); status = rtwn_classify_intr(sc, &tx_rings, 0); RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: status %08X, tx_rings %08X\n", __func__, status, tx_rings); if (status == 0 && tx_rings == 0) goto unlock; if (status & RTWN_PCI_INTR_RX) { rtwn_pci_rx_done(sc); if (!(sc->sc_flags & RTWN_RUNNING)) goto unlock; } if (tx_rings != 0) for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) if (tx_rings & (1 << i)) rtwn_pci_tx_done(sc, i); if (sc->sc_flags & RTWN_RUNNING) rtwn_pci_enable_intr(pc); unlock: RTWN_UNLOCK(sc); } Index: head/sys/dev/rtwn/pci/rtwn_pci_tx.c =================================================================== --- head/sys/dev/rtwn/pci/rtwn_pci_tx.c (revision 308574) +++ head/sys/dev/rtwn/pci/rtwn_pci_tx.c (revision 308575) @@ -1,241 +1,272 @@ /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2015 Stefan Sperling * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +static struct mbuf * +rtwn_mbuf_defrag(struct mbuf *m0, int how) +{ + struct mbuf *m = NULL; + + KASSERT(m0->m_flags & M_PKTHDR, + ("M_PKTHDR flag is absent (m %p)!", m0)); + + /* NB: we need _exactly_ one mbuf (no less, no more). */ + if (m0->m_pkthdr.len > MJUMPAGESIZE) { + /* XXX MJUM9BYTES? */ + return (NULL); + } else if (m0->m_pkthdr.len > MCLBYTES) { + m = m_getjcl(how, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m == NULL) + return (NULL); + + if (m_dup_pkthdr(m, m0, how) == 0) { + m_freem(m); + return (NULL); + } + + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t)); + m->m_len = m->m_pkthdr.len; + m_freem(m0); + + return (m); + } else + return (m_defrag(m0, how)); +} + static int rtwn_pci_tx_start_frame(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, uint8_t *tx_desc, uint8_t type) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring; struct rtwn_tx_data *data; struct rtwn_tx_desc_common *txd; bus_dma_segment_t segs[1]; uint8_t qid; int nsegs, error; RTWN_ASSERT_LOCKED(sc); switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: qid = RTWN_PCI_MGNT_QUEUE; break; default: qid = M_WME_GETAC(m); break; } ring = &pc->tx_ring[qid]; data = &ring->tx_data[ring->cur]; if (data->m != NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, "%s: ring #%u is full (m %p)\n", __func__, qid, data->m); return (ENOBUFS); } txd = (struct rtwn_tx_desc_common *) ((uint8_t *)ring->desc + sc->txdesc_len * ring->cur); if (txd->flags0 & RTWN_FLAGS0_OWN) { device_printf(sc->sc_dev, "%s: OWN bit is set (tx desc %d, ring %u)!\n", __func__, ring->cur, qid); return (ENOBUFS); } /* Copy Tx descriptor. */ rtwn_pci_copy_tx_desc(pc, txd, tx_desc); txd->pktlen = htole16(m->m_pkthdr.len); txd->offset = sc->txdesc_len; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); return (error); } if (error != 0) { struct mbuf *mnew; - mnew = m_defrag(m, M_NOWAIT); + mnew = rtwn_mbuf_defrag(m, M_NOWAIT); if (mnew == NULL) { device_printf(sc->sc_dev, "can't defragment mbuf\n"); return (ENOBUFS); } m = mnew; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); if (ni != NULL) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); } m_freem(m); return (0); /* XXX */ } } rtwn_pci_tx_postsetup(pc, txd, segs); txd->flags0 |= RTWN_FLAGS0_OWN; /* Dump Tx descriptor. */ rtwn_dump_tx_desc(sc, txd); bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); data->m = m; data->ni = ni; ring->cur = (ring->cur + 1) % RTWN_PCI_TX_LIST_COUNT; ring->queued++; if (ring->queued >= (RTWN_PCI_TX_LIST_COUNT - 1)) sc->qfullmsk |= (1 << qid); #ifndef D4054 sc->sc_tx_timer = 5; #endif /* Kick TX. */ rtwn_write_2(sc, R92C_PCIE_CTRL_REG, (1 << qid)); return (0); } static int rtwn_pci_tx_start_beacon(struct rtwn_softc *sc, struct mbuf *m, uint8_t *tx_desc, int id) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring; struct rtwn_tx_data *data; struct rtwn_tx_desc_common *txd; bus_dma_segment_t segs[1]; int nsegs, error, own; RTWN_ASSERT_LOCKED(sc); KASSERT(id == 0 || id == 1, ("bogus vap id %d\n", id)); ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE]; data = &ring->tx_data[id]; txd = (struct rtwn_tx_desc_common *) ((uint8_t *)ring->desc + id * sc->txdesc_len); bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); own = !!(txd->flags0 & RTWN_FLAGS0_OWN); error = 0; if (!own || txd->pktlen != htole16(m->m_pkthdr.len)) { if (!own) { /* Copy Tx descriptor. */ rtwn_pci_copy_tx_desc(pc, txd, tx_desc); txd->offset = sc->txdesc_len; } else { /* Reload mbuf. */ bus_dmamap_unload(ring->data_dmat, data->map); } error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "can't map beacon (error %d)\n", error); txd->flags0 &= ~RTWN_FLAGS0_OWN; goto end; } txd->pktlen = htole16(m->m_pkthdr.len); rtwn_pci_tx_postsetup(pc, txd, segs); txd->flags0 |= RTWN_FLAGS0_OWN; end: bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); } /* Dump Tx descriptor. */ rtwn_dump_tx_desc(sc, txd); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); return (0); } int rtwn_pci_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, uint8_t *tx_desc, uint8_t type, int id) { int error = 0; RTWN_ASSERT_LOCKED(sc); if (ni == NULL) /* beacon frame */ error = rtwn_pci_tx_start_beacon(sc, m, tx_desc, id); else error = rtwn_pci_tx_start_frame(sc, ni, m, tx_desc, type); return (error); } Index: head/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c =================================================================== --- head/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c (revision 308574) +++ head/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c (revision 308575) @@ -1,267 +1,263 @@ /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2015 Stefan Sperling * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct rtwn_r92c_txpwr r92c_txpwr; void r92ce_attach(struct rtwn_pci_softc *); static void r92ce_postattach(struct rtwn_softc *sc) { struct r92c_softc *rs = sc->sc_priv; struct ieee80211com *ic = &sc->sc_ic; if (!(rs->chip & R92C_CHIP_92C) && rs->board_type == R92C_BOARD_TYPE_HIGHPA) rs->rs_txagc = &rtl8188ru_txagc[0]; else rs->rs_txagc = &rtl8192cu_txagc[0]; if ((rs->chip & (R92C_CHIP_UMC_A_CUT | R92C_CHIP_92C)) == R92C_CHIP_UMC_A_CUT) sc->fwname = "rtwn-rtl8192cfwE"; else sc->fwname = "rtwn-rtl8192cfwE_B"; sc->fwsig = 0x88c; rs->rs_scan_start = ic->ic_scan_start; ic->ic_scan_start = r92c_scan_start; rs->rs_scan_end = ic->ic_scan_end; ic->ic_scan_end = r92c_scan_end; } static void r92ce_set_name(struct rtwn_softc *sc) { struct r92c_softc *rs = sc->sc_priv; if (rs->chip & R92C_CHIP_92C) sc->name = "RTL8192CE"; else sc->name = "RTL8188CE"; } static void r92ce_attach_private(struct rtwn_softc *sc) { struct r92c_softc *rs; rs = malloc(sizeof(struct r92c_softc), M_RTWN_PRIV, M_WAITOK | M_ZERO); rs->rs_txpwr = &r92c_txpwr; rs->rs_set_bw20 = r92c_set_bw20; rs->rs_get_txpower = r92c_get_txpower; rs->rs_set_gain = r92c_set_gain; rs->rs_tx_enable_ampdu = r92c_tx_enable_ampdu; rs->rs_tx_setup_hwseq = r92c_tx_setup_hwseq; rs->rs_tx_setup_macid = r92c_tx_setup_macid; rs->rs_set_name = r92ce_set_name; /* XXX TODO: test with net80211 ratectl! */ #ifndef RTWN_WITHOUT_UCODE rs->rs_c2h_timeout = hz; callout_init_mtx(&rs->rs_c2h_report, &sc->sc_mtx, 0); #endif rs->rf_read_delay[0] = 1000; rs->rf_read_delay[1] = 1000; rs->rf_read_delay[2] = 1000; sc->sc_priv = rs; } static void r92ce_adj_devcaps(struct rtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; - /* XXX TODO: test everything that removed here before enabling. */ - /* XX do NOT enable PMGT until RSVD_PAGE command will not be fixed. */ - ic->ic_caps &= ~( - IEEE80211_C_PMGT /* check null frame / device usability */ - | IEEE80211_C_SWAMSDUTX - | IEEE80211_C_FF - ); - - ic->ic_htcaps = 0; + /* + * XXX do NOT enable PMGT until RSVD_PAGE command + * will not be tested / fixed + HRPWM register must be set too. + */ + ic->ic_caps &= ~IEEE80211_C_PMGT; } void r92ce_attach(struct rtwn_pci_softc *pc) { struct rtwn_softc *sc = &pc->pc_sc; /* PCIe part. */ pc->pc_setup_tx_desc = r92ce_setup_tx_desc; pc->pc_tx_postsetup = r92ce_tx_postsetup; pc->pc_copy_tx_desc = r92ce_copy_tx_desc; pc->pc_enable_intr = r92ce_enable_intr; pc->pc_qmap = 0xf771; pc->tcr = R92C_TCR_CFENDFORM | (1 << 12) | (1 << 13); /* Common part. */ /* RTL8192C* cannot use pairwise keys from first 4 slots */ sc->sc_flags = RTWN_FLAG_CAM_FIXED; sc->sc_start_xfers = r92ce_start_xfers; sc->sc_set_chan = r92c_set_chan; sc->sc_fill_tx_desc = r92c_fill_tx_desc; sc->sc_fill_tx_desc_raw = r92c_fill_tx_desc_raw; sc->sc_fill_tx_desc_null = r92c_fill_tx_desc_null; /* XXX recheck */ sc->sc_dump_tx_desc = r92ce_dump_tx_desc; sc->sc_tx_radiotap_flags = r92c_tx_radiotap_flags; sc->sc_rx_radiotap_flags = r92c_rx_radiotap_flags; sc->sc_get_rssi_cck = r92c_get_rssi_cck; sc->sc_get_rssi_ofdm = r92c_get_rssi_ofdm; sc->sc_classify_intr = r92ce_classify_intr; sc->sc_handle_tx_report = rtwn_nop_softc_uint8_int; sc->sc_handle_c2h_report = rtwn_nop_softc_uint8_int; sc->sc_check_frame = rtwn_nop_int_softc_mbuf; sc->sc_rf_read = r92c_rf_read; sc->sc_rf_write = r92c_rf_write; sc->sc_check_condition = r92c_check_condition; sc->sc_efuse_postread = r92c_efuse_postread; sc->sc_parse_rom = r92c_parse_rom; sc->sc_set_led = r92ce_set_led; sc->sc_power_on = r92ce_power_on; sc->sc_power_off = r92ce_power_off; #ifndef RTWN_WITHOUT_UCODE sc->sc_fw_reset = r92ce_fw_reset; sc->sc_fw_download_enable = r92c_fw_download_enable; #endif sc->sc_set_page_size = r92c_set_page_size; sc->sc_lc_calib = r92c_lc_calib; sc->sc_iq_calib = r92ce_iq_calib; sc->sc_read_chipid_vendor = r92c_read_chipid_vendor; sc->sc_adj_devcaps = r92ce_adj_devcaps; sc->sc_vap_preattach = rtwn_nop_softc_vap; sc->sc_postattach = r92ce_postattach; sc->sc_detach_private = r92c_detach_private; sc->sc_set_media_status = r92c_joinbss_rpt; #ifndef RTWN_WITHOUT_UCODE sc->sc_set_rsvd_page = r92c_set_rsvd_page; sc->sc_set_pwrmode = r92c_set_pwrmode; sc->sc_set_rssi = r92c_set_rssi; #endif sc->sc_beacon_init = r92c_beacon_init; sc->sc_beacon_enable = r92c_beacon_enable; sc->sc_beacon_set_rate = rtwn_nop_void_int; sc->sc_beacon_select = rtwn_nop_softc_int; sc->sc_temp_measure = r92c_temp_measure; sc->sc_temp_read = r92c_temp_read; sc->sc_init_tx_agg = rtwn_nop_softc; sc->sc_init_rx_agg = rtwn_nop_softc; sc->sc_init_ampdu = r92ce_init_ampdu; sc->sc_init_intr = r92ce_init_intr; sc->sc_init_edca = r92ce_init_edca; sc->sc_init_bb = r92ce_init_bb; sc->sc_init_rf = r92c_init_rf; sc->sc_init_antsel = rtwn_nop_softc; sc->sc_post_init = r92ce_post_init; sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc; sc->mac_prog = &rtl8192ce_mac[0]; sc->mac_size = nitems(rtl8192ce_mac); sc->bb_prog = &rtl8192ce_bb[0]; sc->bb_size = nitems(rtl8192ce_bb); sc->agc_prog = &rtl8192ce_agc[0]; sc->agc_size = nitems(rtl8192ce_agc); sc->rf_prog = &rtl8192c_rf[0]; sc->page_count = R92CE_TX_PAGE_COUNT; sc->pktbuf_count = R92C_TXPKTBUF_COUNT; sc->ackto = 0x40; sc->npubqpages = R92CE_PUBQ_NPAGES; sc->nhqpages = R92CE_HPQ_NPAGES; sc->nnqpages = 0; sc->nlqpages = R92CE_LPQ_NPAGES; sc->page_size = R92C_TX_PAGE_SIZE; sc->txdesc_len = sizeof(struct r92ce_tx_desc); sc->efuse_maxlen = R92C_EFUSE_MAX_LEN; sc->efuse_maplen = R92C_EFUSE_MAP_LEN; sc->rx_dma_size = R92C_RX_DMA_BUFFER_SIZE; sc->macid_limit = R92C_MACID_MAX + 1; sc->cam_entry_limit = R92C_CAM_ENTRY_COUNT; sc->fwsize_limit = R92C_MAX_FW_SIZE; sc->temp_delta = R92C_CALIB_THRESHOLD; sc->bcn_status_reg[0] = R92C_TDECTRL; /* * TODO: some additional setup is required * to maintain few beacons at the same time. * * XXX BCNQ1 mechanism is not needed here; move it to the USB module. */ sc->bcn_status_reg[1] = R92C_TDECTRL; sc->rcr = 0; r92ce_attach_private(sc); } Index: head/sys/dev/rtwn/usb/rtwn_usb_rx.c =================================================================== --- head/sys/dev/rtwn/usb/rtwn_usb_rx.c (revision 308574) +++ head/sys/dev/rtwn/usb/rtwn_usb_rx.c (revision 308575) @@ -1,335 +1,336 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include #include #include #include #include #include #include #include #include #include /* for CAM_ALGO_NONE */ #include static struct mbuf * rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct r92c_rx_stat *stat, int totlen) { struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m; uint32_t rxdw0; int pktlen; RTWN_ASSERT_LOCKED(sc); /* Dump Rx descriptor. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n", __func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1), le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4), le32toh(stat->tsf_low)); /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!(sc->sc_flags & RTWN_RUNNING)) return (NULL); rxdw0 = le32toh(stat->rxdw0); if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV"); goto fail; } pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack))) { /* * Should not happen (because of Rx filter setup). */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: frame is too short: %d\n", __func__, pktlen); goto fail; } m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n", __func__); goto fail; } /* Finalize mbuf. */ memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen); m->m_pkthdr.len = m->m_len = totlen; if (rtwn_check_frame(sc, m) != 0) { m_freem(m); goto fail; } return (m); fail: counter_u64_add(ic->ic_ierrors, 1); return (NULL); } static struct mbuf * rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len) { struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); struct r92c_rx_stat *stat; struct mbuf *m, *m0 = NULL; uint32_t rxdw0; int totlen, pktlen, infosz; /* Process packets. */ while (len >= sizeof(*stat)) { stat = (struct r92c_rx_stat *)buf; rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (__predict_false(pktlen == 0)) break; infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (totlen > len) break; if (m0 == NULL) m0 = m = rtwn_rx_copy_to_mbuf(sc, stat, totlen); else { m->m_next = rtwn_rx_copy_to_mbuf(sc, stat, totlen); if (m->m_next != NULL) m = m->m_next; } /* Align next frame. */ totlen = rtwn_usb_align_rx(uc, totlen, len); buf += totlen; len -= totlen; } return (m0); } static struct mbuf * rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb_xfer *xfer, struct rtwn_data *data) { struct rtwn_softc *sc = &uc->uc_sc; struct ieee80211com *ic = &sc->sc_ic; uint8_t *buf; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); if (__predict_false(len < sizeof(struct r92c_rx_stat))) { counter_u64_add(ic->ic_ierrors, 1); return (NULL); } buf = data->buf; switch (rtwn_classify_intr(sc, buf, len)) { case RTWN_RX_DATA: return (rtwn_rxeof(sc, buf, len)); case RTWN_RX_TX_REPORT: if (sc->sc_ratectl != RTWN_RATECTL_NET80211) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s called while ratectl = %d!\n", __func__, sc->sc_ratectl); break; } RTWN_NT_LOCK(sc); rtwn_handle_tx_report(sc, buf, len); RTWN_NT_UNLOCK(sc); #ifdef IEEE80211_SUPPORT_SUPERG /* * NB: this will executed only when 'report' bit is set. */ if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1) rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); #endif break; case RTWN_RX_OTHER: rtwn_handle_c2h_report(sc, buf, len); break; default: /* NOTREACHED */ KASSERT(0, ("unknown Rx classification code")); break; } return (NULL); } static struct ieee80211_node * rtwn_rx_frame(struct rtwn_softc *sc, struct mbuf *m, int8_t *rssi) { struct r92c_rx_stat stat; /* Imitate PCIe layout. */ m_copydata(m, 0, sizeof(struct r92c_rx_stat), (caddr_t)&stat); m_adj(m, sizeof(struct r92c_rx_stat)); return (rtwn_rx_common(sc, m, &stat, rssi)); } void rtwn_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct rtwn_usb_softc *uc = usbd_xfer_softc(xfer); struct rtwn_softc *sc = &uc->uc_sc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m = NULL, *next; struct rtwn_data *data; int8_t nf, rssi; RTWN_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&uc->uc_rx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&uc->uc_rx_active, next); m = rtwn_report_intr(uc, xfer, data); STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&uc->uc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); goto finish; } STAILQ_REMOVE_HEAD(&uc->uc_rx_inactive, next); STAILQ_INSERT_TAIL(&uc->uc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ while (m != NULL) { next = m->m_next; m->m_next = NULL; ni = rtwn_rx_frame(sc, m, &rssi); RTWN_UNLOCK(sc); nf = RTWN_NOISE_FLOOR; if (ni != NULL) { if (ni->ni_flags & IEEE80211_NODE_HT) m->m_flags |= M_AMPDU; (void)ieee80211_input(ni, m, rssi - nf, nf); ieee80211_free_node(ni); } else { (void)ieee80211_input_all(ic, m, rssi - nf, nf); } RTWN_LOCK(sc); m = next; } break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&uc->uc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&uc->uc_rx_active, next); STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } break; } finish: /* Finished receive; age anything left on the FF queue by a little bump */ /* * XXX TODO: just make this a callout timer schedule so we can * flush the FF staging queue if we're approaching idle. */ #ifdef IEEE80211_SUPPORT_SUPERG - if (!(sc->sc_flags & RTWN_FW_LOADED)) + if (!(sc->sc_flags & RTWN_FW_LOADED) || + sc->sc_ratectl != RTWN_RATECTL_NET80211) rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); #endif /* Kick-start more transmit in case we stalled */ rtwn_start(sc); }