Index: head/sys/dev/rtwn/if_rtwnreg.h =================================================================== --- head/sys/dev/rtwn/if_rtwnreg.h (revision 321734) +++ head/sys/dev/rtwn/if_rtwnreg.h (revision 321735) @@ -1,167 +1,172 @@ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD: if_urtwnreg.h,v 1.3 2010/11/16 18:02:59 damien Exp $ * $FreeBSD$ */ +#ifndef IF_RTWNREG_H +#define IF_RTWNREG_H + #define R92C_MIN_TX_PWR 0x00 #define R92C_MAX_TX_PWR 0x3f #define R92C_H2C_NBOX 4 /* Common part of Tx descriptor (named only!). */ struct rtwn_tx_desc_common { uint16_t pktlen; uint8_t offset; uint8_t flags0; #define RTWN_FLAGS0_OWN 0x80 uint32_t txdw1; /* NB: qsel is shared too; however, it looks better at the lower level */ #define RTWN_TXDW1_CIPHER_M 0x00c00000 #define RTWN_TXDW1_CIPHER_S 22 #define RTWN_TXDW1_CIPHER_NONE 0 #define RTWN_TXDW1_CIPHER_RC4 1 #define RTWN_TXDW1_CIPHER_SM4 2 #define RTWN_TXDW1_CIPHER_AES 3 uint32_t reserved[5]; union txdw7_shared { uint16_t usb_checksum; uint16_t pci_txbufsize; } txdw7; } __packed __attribute__((aligned(4))); /* Common part of Rx descriptor. */ struct rtwn_rx_stat_common { uint32_t rxdw0; #define RTWN_RXDW0_PKTLEN_M 0x00003fff #define RTWN_RXDW0_PKTLEN_S 0 #define RTWN_RXDW0_CRCERR 0x00004000 #define RTWN_RXDW0_ICVERR 0x00008000 #define RTWN_RXDW0_INFOSZ_M 0x000f0000 #define RTWN_RXDW0_INFOSZ_S 16 #define RTWN_RXDW0_CIPHER_M 0x00700000 #define RTWN_RXDW0_CIPHER_S 20 #define RTWN_RXDW0_QOS 0x00800000 #define RTWN_RXDW0_SHIFT_M 0x03000000 #define RTWN_RXDW0_SHIFT_S 24 #define RTWN_RXDW0_PHYST 0x04000000 #define RTWN_RXDW0_SWDEC 0x08000000 #define RTWN_RXDW0_LS 0x10000000 #define RTWN_RXDW0_FS 0x20000000 #define RTWN_RXDW0_EOR 0x40000000 #define RTWN_RXDW0_OWN 0x80000000 uint32_t rxdw1; #define RTWN_RXDW1_AMSDU 0x00002000 #define RTWN_RXDW1_MC 0x40000000 #define RTWN_RXDW1_BC 0x80000000 uint32_t rxdw2; uint32_t rxdw3; #define RTWN_RXDW3_HTC 0x00000400 #define RTWN_RXDW3_BSSID01_FIT_M 0x00003000 #define RTWN_RXDW3_BSSID01_FIT_S 12 uint32_t rxdw4; uint32_t tsf_low; } __packed __attribute__((aligned(4))); /* Rx descriptor for PCIe devices. */ struct rtwn_rx_stat_pci { uint32_t rxdw0; uint32_t rxdw1; uint32_t rxdw2; uint32_t rxdw3; uint32_t rxdw4; uint32_t tsf_low; uint32_t rxbufaddr; uint32_t rxbufaddr64; } __packed __attribute__((aligned(4))); /* * Macros to access subfields in registers. */ /* Mask and Shift (getter). */ #define MS(val, field) \ (((val) & field##_M) >> field##_S) /* Shift and Mask (setter). */ #define SM(field, val) \ (((val) << field##_S) & field##_M) /* Rewrite. */ #define RW(var, field, val) \ (((var) & ~field##_M) | SM(field, val)) #define RTWN_MAX_CONDITIONS 3 /* * Structure for MAC initialization values. */ struct rtwn_mac_prog { uint16_t reg; uint8_t val; }; /* * Structure for baseband initialization values. */ struct rtwn_bb_prog { int count; const uint16_t *reg; const uint32_t *val; const uint8_t cond[RTWN_MAX_CONDITIONS]; const struct rtwn_bb_prog *next; }; struct rtwn_agc_prog { int count; const uint32_t *val; const uint8_t cond[RTWN_MAX_CONDITIONS]; const struct rtwn_agc_prog *next; }; /* * Structure for RF initialization values. */ struct rtwn_rf_prog { int count; const uint8_t *reg; const uint32_t *val; const uint8_t cond[RTWN_MAX_CONDITIONS]; const struct rtwn_rf_prog *next; }; /* XXX move to net80211. */ static __inline int rtwn_chan2centieee(const struct ieee80211_channel *c) { int chan; chan = c->ic_ieee; if (c->ic_extieee != 0) chan = (chan + c->ic_extieee) / 2; return (chan); } + +#endif /* IF_RTWNREG_H */ Index: head/sys/dev/rtwn/usb/rtwn_usb_attach.c =================================================================== --- head/sys/dev/rtwn/usb/rtwn_usb_attach.c (revision 321734) +++ head/sys/dev/rtwn/usb/rtwn_usb_attach.c (revision 321735) @@ -1,442 +1,491 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #include #include 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_reset_rx_list(struct rtwn_usb_softc *); 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_drop_incorrect_tx(struct rtwn_softc *); static void rtwn_usb_attach_methods(struct rtwn_softc *); +static void rtwn_usb_sysctlattach(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; - /* XXX recheck */ error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT, - sc->rx_dma_size + 1024); + uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT); 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_USB_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); + uc->uc_rx_stat_len = 0; + uc->uc_rx_off = 0; + 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) + if (vap == NULL) { + rtwn_usb_reset_rx_list(uc); 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_reset_rx_list(struct rtwn_usb_softc *uc) +{ + int i; + + for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) { + struct rtwn_data *dp = &uc->uc_rx[i]; + + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + } + } + uc->uc_rx_stat_len = 0; + uc->uc_rx_off = 0; +} + +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 */ RTWN_UNLOCK(sc); for (i = 0; i < RTWN_N_TRANSFER; i++) usbd_transfer_drain(uc->uc_xfer[i]); RTWN_LOCK(sc); } 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; sc->sc_beacon_update_begin = rtwn_nop_softc_vap; sc->sc_beacon_update_end = rtwn_nop_softc_vap; sc->sc_beacon_unload = rtwn_nop_softc_int; sc->bcn_check_interval = 100; } +static void +rtwn_usb_sysctlattach(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + char str[64]; + int ret; + + ret = snprintf(str, sizeof(str), + "Rx buffer size, 512-byte units [%d...%d]", + RTWN_USB_RXBUFSZ_MIN, RTWN_USB_RXBUFSZ_MAX); + KASSERT(ret > 0, ("ret (%d) <= 0!\n", ret)); + (void) ret; + + uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_DEF; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "rx_buf_size", CTLFLAG_RDTUN, &uc->uc_rx_buf_size, + uc->uc_rx_buf_size, str); + if (uc->uc_rx_buf_size < RTWN_USB_RXBUFSZ_MIN) + uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MIN; + if (uc->uc_rx_buf_size > RTWN_USB_RXBUFSZ_MAX) + uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MAX; +} + 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); + rtwn_usb_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); Index: head/sys/dev/rtwn/usb/rtwn_usb_ep.c =================================================================== --- head/sys/dev/rtwn/usb/rtwn_usb_ep.c (revision 321734) +++ head/sys/dev/rtwn/usb/rtwn_usb_ep.c (revision 321735) @@ -1,259 +1,260 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { [RTWN_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .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_USB_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_USB_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_USB_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_USB_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 usb_config *rtwn_config; 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); } rtwn_config = malloc(sizeof(rtwn_config_common), M_TEMP, M_WAITOK); memcpy(rtwn_config, rtwn_config_common, sizeof(rtwn_config_common)); /* 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; } - rtwn_config[RTWN_BULK_RX].bufsize = sc->rx_dma_size + 1024; + rtwn_config[RTWN_BULK_RX].bufsize = + uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT; error = usbd_transfer_setup(uc->uc_udev, &iface_index, uc->uc_xfer, rtwn_config, RTWN_N_TRANSFER, uc, &sc->sc_mtx); free(rtwn_config, M_TEMP); 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); } } Index: head/sys/dev/rtwn/usb/rtwn_usb_rx.c =================================================================== --- head/sys/dev/rtwn/usb/rtwn_usb_rx.c (revision 321734) +++ head/sys/dev/rtwn/usb/rtwn_usb_rx.c (revision 321735) @@ -1,321 +1,439 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include #include #include #include #include #include #include #include #include +static struct mbuf * rtwn_rxeof(struct rtwn_softc *, struct rtwn_data *, + uint8_t *, int); -static struct mbuf * -rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat, - int totlen) +static int +rtwn_rx_check_pre_alloc(struct rtwn_softc *sc, + struct rtwn_rx_stat_common *stat) { - 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); + return (-1); rxdw0 = le32toh(stat->rxdw0); if (__predict_false(rxdw0 & (RTWN_RXDW0_CRCERR | RTWN_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 & RTWN_RXDW0_CRCERR ? "CRC" : "ICV"); - goto fail; + return (-1); } pktlen = MS(rxdw0, RTWN_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; + return (-1); } + return (0); +} + +static struct mbuf * +rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat, + int totlen) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m; + + 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)); + + if (rtwn_rx_check_pre_alloc(sc, stat) != 0) + 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) +rtwn_rxeof_fragmented(struct rtwn_usb_softc *uc, struct rtwn_data *data, + uint8_t *buf, int len) { + struct rtwn_softc *sc = &uc->uc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct rtwn_rx_stat_common *stat = &uc->uc_rx_stat; + uint32_t rxdw0; + int totlen, pktlen, infosz, min_len; + int orig_len = len; + int alloc_mbuf = 0; + + /* Check if Rx descriptor is not truncated. */ + if (uc->uc_rx_stat_len < sizeof(*stat)) { + min_len = min(sizeof(*stat) - uc->uc_rx_stat_len, len); + memcpy((uint8_t *)stat + uc->uc_rx_stat_len, buf, min_len); + + uc->uc_rx_stat_len += min_len; + buf += min_len; + len -= min_len; + + if (uc->uc_rx_stat_len < sizeof(*stat)) + goto end; + + KASSERT(data->m == NULL, ("%s: data->m != NULL!\n", __func__)); + alloc_mbuf = 1; + + /* 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)); + } + + rxdw0 = le32toh(stat->rxdw0); + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); + infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; + totlen = sizeof(*stat) + infosz + pktlen; + if (alloc_mbuf) { + if (rtwn_rx_check_pre_alloc(sc, stat) == 0) { + data->m = m_getm(NULL, totlen, M_NOWAIT, MT_DATA); + if (data->m != NULL) { + m_copyback(data->m, 0, uc->uc_rx_stat_len, + (caddr_t)stat); + + if (rtwn_check_frame(sc, data->m) != 0) { + m_freem(data->m); + data->m = NULL; + counter_u64_add(ic->ic_ierrors, 1); + } + } else + counter_u64_add(ic->ic_ierrors, 1); + } else + counter_u64_add(ic->ic_ierrors, 1); + + uc->uc_rx_off = sizeof(*stat); + } + + /* If mbuf allocation fails just discard the data. */ + min_len = min(totlen - uc->uc_rx_off, len); + if (data->m != NULL) + m_copyback(data->m, uc->uc_rx_off, min_len, buf); + + uc->uc_rx_off += min_len; + if (uc->uc_rx_off == totlen) { + /* Align next frame. */ + min_len = rtwn_usb_align_rx(uc, + orig_len - len + min_len, orig_len); + min_len -= (orig_len - len); + KASSERT(len >= min_len, ("%s: len (%d) < min_len (%d)!\n", + __func__, len, min_len)); + + /* Clear mbuf stats. */ + uc->uc_rx_stat_len = 0; + uc->uc_rx_off = 0; + } + len -= min_len; + buf += min_len; +end: + if (uc->uc_rx_stat_len == 0) + return (rtwn_rxeof(sc, data, buf, len)); + else + return (NULL); +} + +static struct mbuf * +rtwn_rxeof(struct rtwn_softc *sc, struct rtwn_data *data, uint8_t *buf, + int len) +{ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); struct rtwn_rx_stat_common *stat; struct mbuf *m, *m0 = NULL; uint32_t rxdw0; int totlen, pktlen, infosz; + /* Prepend defragmented frame (if any). */ + if (data->m != NULL) { + m0 = m = data->m; + data->m = NULL; + } + /* Process packets. */ while (len >= sizeof(*stat)) { stat = (struct rtwn_rx_stat_common *)buf; rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); if (__predict_false(pktlen == 0)) break; infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (totlen > len) { - device_printf(sc->sc_dev, - "%s: totlen (%d) > len (%d)!\n", + RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, + "%s: frame is fragmented (totlen %d len %d)\n", __func__, 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; + m->m_nextpkt = rtwn_rx_copy_to_mbuf(sc, stat, totlen); + if (m->m_nextpkt != NULL) + m = m->m_nextpkt; } /* Align next frame. */ totlen = rtwn_usb_align_rx(uc, totlen, len); buf += totlen; len -= totlen; } + if (len > 0) + (void)rtwn_rxeof_fragmented(uc, data, buf, len); + 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 rtwn_rx_stat_common))) { + if (__predict_false(len < sizeof(struct rtwn_rx_stat_common) && + uc->uc_rx_stat_len == 0)) { counter_u64_add(ic->ic_ierrors, 1); return (NULL); } buf = data->buf; + if (uc->uc_rx_stat_len > 0) + return (rtwn_rxeof_fragmented(uc, data, data->buf, len)); + switch (rtwn_classify_intr(sc, buf, len)) { case RTWN_RX_DATA: - return (rtwn_rxeof(sc, buf, len)); + return (rtwn_rxeof(sc, data, buf, len)); case RTWN_RX_TX_REPORT: if (sc->sc_ratectl != RTWN_RATECTL_NET80211) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s called while ratectl = %d!\n", __func__, sc->sc_ratectl); break; } RTWN_NT_LOCK(sc); rtwn_handle_tx_report(sc, buf, len); RTWN_NT_UNLOCK(sc); #ifdef IEEE80211_SUPPORT_SUPERG /* * NB: this will executed only when 'report' bit is set. */ if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1) rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); #endif break; case RTWN_RX_OTHER: rtwn_handle_c2h_report(sc, buf, len); break; default: /* NOTREACHED */ KASSERT(0, ("unknown Rx classification code")); break; } return (NULL); } static struct ieee80211_node * rtwn_rx_frame(struct rtwn_softc *sc, struct mbuf *m) { struct rtwn_rx_stat_common stat; /* Imitate PCIe layout. */ m_copydata(m, 0, sizeof(stat), (caddr_t)&stat); m_adj(m, sizeof(stat)); return (rtwn_rx_common(sc, m, &stat)); } 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; 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; + next = m->m_nextpkt; + m->m_nextpkt = NULL; ni = rtwn_rx_frame(sc, m); RTWN_UNLOCK(sc); if (ni != NULL) { (void)ieee80211_input_mimo(ni, m); ieee80211_free_node(ni); } else { (void)ieee80211_input_mimo_all(ic, m); } 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) { + /* XXX restart device if frame was fragmented? */ + usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } break; } finish: /* Kick-start more transmit in case we stalled */ rtwn_start(sc); } Index: head/sys/dev/rtwn/usb/rtwn_usb_var.h =================================================================== --- head/sys/dev/rtwn/usb/rtwn_usb_var.h (revision 321734) +++ head/sys/dev/rtwn/usb/rtwn_usb_var.h (revision 321735) @@ -1,76 +1,88 @@ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD: if_urtwnreg.h,v 1.3 2010/11/16 18:02:59 damien Exp $ * $FreeBSD$ */ #ifndef RTWN_USBVAR_H #define RTWN_USBVAR_H +#include /* for struct rtwn_rx_stat_common */ + +#define RTWN_USB_RXBUFSZ_UNIT (512) +#define RTWN_USB_RXBUFSZ_MIN ( 4) +#define RTWN_USB_RXBUFSZ_DEF (24) +#define RTWN_USB_RXBUFSZ_MAX (64) #define RTWN_USB_TXBUFSZ (16 * 1024) #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; + int uc_rx_buf_size; + + struct rtwn_rx_stat_common uc_rx_stat; + int uc_rx_stat_len; + int uc_rx_off; + 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 */