diff options
| author | Andriy Voskoboinyk <avos@FreeBSD.org> | 2016-10-17 20:38:24 +0000 |
|---|---|---|
| committer | Andriy Voskoboinyk <avos@FreeBSD.org> | 2016-10-17 20:38:24 +0000 |
| commit | 7453645f2a9411a3f9d982b768bcc323f41cf906 (patch) | |
| tree | 16c1ed3b4792028154ca7701feb649ba12da9951 /sys/dev/rtwn/pci | |
| parent | a1a604ca902bbe790abd63576d5127680663f0e1 (diff) | |
Notes
Diffstat (limited to 'sys/dev/rtwn/pci')
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_attach.c | 687 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_attach.h | 48 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_reg.c | 123 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_reg.h | 30 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_rx.c | 293 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_rx.h | 27 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_tx.c | 195 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_tx.h | 25 | ||||
| -rw-r--r-- | sys/dev/rtwn/pci/rtwn_pci_var.h | 141 |
9 files changed, 1569 insertions, 0 deletions
diff --git a/sys/dev/rtwn/pci/rtwn_pci_attach.c b/sys/dev/rtwn/pci/rtwn_pci_attach.c new file mode 100644 index 0000000000000..e48ae81412914 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_attach.c @@ -0,0 +1,687 @@ +/* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/linker.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <net/if.h> +#include <net/ethernet.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/rtwn/if_rtwnvar.h> +#include <dev/rtwn/if_rtwn_nop.h> +#include <dev/rtwn/if_rtwn_debug.h> + +#include <dev/rtwn/pci/rtwn_pci_var.h> + +#include <dev/rtwn/pci/rtwn_pci_attach.h> +#include <dev/rtwn/pci/rtwn_pci_reg.h> +#include <dev/rtwn/pci/rtwn_pci_rx.h> +#include <dev/rtwn/pci/rtwn_pci_tx.h> + +#include <dev/rtwn/rtl8192c/pci/r92ce_reg.h> +#include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> + + +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_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_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); + 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); + 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); + 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); + } + 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_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); + 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_list(struct rtwn_softc *sc, struct ieee80211vap *vap, + int qid) +{ + struct rtwn_vap *uvp = RTWN_VAP(vap); + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; + int i, id; + + id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID); + + for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { + struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; + + if (vap == NULL || (tx_data->ni == NULL && + (tx_data->id == id || id == RTWN_VAP_ID_INVALID)) || + (tx_data->ni != NULL && tx_data->ni->ni_vap == vap)) { + void *tx_desc = + (uint8_t *)tx_ring->desc + sc->txdesc_len * i; + + rtwn_pci_copy_tx_desc(pc, tx_desc, NULL); + + 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_data->ni != NULL) { + ieee80211_free_node(tx_data->ni); + tx_data->ni = NULL; + } + } + } + + bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, + BUS_DMASYNC_POSTWRITE); + + sc->qfullmsk &= ~(1 << qid); + tx_ring->queued = 0; + tx_ring->last = tx_ring->cur = 0; +} + +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_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; +} + +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); diff --git a/sys/dev/rtwn/pci/rtwn_pci_attach.h b/sys/dev/rtwn/pci/rtwn_pci_attach.h new file mode 100644 index 0000000000000..6df5812e4cd2d --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_attach.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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$ + */ + +void r92ce_attach(struct rtwn_pci_softc *); + +enum { + RTWN_CHIP_RTL8192CE, + RTWN_CHIP_MAX_PCI +}; + +struct rtwn_pci_ident { + uint16_t vendor; + uint16_t device; + const char *name; + int chip; +}; + +static const struct rtwn_pci_ident rtwn_pci_ident_table[] = { + { 0x10ec, 0x8176, "Realtek RTL8188CE", RTWN_CHIP_RTL8192CE }, + { 0, 0, NULL, RTWN_CHIP_MAX_PCI } +}; + +typedef void (*chip_pci_attach)(struct rtwn_pci_softc *); + +static const chip_pci_attach rtwn_chip_pci_attach[RTWN_CHIP_MAX_PCI] = { + [RTWN_CHIP_RTL8192CE] = r92ce_attach +}; + +static __inline void +rtwn_pci_attach_private(struct rtwn_pci_softc *pc, int chip) +{ + rtwn_chip_pci_attach[chip](pc); +} diff --git a/sys/dev/rtwn/pci/rtwn_pci_reg.c b/sys/dev/rtwn/pci/rtwn_pci_reg.c new file mode 100644 index 0000000000000..664fb33e6db84 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_reg.c @@ -0,0 +1,123 @@ +/* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org> + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/taskqueue.h> +#include <sys/bus.h> +#include <sys/endian.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/ethernet.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/rtwn/if_rtwnvar.h> + +#include <dev/rtwn/pci/rtwn_pci_var.h> +#include <dev/rtwn/pci/rtwn_pci_reg.h> + + +int +rtwn_pci_write_1(struct rtwn_softc *sc, uint16_t addr, uint8_t val) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + + bus_space_write_1(pc->pc_st, pc->pc_sh, addr, val); + + return (0); +} + +int +rtwn_pci_write_2(struct rtwn_softc *sc, uint16_t addr, uint16_t val) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + + val = htole16(val); + bus_space_write_2(pc->pc_st, pc->pc_sh, addr, val); + + return (0); +} + +int +rtwn_pci_write_4(struct rtwn_softc *sc, uint16_t addr, uint32_t val) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + + val = htole32(val); + bus_space_write_4(pc->pc_st, pc->pc_sh, addr, val); + + return (0); +} + +uint8_t +rtwn_pci_read_1(struct rtwn_softc *sc, uint16_t addr) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + + return (bus_space_read_1(pc->pc_st, pc->pc_sh, addr)); +} + +uint16_t +rtwn_pci_read_2(struct rtwn_softc *sc, uint16_t addr) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + uint16_t val; + + val = bus_space_read_2(pc->pc_st, pc->pc_sh, addr); + return le16toh(val); +} + +uint32_t +rtwn_pci_read_4(struct rtwn_softc *sc, uint16_t addr) +{ + struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + uint32_t val; + + val = bus_space_read_4(pc->pc_st, pc->pc_sh, addr); + return le32toh(val); +} + +void +rtwn_pci_delay(struct rtwn_softc *sc, int usec) +{ + if (usec < 1000) + DELAY(usec); + else { + (void) mtx_sleep(sc, &sc->sc_mtx, 0, "rtwn_pci", + MAX(msecs_to_ticks(usec / 1000), 1)); + } +} diff --git a/sys/dev/rtwn/pci/rtwn_pci_reg.h b/sys/dev/rtwn/pci/rtwn_pci_reg.h new file mode 100644 index 0000000000000..7c900345f46b4 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_reg.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 RTWN_PCI_REG_H +#define RTWN_PCI_REG_H + +int rtwn_pci_write_1(struct rtwn_softc *, uint16_t, uint8_t); +int rtwn_pci_write_2(struct rtwn_softc *, uint16_t, uint16_t); +int rtwn_pci_write_4(struct rtwn_softc *, uint16_t, uint32_t); +uint8_t rtwn_pci_read_1(struct rtwn_softc *, uint16_t); +uint16_t rtwn_pci_read_2(struct rtwn_softc *, uint16_t); +uint32_t rtwn_pci_read_4(struct rtwn_softc *, uint16_t); +void rtwn_pci_delay(struct rtwn_softc *, int); + +#endif /* RTWN_PCI_REG_H */ diff --git a/sys/dev/rtwn/pci/rtwn_pci_rx.c b/sys/dev/rtwn/pci/rtwn_pci_rx.c new file mode 100644 index 0000000000000..4425b4005a1a6 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_rx.c @@ -0,0 +1,293 @@ +/* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org> + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/taskqueue.h> +#include <sys/bus.h> +#include <sys/endian.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/ethernet.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/rtwn/if_rtwnreg.h> +#include <dev/rtwn/if_rtwnvar.h> +#include <dev/rtwn/if_rtwn_debug.h> +#include <dev/rtwn/if_rtwn_rx.h> +#include <dev/rtwn/if_rtwn_tx.h> + +#include <dev/rtwn/pci/rtwn_pci_var.h> +#include <dev/rtwn/pci/rtwn_pci_rx.h> + +#include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> + + +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)) { + 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); + 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); + 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); + 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); + 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, + 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--; + } 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)) + sc->qfullmsk &= ~(1 << qid); + rtwn_start(sc); +} + +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; + } +} + +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) { + RTWN_UNLOCK(sc); + return; + } + + if (status & RTWN_PCI_INTR_RX) + rtwn_pci_rx_done(sc); + + 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); + RTWN_UNLOCK(sc); +} diff --git a/sys/dev/rtwn/pci/rtwn_pci_rx.h b/sys/dev/rtwn/pci/rtwn_pci_rx.h new file mode 100644 index 0000000000000..265d32d8a1178 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_rx.h @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 RTWN_PCI_RX_H +#define RTWN_PCI_RX_H + +void rtwn_pci_dma_map_addr(void *, bus_dma_segment_t *, int, int); +void rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *, + struct r92ce_rx_stat *, bus_addr_t, size_t, int); +void rtwn_pci_intr(void *); + +#endif /* RTWN_PCI_RX_H */ diff --git a/sys/dev/rtwn/pci/rtwn_pci_tx.c b/sys/dev/rtwn/pci/rtwn_pci_tx.c new file mode 100644 index 0000000000000..c1da8f6485546 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_tx.c @@ -0,0 +1,195 @@ +/* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org> + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/taskqueue.h> +#include <sys/bus.h> +#include <sys/endian.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/ethernet.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/rtwn/if_rtwnreg.h> +#include <dev/rtwn/if_rtwnvar.h> +#include <dev/rtwn/if_rtwn_debug.h> + +#include <dev/rtwn/pci/rtwn_pci_var.h> +#include <dev/rtwn/pci/rtwn_pci_tx.h> + +#include <dev/rtwn/rtl8192c/pci/r92ce_reg.h> + + +static int +rtwn_pci_tx_start_common(struct rtwn_softc *sc, struct ieee80211_node *ni, + struct mbuf *m, uint8_t *tx_desc, uint8_t type, 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]; + 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_VO_QUEUE; + break; + default: + qid = M_WME_GETAC(m); + break; + } + + if (ni == NULL) /* beacon frame */ + qid = RTWN_PCI_BEACON_QUEUE; + + 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); + 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; + data->id = id; + + ring->cur = (ring->cur + 1) % RTWN_PCI_TX_LIST_COUNT; + + if (qid != RTWN_PCI_BEACON_QUEUE) { + 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); +} + +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; + + if (ni == NULL) { /* beacon frame */ + m = m_dup(m, M_NOWAIT); + if (__predict_false(m == NULL)) { + device_printf(sc->sc_dev, + "%s: could not copy beacon frame\n", __func__); + return (ENOMEM); + } + + error = rtwn_pci_tx_start_common(sc, ni, m, tx_desc, type, id); + if (error != 0) + m_freem(m); + } else + error = rtwn_pci_tx_start_common(sc, ni, m, tx_desc, type, id); + + return (error); +} diff --git a/sys/dev/rtwn/pci/rtwn_pci_tx.h b/sys/dev/rtwn/pci/rtwn_pci_tx.h new file mode 100644 index 0000000000000..9b9d2e33693ef --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_tx.h @@ -0,0 +1,25 @@ +/*- + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 RTWN_PCI_TX_H +#define RTWN_PCI_TX_H + +int rtwn_pci_tx_start(struct rtwn_softc *, struct ieee80211_node *, + struct mbuf *, uint8_t *, uint8_t, int); + +#endif /* RTWN_PCI_TX_H */ diff --git a/sys/dev/rtwn/pci/rtwn_pci_var.h b/sys/dev/rtwn/pci/rtwn_pci_var.h new file mode 100644 index 0000000000000..4a6d54c21da80 --- /dev/null +++ b/sys/dev/rtwn/pci/rtwn_pci_var.h @@ -0,0 +1,141 @@ +/* $OpenBSD: if_rtwnreg.h,v 1.3 2015/06/14 08:02:47 stsp Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org> + * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * 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 RTWN_PCI_VAR_H +#define RTWN_PCI_VAR_H + +#include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> + + +#define RTWN_PCI_RX_LIST_COUNT 256 +#define RTWN_PCI_TX_LIST_COUNT 256 + +struct rtwn_rx_data { + bus_dmamap_t map; + struct mbuf *m; + bus_addr_t paddr; +}; + +struct rtwn_rx_ring { + struct r92ce_rx_stat *desc; + bus_addr_t paddr; + bus_dma_tag_t desc_dmat; + bus_dmamap_t desc_map; + bus_dma_tag_t data_dmat; + bus_dma_segment_t seg; + struct rtwn_rx_data rx_data[RTWN_PCI_RX_LIST_COUNT]; + int cur; +}; + +struct rtwn_tx_data { + bus_dmamap_t map; + struct mbuf *m; + struct ieee80211_node *ni; + uint8_t id; +}; + +struct rtwn_tx_ring { + bus_addr_t paddr; + bus_dma_tag_t desc_dmat; + bus_dmamap_t desc_map; + bus_dma_tag_t data_dmat; + bus_dma_segment_t seg; + void *desc; + struct rtwn_tx_data tx_data[RTWN_PCI_TX_LIST_COUNT]; + int queued; + int cur; + int last; +}; + +/* + * TX queue indices. + */ +enum { + RTWN_PCI_BK_QUEUE, + RTWN_PCI_BE_QUEUE, + RTWN_PCI_VI_QUEUE, + RTWN_PCI_VO_QUEUE, + RTWN_PCI_BEACON_QUEUE, + RTWN_PCI_TXCMD_QUEUE, + RTWN_PCI_MGNT_QUEUE, + RTWN_PCI_HIGH_QUEUE, + RTWN_PCI_HCCA_QUEUE, + RTWN_PCI_NTXQUEUES +}; + +/* + * Interrupt events. + */ +enum { + RTWN_PCI_INTR_RX_ERROR = 0x00000001, + RTWN_PCI_INTR_RX_OVERFLOW = 0x00000002, + RTWN_PCI_INTR_RX_DESC_UNAVAIL = 0x00000004, + RTWN_PCI_INTR_RX_DONE = 0x00000008, + RTWN_PCI_INTR_TX_ERROR = 0x00000010, + RTWN_PCI_INTR_TX_OVERFLOW = 0x00000020, + RTWN_PCI_INTR_TX_REPORT = 0x00000040, + RTWN_PCI_INTR_PS_TIMEOUT = 0x00000080 +}; + +/* Shortcuts */ +/* Vendor driver treats RX errors like ROK... */ +#define RTWN_PCI_INTR_RX \ + (RTWN_PCI_INTR_RX_OVERFLOW | RTWN_PCI_INTR_RX_DESC_UNAVAIL | \ + RTWN_PCI_INTR_RX_DONE) + + +struct rtwn_pci_softc { + struct rtwn_softc pc_sc; /* must be the first */ + + struct resource *irq; + struct resource *mem; + bus_space_tag_t pc_st; + bus_space_handle_t pc_sh; + void *pc_ih; + bus_size_t pc_mapsize; + + struct rtwn_rx_ring rx_ring; + struct rtwn_tx_ring tx_ring[RTWN_PCI_NTXQUEUES]; + + /* must be set by the driver. */ + uint16_t pc_qmap; + uint32_t tcr; + + void (*pc_setup_tx_desc)(struct rtwn_pci_softc *, + void *, uint32_t); + void (*pc_tx_postsetup)(struct rtwn_pci_softc *, + void *, bus_dma_segment_t *); + void (*pc_copy_tx_desc)(void *, const void *); + void (*pc_enable_intr)(struct rtwn_pci_softc *); +}; +#define RTWN_PCI_SOFTC(sc) ((struct rtwn_pci_softc *)(sc)) + +#define rtwn_pci_setup_tx_desc(_pc, _desc, _addr) \ + (((_pc)->pc_setup_tx_desc)((_pc), (_desc), (_addr))) +#define rtwn_pci_tx_postsetup(_pc, _txd, _segs) \ + (((_pc)->pc_tx_postsetup)((_pc), (_txd), (_segs))) +#define rtwn_pci_copy_tx_desc(_pc, _dest, _src) \ + (((_pc)->pc_copy_tx_desc)((_dest), (_src))) +#define rtwn_pci_enable_intr(_pc) \ + (((_pc)->pc_enable_intr)((_pc))) + +#endif /* RTWN_PCI_VAR_H */ |
