aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/rtwn/pci
diff options
context:
space:
mode:
authorAndriy Voskoboinyk <avos@FreeBSD.org>2016-10-17 20:38:24 +0000
committerAndriy Voskoboinyk <avos@FreeBSD.org>2016-10-17 20:38:24 +0000
commit7453645f2a9411a3f9d982b768bcc323f41cf906 (patch)
tree16c1ed3b4792028154ca7701feb649ba12da9951 /sys/dev/rtwn/pci
parenta1a604ca902bbe790abd63576d5127680663f0e1 (diff)
Notes
Diffstat (limited to 'sys/dev/rtwn/pci')
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_attach.c687
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_attach.h48
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_reg.c123
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_reg.h30
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_rx.c293
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_rx.h27
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_tx.c195
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_tx.h25
-rw-r--r--sys/dev/rtwn/pci/rtwn_pci_var.h141
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 */