aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/rtwn/usb
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/usb
parenta1a604ca902bbe790abd63576d5127680663f0e1 (diff)
downloadsrc-7453645f2a9411a3f9d982b768bcc323f41cf906.tar.gz
src-7453645f2a9411a3f9d982b768bcc323f41cf906.zip
rtwn(4), urtwn(4): merge common code, add support for 11ac devices.
All devices: - add support for rate adaptation via ieee80211_amrr(9); - use short preamble for transmitted frames when needed; - multi-bss support: * for RTL8821AU: 2 VAPs at the same time; * other: 1 any VAP + 1 sta VAP. RTL8188CE: - fix IQ calibration bug (reason of significant speed degradation); - add h/w crypto acceleration support. USB: - A-MPDU Tx support; - short GI support; Other: - add support for RTL8812AU / RTL8821AU chipsets (a/b/g/n only; no ac yet); - split merged code into subparts: * bus glue (usb/*, pci/*, rtl*/usb/*, rtl*/pci/*) * common (if_rtwn*) * chip-specific (rtl*/*) - various other bugfixes. Due to code reorganization, module names / requirements were changed too: urtwn urtwnfw -> rtwn rtwn_usb rtwnfw rtwn rtwnfw -> rtwn rtwn_pci rtwnfw Tested with RTL8188CE, RTL8188CUS, RTL8188EU and RTL8821AU. Tested by: kevlo, garga, Peter Garshtja <peter.garshtja@ambient-md.com>, Kevin McAleavey <kevin.mcaleavey@knosproject.com>, Ilias-Dimitrios Vrachnis <id@vrachnis.com>, <otacilio.neto@bsd.com.br> Relnotes: yes
Notes
Notes: svn path=/head/; revision=307529
Diffstat (limited to 'sys/dev/rtwn/usb')
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_attach.c433
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_attach.h159
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_ep.c253
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_ep.h25
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_reg.c179
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_reg.h45
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_rx.c335
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_rx.h24
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_tx.c282
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_tx.h26
-rw-r--r--sys/dev/rtwn/usb/rtwn_usb_var.h74
11 files changed, 1835 insertions, 0 deletions
diff --git a/sys/dev/rtwn/usb/rtwn_usb_attach.c b/sys/dev/rtwn/usb/rtwn_usb_attach.c
new file mode 100644
index 000000000000..a47797b56af7
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_attach.c
@@ -0,0 +1,433 @@
+/* $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) 2015-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 <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include "usbdevs.h"
+
+#include <dev/rtwn/if_rtwnvar.h>
+#include <dev/rtwn/if_rtwn_nop.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+
+#include <dev/rtwn/usb/rtwn_usb_attach.h>
+#include <dev/rtwn/usb/rtwn_usb_ep.h>
+#include <dev/rtwn/usb/rtwn_usb_reg.h>
+#include <dev/rtwn/usb/rtwn_usb_tx.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+static device_probe_t rtwn_usb_match;
+static device_attach_t rtwn_usb_attach;
+static device_detach_t rtwn_usb_detach;
+static device_suspend_t rtwn_usb_suspend;
+static device_resume_t rtwn_usb_resume;
+
+static int rtwn_usb_alloc_list(struct rtwn_softc *,
+ struct rtwn_data[], int, int);
+static int rtwn_usb_alloc_rx_list(struct rtwn_softc *);
+static int rtwn_usb_alloc_tx_list(struct rtwn_softc *);
+static void rtwn_usb_free_list(struct rtwn_softc *,
+ struct rtwn_data data[], int);
+static void rtwn_usb_free_rx_list(struct rtwn_softc *);
+static void rtwn_usb_free_tx_list(struct rtwn_softc *);
+static void rtwn_usb_reset_lists(struct rtwn_softc *,
+ struct ieee80211vap *);
+static void rtwn_usb_reset_tx_list(struct rtwn_usb_softc *,
+ rtwn_datahead *, struct ieee80211vap *);
+static void rtwn_usb_start_xfers(struct rtwn_softc *);
+static void rtwn_usb_abort_xfers(struct rtwn_softc *);
+static int rtwn_usb_fw_write_block(struct rtwn_softc *,
+ const uint8_t *, uint16_t, int);
+static void rtwn_usb_attach_methods(struct rtwn_softc *);
+
+#define RTWN_CONFIG_INDEX 0
+
+
+static int
+rtwn_usb_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != RTWN_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RTWN_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(rtwn_devs, sizeof(rtwn_devs), uaa));
+}
+
+static int
+rtwn_usb_alloc_list(struct rtwn_softc *sc, struct rtwn_data data[],
+ int ndata, int maxsz)
+{
+ int i, error;
+
+ for (i = 0; i < ndata; i++) {
+ struct rtwn_data *dp = &data[i];
+ dp->m = NULL;
+ dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
+ if (dp->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ dp->ni = NULL;
+ }
+
+ return (0);
+fail:
+ rtwn_usb_free_list(sc, data, ndata);
+ return (error);
+}
+
+static int
+rtwn_usb_alloc_rx_list(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ int error, i;
+
+ error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT,
+ RTWN_RXBUFSZ);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&uc->uc_rx_active);
+ STAILQ_INIT(&uc->uc_rx_inactive);
+
+ for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++)
+ STAILQ_INSERT_HEAD(&uc->uc_rx_inactive, &uc->uc_rx[i], next);
+
+ return (0);
+}
+
+static int
+rtwn_usb_alloc_tx_list(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ int error, i;
+
+ error = rtwn_usb_alloc_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT,
+ RTWN_TXBUFSZ);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&uc->uc_tx_active);
+ STAILQ_INIT(&uc->uc_tx_inactive);
+ STAILQ_INIT(&uc->uc_tx_pending);
+
+ for (i = 0; i < RTWN_USB_TX_LIST_COUNT; i++)
+ STAILQ_INSERT_HEAD(&uc->uc_tx_inactive, &uc->uc_tx[i], next);
+
+ return (0);
+}
+
+static void
+rtwn_usb_free_list(struct rtwn_softc *sc, struct rtwn_data data[], int ndata)
+{
+ int i;
+
+ for (i = 0; i < ndata; i++) {
+ struct rtwn_data *dp = &data[i];
+
+ if (dp->buf != NULL) {
+ free(dp->buf, M_USBDEV);
+ dp->buf = NULL;
+ }
+ if (dp->ni != NULL) {
+ ieee80211_free_node(dp->ni);
+ dp->ni = NULL;
+ }
+ if (dp->m != NULL) {
+ m_freem(dp->m);
+ dp->m = NULL;
+ }
+ }
+}
+
+static void
+rtwn_usb_free_rx_list(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+
+ rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT);
+
+ STAILQ_INIT(&uc->uc_rx_active);
+ STAILQ_INIT(&uc->uc_rx_inactive);
+}
+
+static void
+rtwn_usb_free_tx_list(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+
+ rtwn_usb_free_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT);
+
+ STAILQ_INIT(&uc->uc_tx_active);
+ STAILQ_INIT(&uc->uc_tx_inactive);
+ STAILQ_INIT(&uc->uc_tx_pending);
+}
+
+static void
+rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap);
+ rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap);
+ if (vap == NULL)
+ sc->qfullmsk = 0;
+}
+
+static void
+rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc,
+ rtwn_datahead *head, struct ieee80211vap *vap)
+{
+ struct rtwn_vap *uvp = RTWN_VAP(vap);
+ struct rtwn_data *dp, *tmp;
+ int id;
+
+ id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID);
+
+ STAILQ_FOREACH_SAFE(dp, head, next, tmp) {
+ if (vap == NULL || (dp->ni == NULL &&
+ (dp->id == id || id == RTWN_VAP_ID_INVALID)) ||
+ (dp->ni != NULL && dp->ni->ni_vap == vap)) {
+ if (dp->ni != NULL) {
+ ieee80211_free_node(dp->ni);
+ dp->ni = NULL;
+ }
+
+ if (dp->m != NULL) {
+ m_freem(dp->m);
+ dp->m = NULL;
+ }
+
+ STAILQ_REMOVE(head, dp, rtwn_data, next);
+ STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, dp, next);
+ }
+ }
+}
+
+static void
+rtwn_usb_start_xfers(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+
+ usbd_transfer_start(uc->uc_xfer[RTWN_BULK_RX]);
+}
+
+static void
+rtwn_usb_abort_xfers(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ int i;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ /* abort any pending transfers */
+ for (i = 0; i < RTWN_N_TRANSFER; i++)
+ usbd_transfer_stop(uc->uc_xfer[i]);
+}
+
+static int
+rtwn_usb_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf,
+ uint16_t reg, int mlen)
+{
+ int error;
+
+ /* XXX fix this deconst */
+ error = rtwn_usb_write_region_1(sc, reg, __DECONST(uint8_t *, buf),
+ mlen);
+
+ return (error);
+}
+
+static void
+rtwn_usb_drop_incorrect_tx(struct rtwn_softc *sc)
+{
+
+ rtwn_setbits_1_shift(sc, R92C_TXDMA_OFFSET_CHK, 0,
+ R92C_TXDMA_OFFSET_DROP_DATA_EN, 1);
+}
+
+static void
+rtwn_usb_attach_methods(struct rtwn_softc *sc)
+{
+ sc->sc_write_1 = rtwn_usb_write_1;
+ sc->sc_write_2 = rtwn_usb_write_2;
+ sc->sc_write_4 = rtwn_usb_write_4;
+ sc->sc_read_1 = rtwn_usb_read_1;
+ sc->sc_read_2 = rtwn_usb_read_2;
+ sc->sc_read_4 = rtwn_usb_read_4;
+ sc->sc_delay = rtwn_usb_delay;
+ sc->sc_tx_start = rtwn_usb_tx_start;
+ sc->sc_start_xfers = rtwn_usb_start_xfers;
+ sc->sc_reset_lists = rtwn_usb_reset_lists;
+ sc->sc_abort_xfers = rtwn_usb_abort_xfers;
+ sc->sc_fw_write_block = rtwn_usb_fw_write_block;
+ sc->sc_get_qmap = rtwn_usb_get_qmap;
+ sc->sc_set_desc_addr = rtwn_nop_softc;
+ sc->sc_drop_incorrect_tx = rtwn_usb_drop_incorrect_tx;
+}
+
+static int
+rtwn_usb_attach(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct rtwn_usb_softc *uc = device_get_softc(self);
+ struct rtwn_softc *sc = &uc->uc_sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ int error;
+
+ device_set_usb_desc(self);
+ uc->uc_udev = uaa->device;
+ sc->sc_dev = self;
+ ic->ic_name = device_get_nameunit(self);
+
+ /* Need to be initialized early. */
+ rtwn_sysctlattach(sc);
+ mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF);
+
+ rtwn_usb_attach_methods(sc);
+ rtwn_usb_attach_private(uc, USB_GET_DRIVER_INFO(uaa));
+
+ error = rtwn_usb_setup_endpoints(uc);
+ if (error != 0)
+ goto detach;
+
+ /* Allocate Tx/Rx buffers. */
+ error = rtwn_usb_alloc_rx_list(sc);
+ if (error != 0)
+ goto detach;
+
+ error = rtwn_usb_alloc_tx_list(sc);
+ if (error != 0)
+ goto detach;
+
+ /* Generic attach. */
+ error = rtwn_attach(sc);
+ if (error != 0)
+ goto detach;
+
+ return (0);
+
+detach:
+ rtwn_usb_detach(self); /* failure */
+ return (ENXIO);
+}
+
+static int
+rtwn_usb_detach(device_t self)
+{
+ struct rtwn_usb_softc *uc = device_get_softc(self);
+ struct rtwn_softc *sc = &uc->uc_sc;
+
+ /* Generic detach. */
+ rtwn_detach(sc);
+
+ /* Free Tx/Rx buffers. */
+ rtwn_usb_free_tx_list(sc);
+ rtwn_usb_free_rx_list(sc);
+
+ /* Detach all USB transfers. */
+ usbd_transfer_unsetup(uc->uc_xfer, RTWN_N_TRANSFER);
+
+ rtwn_detach_private(sc);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+rtwn_usb_suspend(device_t self)
+{
+ struct rtwn_usb_softc *uc = device_get_softc(self);
+
+ rtwn_suspend(&uc->uc_sc);
+
+ return (0);
+}
+
+static int
+rtwn_usb_resume(device_t self)
+{
+ struct rtwn_usb_softc *uc = device_get_softc(self);
+
+ rtwn_resume(&uc->uc_sc);
+
+ return (0);
+}
+
+static device_method_t rtwn_usb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rtwn_usb_match),
+ DEVMETHOD(device_attach, rtwn_usb_attach),
+ DEVMETHOD(device_detach, rtwn_usb_detach),
+ DEVMETHOD(device_suspend, rtwn_usb_suspend),
+ DEVMETHOD(device_resume, rtwn_usb_resume),
+
+ DEVMETHOD_END
+};
+
+static driver_t rtwn_usb_driver = {
+ "rtwn",
+ rtwn_usb_methods,
+ sizeof(struct rtwn_usb_softc)
+};
+
+static devclass_t rtwn_usb_devclass;
+
+DRIVER_MODULE(rtwn_usb, uhub, rtwn_usb_driver, rtwn_usb_devclass, NULL, NULL);
+MODULE_VERSION(rtwn_usb, 1);
+MODULE_DEPEND(rtwn_usb, usb, 1, 1, 1);
+MODULE_DEPEND(rtwn_usb, wlan, 1, 1, 1);
+MODULE_DEPEND(rtwn_usb, rtwn, 2, 2, 2);
+USB_PNP_HOST_INFO(rtwn_devs);
diff --git a/sys/dev/rtwn/usb/rtwn_usb_attach.h b/sys/dev/rtwn/usb/rtwn_usb_attach.h
new file mode 100644
index 000000000000..6bfed2e43a07
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_attach.h
@@ -0,0 +1,159 @@
+/* $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) 2015-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 r92cu_attach(struct rtwn_usb_softc *);
+void r88eu_attach(struct rtwn_usb_softc *);
+void r12au_attach(struct rtwn_usb_softc *);
+void r21au_attach(struct rtwn_usb_softc *);
+
+enum {
+ RTWN_CHIP_RTL8192CU,
+ RTWN_CHIP_RTL8188EU,
+ RTWN_CHIP_RTL8812AU,
+ RTWN_CHIP_RTL8821AU,
+ RTWN_CHIP_MAX_USB
+};
+
+/* various supported device vendors/products */
+static const STRUCT_USB_HOST_ID rtwn_devs[] = {
+ /* RTL8188CE-VAU/RTL8188CUS/RTL8188RU/RTL8192CU */
+#define RTWN_RTL8192CU_DEV(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8192CU) }
+ RTWN_RTL8192CU_DEV(ABOCOM, RTL8188CU_1),
+ RTWN_RTL8192CU_DEV(ABOCOM, RTL8188CU_2),
+ RTWN_RTL8192CU_DEV(ABOCOM, RTL8192CU),
+ RTWN_RTL8192CU_DEV(ASUS, RTL8192CU),
+ RTWN_RTL8192CU_DEV(ASUS, USBN10NANO),
+ RTWN_RTL8192CU_DEV(AZUREWAVE, RTL8188CE_1),
+ RTWN_RTL8192CU_DEV(AZUREWAVE, RTL8188CE_2),
+ RTWN_RTL8192CU_DEV(AZUREWAVE, RTL8188CU),
+ RTWN_RTL8192CU_DEV(BELKIN, F7D2102),
+ RTWN_RTL8192CU_DEV(BELKIN, RTL8188CU),
+ RTWN_RTL8192CU_DEV(BELKIN, RTL8192CU),
+ RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_1),
+ RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_2),
+ RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_3),
+ RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_4),
+ RTWN_RTL8192CU_DEV(CHICONY, RTL8188CUS_5),
+ RTWN_RTL8192CU_DEV(COREGA, RTL8192CU),
+ RTWN_RTL8192CU_DEV(DLINK, RTL8188CU),
+ RTWN_RTL8192CU_DEV(DLINK, RTL8192CU_1),
+ RTWN_RTL8192CU_DEV(DLINK, RTL8192CU_2),
+ RTWN_RTL8192CU_DEV(DLINK, RTL8192CU_3),
+ RTWN_RTL8192CU_DEV(DLINK, DWA131B),
+ RTWN_RTL8192CU_DEV(EDIMAX, EW7811UN),
+ RTWN_RTL8192CU_DEV(EDIMAX, RTL8192CU),
+ RTWN_RTL8192CU_DEV(FEIXUN, RTL8188CU),
+ RTWN_RTL8192CU_DEV(FEIXUN, RTL8192CU),
+ RTWN_RTL8192CU_DEV(GUILLEMOT, HWNUP150),
+ RTWN_RTL8192CU_DEV(HAWKING, RTL8192CU),
+ RTWN_RTL8192CU_DEV(HP3, RTL8188CU),
+ RTWN_RTL8192CU_DEV(NETGEAR, WNA1000M),
+ RTWN_RTL8192CU_DEV(NETGEAR, RTL8192CU),
+ RTWN_RTL8192CU_DEV(NETGEAR4, RTL8188CU),
+ RTWN_RTL8192CU_DEV(NOVATECH, RTL8188CU),
+ RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_1),
+ RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_2),
+ RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_3),
+ RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CU_4),
+ RTWN_RTL8192CU_DEV(PLANEX2, RTL8188CUS),
+ RTWN_RTL8192CU_DEV(PLANEX2, RTL8192CU),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CE_0),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CE_1),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CTV),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_0),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_1),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_2),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_3),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CU_COMBO),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188CUS),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188RU_1),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188RU_2),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8188RU_3),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8191CU),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8192CE),
+ RTWN_RTL8192CU_DEV(REALTEK, RTL8192CU),
+ RTWN_RTL8192CU_DEV(SITECOMEU, RTL8188CU_1),
+ RTWN_RTL8192CU_DEV(SITECOMEU, RTL8188CU_2),
+ RTWN_RTL8192CU_DEV(SITECOMEU, RTL8192CU),
+ RTWN_RTL8192CU_DEV(TRENDNET, RTL8188CU),
+ RTWN_RTL8192CU_DEV(TRENDNET, RTL8192CU),
+ RTWN_RTL8192CU_DEV(ZYXEL, RTL8192CU),
+#undef RTWN_RTL8192CU_DEV
+
+ /* RTL8188EU */
+#define RTWN_RTL8188EU_DEV(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8188EU) }
+ RTWN_RTL8188EU_DEV(ABOCOM, RTL8188EU),
+ RTWN_RTL8188EU_DEV(DLINK, DWA123D1),
+ RTWN_RTL8188EU_DEV(DLINK, DWA125D1),
+ RTWN_RTL8188EU_DEV(ELECOM, WDC150SU2M),
+ RTWN_RTL8188EU_DEV(REALTEK, RTL8188ETV),
+ RTWN_RTL8188EU_DEV(REALTEK, RTL8188EU),
+#undef RTWN_RTL8188EU_DEV
+
+ /* RTL8812AU */
+#define RTWN_RTL8812AU_DEV(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8812AU) }
+ RTWN_RTL8812AU_DEV(ASUS, USBAC56),
+ RTWN_RTL8812AU_DEV(CISCOLINKSYS, WUSB6300),
+ RTWN_RTL8812AU_DEV(DLINK, DWA182C1),
+ RTWN_RTL8812AU_DEV(DLINK, DWA180A1),
+ RTWN_RTL8812AU_DEV(EDIMAX, EW7822UAC),
+ RTWN_RTL8812AU_DEV(IODATA, WNAC867U),
+ RTWN_RTL8812AU_DEV(MELCO, WIU3866D),
+ RTWN_RTL8812AU_DEV(NEC, WL900U),
+ RTWN_RTL8812AU_DEV(PLANEX2, GW900D),
+ RTWN_RTL8812AU_DEV(SENAO, EUB1200AC),
+ RTWN_RTL8812AU_DEV(SITECOMEU, WLA7100),
+ RTWN_RTL8812AU_DEV(TPLINK, T4U),
+ RTWN_RTL8812AU_DEV(TRENDNET, TEW805UB),
+ RTWN_RTL8812AU_DEV(ZYXEL, NWD6605),
+#undef RTWN_RTL8812AU_DEV
+
+ /* RTL8821AU */
+#define RTWN_RTL8821AU_DEV(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RTWN_CHIP_RTL8821AU) }
+ RTWN_RTL8821AU_DEV(DLINK, DWA171A1),
+ RTWN_RTL8821AU_DEV(DLINK, DWA172A1),
+ RTWN_RTL8821AU_DEV(EDIMAX, EW7811UTC_1),
+ RTWN_RTL8821AU_DEV(EDIMAX, EW7811UTC_2),
+ RTWN_RTL8821AU_DEV(HAWKING, HD65U),
+ RTWN_RTL8821AU_DEV(MELCO, WIU2433DM),
+ RTWN_RTL8821AU_DEV(NETGEAR, A6100)
+#undef RTWN_RTL8821AU_DEV
+};
+
+typedef void (*chip_usb_attach)(struct rtwn_usb_softc *);
+
+static const chip_usb_attach rtwn_chip_usb_attach[RTWN_CHIP_MAX_USB] = {
+ [RTWN_CHIP_RTL8192CU] = r92cu_attach,
+ [RTWN_CHIP_RTL8188EU] = r88eu_attach,
+ [RTWN_CHIP_RTL8812AU] = r12au_attach,
+ [RTWN_CHIP_RTL8821AU] = r21au_attach
+};
+
+static __inline void
+rtwn_usb_attach_private(struct rtwn_usb_softc *uc, int chip)
+{
+ rtwn_chip_usb_attach[chip](uc);
+}
diff --git a/sys/dev/rtwn/usb/rtwn_usb_ep.c b/sys/dev/rtwn/usb/rtwn_usb_ep.c
new file mode 100644
index 000000000000..8694e33dd2d1
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_ep.c
@@ -0,0 +1,253 @@
+/* $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) 2015-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/condvar.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 <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_device.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_debug.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+#include <dev/rtwn/usb/rtwn_usb_ep.h>
+#include <dev/rtwn/usb/rtwn_usb_rx.h>
+#include <dev/rtwn/usb/rtwn_usb_tx.h>
+
+#include <dev/rtwn/rtl8192c/usb/r92cu_reg.h>
+
+
+static struct usb_config rtwn_config[RTWN_N_TRANSFER] = {
+ [RTWN_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = RTWN_RXBUFSZ,
+ .flags = {
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = rtwn_bulk_rx_callback,
+ },
+ [RTWN_BULK_TX_BE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = RTWN_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .force_short_xfer = 1,
+ },
+ .callback = rtwn_bulk_tx_callback,
+ .timeout = RTWN_TX_TIMEOUT, /* ms */
+ },
+ [RTWN_BULK_TX_BK] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = RTWN_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .force_short_xfer = 1,
+ },
+ .callback = rtwn_bulk_tx_callback,
+ .timeout = RTWN_TX_TIMEOUT, /* ms */
+ },
+ [RTWN_BULK_TX_VI] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = RTWN_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .force_short_xfer = 1
+ },
+ .callback = rtwn_bulk_tx_callback,
+ .timeout = RTWN_TX_TIMEOUT, /* ms */
+ },
+ [RTWN_BULK_TX_VO] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = RTWN_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .force_short_xfer = 1
+ },
+ .callback = rtwn_bulk_tx_callback,
+ .timeout = RTWN_TX_TIMEOUT, /* ms */
+ },
+};
+
+static void
+rtwn_usb_setup_queues(struct rtwn_usb_softc *uc)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+ int hasnq, haslq, nqueues, nqpages, nrempages;
+
+ /* Get Tx queues to USB endpoints mapping. */
+ hasnq = haslq = 0;
+ switch (uc->ntx) {
+ case 4:
+ case 3:
+ haslq = 1;
+ /* FALLTHROUGH */
+ case 2:
+ hasnq = 1;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ nqueues = 1 + hasnq + haslq;
+
+ /* Get the number of pages for each queue. */
+ nqpages = (sc->page_count - sc->npubqpages) / nqueues;
+
+ /*
+ * The remaining pages are assigned to the high priority
+ * queue.
+ */
+ nrempages = (sc->page_count - sc->npubqpages) % nqueues;
+
+ sc->nhqpages = nqpages + nrempages;
+ sc->nnqpages = (hasnq ? nqpages : 0);
+ sc->nlqpages = (haslq ? nqpages : 0);
+}
+
+int
+rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+ const uint8_t iface_index = RTWN_IFACE_INDEX;
+ struct usb_endpoint *ep, *ep_end;
+ uint8_t addr[RTWN_MAX_EPOUT];
+ int error;
+
+ /* Determine the number of bulk-out pipes. */
+ uc->ntx = 0;
+ ep = uc->uc_udev->endpoints;
+ ep_end = uc->uc_udev->endpoints + uc->uc_udev->endpoints_max;
+ for (; ep != ep_end; ep++) {
+ uint8_t eaddr;
+
+ if ((ep->edesc == NULL) || (ep->iface_index != iface_index))
+ continue;
+
+ eaddr = ep->edesc->bEndpointAddress;
+ RTWN_DPRINTF(sc, RTWN_DEBUG_USB,
+ "%s: endpoint: addr %u, direction %s\n", __func__,
+ UE_GET_ADDR(eaddr), UE_GET_DIR(eaddr) == UE_DIR_OUT ?
+ "output" : "input");
+
+ if (UE_GET_DIR(eaddr) == UE_DIR_OUT) {
+ if (uc->ntx == RTWN_MAX_EPOUT)
+ break;
+
+ addr[uc->ntx++] = UE_GET_ADDR(eaddr);
+ }
+ }
+ if (uc->ntx == 0 || uc->ntx > RTWN_MAX_EPOUT) {
+ device_printf(sc->sc_dev,
+ "%s: invalid number of Tx bulk pipes (%d)\n", __func__,
+ uc->ntx);
+ return (EINVAL);
+ }
+
+ /* NB: keep in sync with rtwn_dma_init(). */
+ rtwn_config[RTWN_BULK_TX_VO].endpoint = addr[0];
+ switch (uc->ntx) {
+ case 4:
+ case 3:
+ rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[2];
+ rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[2];
+ rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[1];
+ break;
+ case 2:
+ rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[1];
+ rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[1];
+ rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[0];
+ break;
+ case 1:
+ rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[0];
+ rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[0];
+ rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[0];
+ break;
+ default:
+ KASSERT(0, ("unhandled number of endpoints %d\n", uc->ntx));
+ break;
+ }
+
+ error = usbd_transfer_setup(uc->uc_udev, &iface_index,
+ uc->uc_xfer, rtwn_config, RTWN_N_TRANSFER, uc, &sc->sc_mtx);
+ if (error) {
+ device_printf(sc->sc_dev, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ return (error);
+ }
+
+ /* Assign pages for each queue (if not done). */
+ if (sc->nhqpages == 0 && sc->nnqpages == 0 && sc->nlqpages == 0)
+ rtwn_usb_setup_queues(uc);
+
+ return (0);
+}
+
+uint16_t
+rtwn_usb_get_qmap(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+
+ switch (uc->ntx) {
+ case 1:
+ return (R92C_TRXDMA_CTRL_QMAP_HQ);
+ case 2:
+ return (R92C_TRXDMA_CTRL_QMAP_HQ_NQ);
+ default:
+ return (R92C_TRXDMA_CTRL_QMAP_3EP);
+ }
+}
diff --git a/sys/dev/rtwn/usb/rtwn_usb_ep.h b/sys/dev/rtwn/usb/rtwn_usb_ep.h
new file mode 100644
index 000000000000..0cdd738e1794
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_ep.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 IF_RTWN_EP_H
+#define IF_RTWN_EP_H
+
+int rtwn_usb_setup_endpoints(struct rtwn_usb_softc *);
+uint16_t rtwn_usb_get_qmap(struct rtwn_softc *);
+
+#endif /* IF_RTWN_EP_H */
diff --git a/sys/dev/rtwn/usb/rtwn_usb_reg.c b/sys/dev/rtwn/usb/rtwn_usb_reg.c
new file mode 100644
index 000000000000..2091c6ac6444
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_reg.c
@@ -0,0 +1,179 @@
+/* $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) 2015-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 <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/rtwn/if_rtwnvar.h>
+#include <dev/rtwn/if_rtwn_debug.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+#include <dev/rtwn/usb/rtwn_usb_reg.h>
+
+static int rtwn_do_request(struct rtwn_softc *,
+ struct usb_device_request *, void *);
+static int rtwn_usb_read_region_1(struct rtwn_softc *,
+ uint16_t, uint8_t *, int);
+
+/* USB Requests. */
+#define R92C_REQ_REGS 0x05
+
+
+static int
+rtwn_do_request(struct rtwn_softc *sc, struct usb_device_request *req,
+ void *data)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ usb_error_t err;
+ int ntries = 10;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ while (ntries--) {
+ err = usbd_do_request_flags(uc->uc_udev, &sc->sc_mtx,
+ req, data, 0, NULL, 250 /* ms */);
+ if (err == USB_ERR_NORMAL_COMPLETION)
+ return (0);
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_USB,
+ "%s: control request failed, %s (retries left: %d)\n",
+ __func__, usbd_errstr(err), ntries);
+ if (err == USB_ERR_NOT_CONFIGURED)
+ return (ENXIO);
+
+ usb_pause_mtx(&sc->sc_mtx, hz / 100);
+ }
+ return (EIO);
+}
+
+/* export for rtwn_fw_write_block() */
+int
+rtwn_usb_write_region_1(struct rtwn_softc *sc, uint16_t addr, uint8_t *buf,
+ int len)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = R92C_REQ_REGS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+ return (rtwn_do_request(sc, &req, buf));
+}
+
+int
+rtwn_usb_write_1(struct rtwn_softc *sc, uint16_t addr, uint8_t val)
+{
+ return (rtwn_usb_write_region_1(sc, addr, &val, sizeof(val)));
+}
+
+int
+rtwn_usb_write_2(struct rtwn_softc *sc, uint16_t addr, uint16_t val)
+{
+ val = htole16(val);
+ return (rtwn_usb_write_region_1(sc, addr, (uint8_t *)&val, sizeof(val)));
+}
+
+int
+rtwn_usb_write_4(struct rtwn_softc *sc, uint16_t addr, uint32_t val)
+{
+ val = htole32(val);
+ return (rtwn_usb_write_region_1(sc, addr, (uint8_t *)&val, sizeof(val)));
+}
+
+static int
+rtwn_usb_read_region_1(struct rtwn_softc *sc, uint16_t addr, uint8_t *buf,
+ int len)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = R92C_REQ_REGS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+ return (rtwn_do_request(sc, &req, buf));
+}
+
+uint8_t
+rtwn_usb_read_1(struct rtwn_softc *sc, uint16_t addr)
+{
+ uint8_t val;
+
+ if (rtwn_usb_read_region_1(sc, addr, &val, 1) != 0)
+ return (0xff);
+ return (val);
+}
+
+uint16_t
+rtwn_usb_read_2(struct rtwn_softc *sc, uint16_t addr)
+{
+ uint16_t val;
+
+ if (rtwn_usb_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
+ return (0xffff);
+ return (le16toh(val));
+}
+
+uint32_t
+rtwn_usb_read_4(struct rtwn_softc *sc, uint16_t addr)
+{
+ uint32_t val;
+
+ if (rtwn_usb_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
+ return (0xffffffff);
+ return (le32toh(val));
+}
+
+void
+rtwn_usb_delay(struct rtwn_softc *sc, int usec)
+{
+
+ /* 1ms delay as default is too big. */
+ if (usec < 1000)
+ DELAY(usec);
+ else {
+ usb_pause_mtx(&sc->sc_mtx,
+ MAX(msecs_to_ticks(usec / 1000), 1));
+ }
+}
diff --git a/sys/dev/rtwn/usb/rtwn_usb_reg.h b/sys/dev/rtwn/usb/rtwn_usb_reg.h
new file mode 100644
index 000000000000..1149248d71aa
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_reg.h
@@ -0,0 +1,45 @@
+/*-
+ * 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_USB_REG_H
+#define RTWN_USB_REG_H
+
+static __inline uint16_t
+rtwn_usb_calc_tx_checksum(void *buf)
+{
+ uint16_t sum = 0;
+ int i;
+
+ /* NB: checksum calculation takes into account only first 32 bytes. */
+ for (i = 0; i < 32 / 2; i++)
+ sum ^= ((uint16_t *)buf)[i];
+
+ return (sum); /* NB: already little endian. */
+}
+
+int rtwn_usb_write_region_1(struct rtwn_softc *, uint16_t,
+ uint8_t *, int);
+int rtwn_usb_write_1(struct rtwn_softc *, uint16_t, uint8_t);
+int rtwn_usb_write_2(struct rtwn_softc *, uint16_t, uint16_t);
+int rtwn_usb_write_4(struct rtwn_softc *, uint16_t, uint32_t);
+uint8_t rtwn_usb_read_1(struct rtwn_softc *, uint16_t);
+uint16_t rtwn_usb_read_2(struct rtwn_softc *, uint16_t);
+uint32_t rtwn_usb_read_4(struct rtwn_softc *, uint16_t);
+void rtwn_usb_delay(struct rtwn_softc *, int);
+
+#endif /* RTWN_USB_REG_H */
diff --git a/sys/dev/rtwn/usb/rtwn_usb_rx.c b/sys/dev/rtwn/usb/rtwn_usb_rx.c
new file mode 100644
index 000000000000..edb605e1aaf1
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_rx.c
@@ -0,0 +1,335 @@
+/* $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) 2015-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 <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.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_ridx.h>
+#include <dev/rtwn/if_rtwn_rx.h>
+#include <dev/rtwn/if_rtwn_task.h>
+#include <dev/rtwn/if_rtwn_tx.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+#include <dev/rtwn/usb/rtwn_usb_rx.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h> /* for CAM_ALGO_NONE */
+#include <dev/rtwn/rtl8192c/r92c_rx_desc.h>
+
+
+static struct mbuf *
+rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct r92c_rx_stat *stat,
+ int totlen)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct mbuf *m;
+ uint32_t rxdw0;
+ int pktlen;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ /* Dump Rx descriptor. */
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC,
+ "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n",
+ __func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1),
+ le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4),
+ le32toh(stat->tsf_low));
+
+ /*
+ * don't pass packets to the ieee80211 framework if the driver isn't
+ * RUNNING.
+ */
+ if (!(sc->sc_flags & RTWN_RUNNING))
+ return (NULL);
+
+ rxdw0 = le32toh(stat->rxdw0);
+ if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) {
+ /*
+ * This should not happen since we setup our Rx filter
+ * to not receive these frames.
+ */
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
+ "%s: RX flags error (%s)\n", __func__,
+ rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV");
+ goto fail;
+ }
+
+ pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN);
+ if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack))) {
+ /*
+ * Should not happen (because of Rx filter setup).
+ */
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
+ "%s: frame is too short: %d\n", __func__, pktlen);
+ goto fail;
+ }
+
+ m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m == NULL)) {
+ device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n",
+ __func__);
+ goto fail;
+ }
+
+ /* Finalize mbuf. */
+ memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen);
+ m->m_pkthdr.len = m->m_len = totlen;
+
+ if (rtwn_check_frame(sc, m) != 0) {
+ m_freem(m);
+ goto fail;
+ }
+
+ return (m);
+fail:
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+}
+
+static struct mbuf *
+rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ struct r92c_rx_stat *stat;
+ struct mbuf *m, *m0 = NULL;
+ uint32_t rxdw0;
+ int totlen, pktlen, infosz;
+
+ /* Process packets. */
+ while (len >= sizeof(*stat)) {
+ stat = (struct r92c_rx_stat *)buf;
+ rxdw0 = le32toh(stat->rxdw0);
+
+ pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN);
+ if (__predict_false(pktlen == 0))
+ break;
+
+ infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8;
+
+ /* Make sure everything fits in xfer. */
+ totlen = sizeof(*stat) + infosz + pktlen;
+ if (totlen > len)
+ break;
+
+ if (m0 == NULL)
+ m0 = m = rtwn_rx_copy_to_mbuf(sc, stat, totlen);
+ else {
+ m->m_next = rtwn_rx_copy_to_mbuf(sc, stat, totlen);
+ if (m->m_next != NULL)
+ m = m->m_next;
+ }
+
+ /* Align next frame. */
+ totlen = rtwn_usb_align_rx(uc, totlen, len);
+ buf += totlen;
+ len -= totlen;
+ }
+
+ return (m0);
+}
+
+static struct mbuf *
+rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb_xfer *xfer,
+ struct rtwn_data *data)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t *buf;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ if (__predict_false(len < sizeof(struct r92c_rx_stat))) {
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+
+ buf = data->buf;
+ switch (rtwn_classify_intr(sc, buf, len)) {
+ case RTWN_RX_DATA:
+ return (rtwn_rxeof(sc, buf, len));
+ case RTWN_RX_TX_REPORT:
+ if (sc->sc_ratectl != RTWN_RATECTL_NET80211) {
+ /* shouldn't happen */
+ device_printf(sc->sc_dev,
+ "%s called while ratectl = %d!\n",
+ __func__, sc->sc_ratectl);
+ break;
+ }
+
+ RTWN_NT_LOCK(sc);
+ rtwn_handle_tx_report(sc, buf, len);
+ RTWN_NT_UNLOCK(sc);
+
+#ifdef IEEE80211_SUPPORT_SUPERG
+ /*
+ * NB: this will executed only when 'report' bit is set.
+ */
+ if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1)
+ rtwn_cmd_sleepable(uc, NULL, 0, rtwn_ff_flush_all);
+#endif
+ break;
+ case RTWN_RX_OTHER:
+ rtwn_handle_c2h_report(sc, buf, len);
+ break;
+ default:
+ /* NOTREACHED */
+ KASSERT(0, ("unknown Rx classification code"));
+ break;
+ }
+
+ return (NULL);
+}
+
+static struct ieee80211_node *
+rtwn_rx_frame(struct rtwn_softc *sc, struct mbuf *m, int8_t *rssi)
+{
+ struct r92c_rx_stat stat;
+
+ /* Imitate PCIe layout. */
+ m_copydata(m, 0, sizeof(struct r92c_rx_stat), (caddr_t)&stat);
+ m_adj(m, sizeof(struct r92c_rx_stat));
+
+ return (rtwn_rx_common(sc, m, &stat, rssi));
+}
+
+void
+rtwn_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rtwn_usb_softc *uc = usbd_xfer_softc(xfer);
+ struct rtwn_softc *sc = &uc->uc_sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL, *next;
+ struct rtwn_data *data;
+ int8_t nf, rssi;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&uc->uc_rx_active);
+ if (data == NULL)
+ goto tr_setup;
+ STAILQ_REMOVE_HEAD(&uc->uc_rx_active, next);
+ m = rtwn_report_intr(uc, xfer, data);
+ STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&uc->uc_rx_inactive);
+ if (data == NULL) {
+ KASSERT(m == NULL, ("mbuf isn't NULL"));
+ goto finish;
+ }
+ STAILQ_REMOVE_HEAD(&uc->uc_rx_inactive, next);
+ STAILQ_INSERT_TAIL(&uc->uc_rx_active, data, next);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf,
+ usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+
+ /*
+ * To avoid LOR we should unlock our private mutex here to call
+ * ieee80211_input() because here is at the end of a USB
+ * callback and safe to unlock.
+ */
+ while (m != NULL) {
+ next = m->m_next;
+ m->m_next = NULL;
+
+ ni = rtwn_rx_frame(sc, m, &rssi);
+
+ RTWN_UNLOCK(sc);
+
+ nf = RTWN_NOISE_FLOOR;
+ if (ni != NULL) {
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ m->m_flags |= M_AMPDU;
+ (void)ieee80211_input(ni, m, rssi - nf, nf);
+ ieee80211_free_node(ni);
+ } else {
+ (void)ieee80211_input_all(ic, m,
+ rssi - nf, nf);
+ }
+ RTWN_LOCK(sc);
+ m = next;
+ }
+ break;
+ default:
+ /* needs it to the inactive queue due to a error. */
+ data = STAILQ_FIRST(&uc->uc_rx_active);
+ if (data != NULL) {
+ STAILQ_REMOVE_HEAD(&uc->uc_rx_active, next);
+ STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next);
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ break;
+ }
+finish:
+ /* Finished receive; age anything left on the FF queue by a little bump */
+ /*
+ * XXX TODO: just make this a callout timer schedule so we can
+ * flush the FF staging queue if we're approaching idle.
+ */
+#ifdef IEEE80211_SUPPORT_SUPERG
+ if (!(sc->sc_flags & RTWN_FW_LOADED))
+ rtwn_cmd_sleepable(uc, NULL, 0, rtwn_ff_flush_all);
+#endif
+
+ /* Kick-start more transmit in case we stalled */
+ rtwn_start(sc);
+}
diff --git a/sys/dev/rtwn/usb/rtwn_usb_rx.h b/sys/dev/rtwn/usb/rtwn_usb_rx.h
new file mode 100644
index 000000000000..e370816969a7
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_rx.h
@@ -0,0 +1,24 @@
+/*-
+ * 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_USB_RX_H
+#define RTWN_USB_RX_H
+
+void rtwn_bulk_rx_callback(struct usb_xfer *, usb_error_t);
+
+#endif /* RTWN_USB_RX_H */
diff --git a/sys/dev/rtwn/usb/rtwn_usb_tx.c b/sys/dev/rtwn/usb/rtwn_usb_tx.c
new file mode 100644
index 000000000000..1580d85d0323
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_tx.c
@@ -0,0 +1,282 @@
+/* $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) 2015-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 <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 <net80211/ieee80211_ratectl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/if_rtwn_beacon.h>
+#include <dev/rtwn/if_rtwn_debug.h>
+#include <dev/rtwn/if_rtwn_ridx.h>
+#include <dev/rtwn/if_rtwn_task.h>
+#include <dev/rtwn/if_rtwn_tx.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+
+#include <dev/rtwn/usb/rtwn_usb_reg.h>
+#include <dev/rtwn/usb/rtwn_usb_tx.h>
+
+static struct rtwn_data * _rtwn_usb_getbuf(struct rtwn_usb_softc *);
+static struct rtwn_data * rtwn_usb_getbuf(struct rtwn_usb_softc *);
+static void rtwn_usb_txeof(struct rtwn_usb_softc *,
+ struct rtwn_data *, int);
+
+
+static const uint8_t wme2qid[] =
+ { RTWN_BULK_TX_BE, RTWN_BULK_TX_BK,
+ RTWN_BULK_TX_VI, RTWN_BULK_TX_VO };
+
+
+static struct rtwn_data *
+_rtwn_usb_getbuf(struct rtwn_usb_softc *uc)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+ struct rtwn_data *bf;
+
+ bf = STAILQ_FIRST(&uc->uc_tx_inactive);
+ if (bf != NULL)
+ STAILQ_REMOVE_HEAD(&uc->uc_tx_inactive, next);
+ else {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
+ "%s: out of xmit buffers\n", __func__);
+ }
+ return (bf);
+}
+
+static struct rtwn_data *
+rtwn_usb_getbuf(struct rtwn_usb_softc *uc)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+ struct rtwn_data *bf;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ bf = _rtwn_usb_getbuf(uc);
+ if (bf == NULL) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, "%s: stop queue\n",
+ __func__);
+ }
+ return (bf);
+}
+
+static void
+rtwn_usb_txeof(struct rtwn_usb_softc *uc, struct rtwn_data *data, int status)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ if (data->ni != NULL) /* not a beacon frame */
+ ieee80211_tx_complete(data->ni, data->m, status);
+
+ if (sc->sc_ratectl != RTWN_RATECTL_NET80211)
+ if (sc->sc_tx_n_active > 0)
+ sc->sc_tx_n_active--;
+
+ data->ni = NULL;
+ data->m = NULL;
+
+ STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, data, next);
+ sc->qfullmsk = 0;
+#ifndef D4054
+ if (STAILQ_EMPTY(&uc->uc_tx_active) && STAILQ_EMPTY(&uc->uc_tx_pending))
+ sc->sc_tx_timer = 0;
+ else
+ sc->sc_tx_timer = 5;
+#endif
+}
+
+void
+rtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rtwn_usb_softc *uc = usbd_xfer_softc(xfer);
+ struct rtwn_softc *sc = &uc->uc_sc;
+ struct rtwn_data *data;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)){
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&uc->uc_tx_active);
+ if (data == NULL)
+ goto tr_setup;
+ STAILQ_REMOVE_HEAD(&uc->uc_tx_active, next);
+ rtwn_usb_txeof(uc, data, 0);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&uc->uc_tx_pending);
+ if (data == NULL) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
+ "%s: empty pending queue\n", __func__);
+ sc->sc_tx_n_active = 0;
+ goto finish;
+ }
+ STAILQ_REMOVE_HEAD(&uc->uc_tx_pending, next);
+ STAILQ_INSERT_TAIL(&uc->uc_tx_active, data, next);
+
+ /*
+ * Note: if this is a beacon frame, ensure that it will go
+ * into appropriate queue.
+ */
+ if (data->ni == NULL && RTWN_CHIP_HAS_BCNQ1(sc))
+ rtwn_switch_bcnq(sc, data->id);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
+ usbd_transfer_submit(xfer);
+ if (sc->sc_ratectl != RTWN_RATECTL_NET80211)
+ sc->sc_tx_n_active++;
+ break;
+ default:
+ data = STAILQ_FIRST(&uc->uc_tx_active);
+ if (data == NULL)
+ goto tr_setup;
+ STAILQ_REMOVE_HEAD(&uc->uc_tx_active, next);
+ rtwn_usb_txeof(uc, data, 1);
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+finish:
+#ifdef IEEE80211_SUPPORT_SUPERG
+ /*
+ * If the TX active queue drops below a certain
+ * threshold, ensure we age fast-frames out so they're
+ * transmitted.
+ */
+ if (sc->sc_ratectl != RTWN_RATECTL_NET80211 &&
+ sc->sc_tx_n_active <= 1) {
+ /* XXX ew - net80211 should defer this for us! */
+
+ /*
+ * Note: this sc_tx_n_active currently tracks
+ * the number of pending transmit submissions
+ * and not the actual depth of the TX frames
+ * pending to the hardware. That means that
+ * we're going to end up with some sub-optimal
+ * aggregation behaviour.
+ */
+ /*
+ * XXX TODO: just make this a callout timer schedule so we can
+ * flush the FF staging queue if we're approaching idle.
+ */
+ rtwn_cmd_sleepable(uc, NULL, 0, rtwn_ff_flush_all);
+ }
+#endif
+ /* Kick-start more transmit */
+ rtwn_start(sc);
+}
+
+static void
+rtwn_usb_tx_checksum(struct rtwn_tx_desc_common *txd)
+{
+ txd->txdw7.usb_checksum = 0;
+ txd->txdw7.usb_checksum = rtwn_usb_calc_tx_checksum(txd);
+}
+
+int
+rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni,
+ struct mbuf *m, uint8_t *tx_desc, uint8_t type, int id)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ struct rtwn_tx_desc_common *txd;
+ struct rtwn_data *data;
+ struct usb_xfer *xfer;
+ uint16_t ac;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ data = rtwn_usb_getbuf(uc);
+ if (data == NULL)
+ return (ENOBUFS);
+
+ ac = M_WME_GETAC(m);
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_CTL:
+ case IEEE80211_FC0_TYPE_MGT:
+ xfer = uc->uc_xfer[RTWN_BULK_TX_VO];
+ break;
+ default:
+ xfer = uc->uc_xfer[wme2qid[ac]];
+ break;
+ }
+
+ txd = (struct rtwn_tx_desc_common *)tx_desc;
+ txd->pktlen = htole16(m->m_pkthdr.len);
+ txd->offset = sc->txdesc_len;
+ txd->flags0 |= RTWN_FLAGS0_OWN;
+ rtwn_usb_tx_checksum(txd);
+
+ /* Dump Tx descriptor. */
+ rtwn_dump_tx_desc(sc, tx_desc);
+
+ memcpy(data->buf, tx_desc, sc->txdesc_len);
+ m_copydata(m, 0, m->m_pkthdr.len,
+ (caddr_t)(data->buf + sc->txdesc_len));
+
+ data->buflen = m->m_pkthdr.len + sc->txdesc_len;
+ data->id = id;
+ data->ni = ni;
+ if (data->ni != NULL) {
+ data->m = m;
+#ifndef D4054
+ sc->sc_tx_timer = 5;
+#endif
+ }
+
+ STAILQ_INSERT_TAIL(&uc->uc_tx_pending, data, next);
+ if (STAILQ_EMPTY(&uc->uc_tx_inactive))
+ sc->qfullmsk = 1;
+
+ usbd_transfer_start(xfer);
+
+ return (0);
+}
diff --git a/sys/dev/rtwn/usb/rtwn_usb_tx.h b/sys/dev/rtwn/usb/rtwn_usb_tx.h
new file mode 100644
index 000000000000..01527080d515
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_tx.h
@@ -0,0 +1,26 @@
+/*-
+ * 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_USB_TX_H
+#define RTWN_USB_TX_H
+
+void rtwn_bulk_tx_callback(struct usb_xfer *, usb_error_t);
+int rtwn_usb_tx_start(struct rtwn_softc *, struct ieee80211_node *,
+ struct mbuf *, uint8_t *, uint8_t, int);
+
+#endif /* RTWN_USB_TX_H */
diff --git a/sys/dev/rtwn/usb/rtwn_usb_var.h b/sys/dev/rtwn/usb/rtwn_usb_var.h
new file mode 100644
index 000000000000..be7f8f198834
--- /dev/null
+++ b/sys/dev/rtwn/usb/rtwn_usb_var.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ * 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.
+ *
+ * $OpenBSD: if_urtwnreg.h,v 1.3 2010/11/16 18:02:59 damien Exp $
+ * $FreeBSD$
+ */
+
+#ifndef RTWN_USBVAR_H
+#define RTWN_USBVAR_H
+
+#define RTWN_IFACE_INDEX 0
+
+#define RTWN_USB_RX_LIST_COUNT 1
+#define RTWN_USB_TX_LIST_COUNT 16
+
+struct rtwn_data {
+ uint8_t *buf;
+ /* 'id' is meaningful for beacons only */
+ int id;
+ uint16_t buflen;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ STAILQ_ENTRY(rtwn_data) next;
+};
+typedef STAILQ_HEAD(, rtwn_data) rtwn_datahead;
+
+enum {
+ RTWN_BULK_RX,
+ RTWN_BULK_TX_BE, /* = WME_AC_BE */
+ RTWN_BULK_TX_BK, /* = WME_AC_BK */
+ RTWN_BULK_TX_VI, /* = WME_AC_VI */
+ RTWN_BULK_TX_VO, /* = WME_AC_VO */
+ RTWN_N_TRANSFER = 5,
+};
+
+#define RTWN_EP_QUEUES RTWN_BULK_RX
+
+struct rtwn_usb_softc {
+ struct rtwn_softc uc_sc; /* must be the first */
+ struct usb_device *uc_udev;
+ struct usb_xfer *uc_xfer[RTWN_N_TRANSFER];
+
+ struct rtwn_data uc_rx[RTWN_USB_RX_LIST_COUNT];
+ rtwn_datahead uc_rx_active;
+ rtwn_datahead uc_rx_inactive;
+ struct rtwn_data uc_tx[RTWN_USB_TX_LIST_COUNT];
+ rtwn_datahead uc_tx_active;
+ rtwn_datahead uc_tx_inactive;
+ rtwn_datahead uc_tx_pending;
+
+ int (*uc_align_rx)(int, int);
+
+ int ntx;
+ int tx_agg_desc_num;
+};
+#define RTWN_USB_SOFTC(sc) ((struct rtwn_usb_softc *)(sc))
+
+#define rtwn_usb_align_rx(_uc, _totlen, _len) \
+ (((_uc)->uc_align_rx)((_totlen), (_len)))
+
+#endif /* RTWN_USBVAR_H */