svn commit: r321735 - in head/sys/dev/rtwn: . usb
Andriy Voskoboinyk
avos at FreeBSD.org
Sun Jul 30 23:35:23 UTC 2017
Author: avos
Date: Sun Jul 30 23:35:21 2017
New Revision: 321735
URL: https://svnweb.freebsd.org/changeset/base/321735
Log:
rtwn_usb: add support for fragmented Rx.
Since device can pass multiple frames in a single payload temporary
Rx buffer was big enough to hold all of them; now the driver can
concatenate a single frame from multiple payloads.
The Rx buffer size may be configured via tunable (dev.rtwn.%d.rx_buf_size).
Tested with:
- rtl8188cus, rtl8188eu and rtl8821au (STA mode).
- (by kevlo) rtl8192cu and rtl8188eu.
PR: 218527
Reviewed by: kevlo
Differential Revision: https://reviews.freebsd.org/D11705
Modified:
head/sys/dev/rtwn/if_rtwnreg.h
head/sys/dev/rtwn/usb/rtwn_usb_attach.c
head/sys/dev/rtwn/usb/rtwn_usb_ep.c
head/sys/dev/rtwn/usb/rtwn_usb_rx.c
head/sys/dev/rtwn/usb/rtwn_usb_var.h
Modified: head/sys/dev/rtwn/if_rtwnreg.h
==============================================================================
--- head/sys/dev/rtwn/if_rtwnreg.h Sun Jul 30 23:15:30 2017 (r321734)
+++ head/sys/dev/rtwn/if_rtwnreg.h Sun Jul 30 23:35:21 2017 (r321735)
@@ -18,6 +18,9 @@
* $FreeBSD$
*/
+#ifndef IF_RTWNREG_H
+#define IF_RTWNREG_H
+
#define R92C_MIN_TX_PWR 0x00
#define R92C_MAX_TX_PWR 0x3f
@@ -165,3 +168,5 @@ rtwn_chan2centieee(const struct ieee80211_channel *c)
return (chan);
}
+
+#endif /* IF_RTWNREG_H */
Modified: head/sys/dev/rtwn/usb/rtwn_usb_attach.c
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_attach.c Sun Jul 30 23:15:30 2017 (r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_attach.c Sun Jul 30 23:35:21 2017 (r321735)
@@ -77,12 +77,14 @@ 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
@@ -133,9 +135,8 @@ 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);
@@ -199,6 +200,9 @@ rtwn_usb_free_rx_list(struct rtwn_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);
}
@@ -224,8 +228,10 @@ rtwn_usb_reset_lists(struct rtwn_softc *sc, struct iee
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
@@ -259,6 +265,23 @@ rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc,
}
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);
@@ -327,6 +350,31 @@ rtwn_usb_attach_methods(struct rtwn_softc *sc)
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)
{
@@ -343,6 +391,7 @@ rtwn_usb_attach(device_t 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);
Modified: head/sys/dev/rtwn/usb/rtwn_usb_ep.c
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_ep.c Sun Jul 30 23:15:30 2017 (r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_ep.c Sun Jul 30 23:35:21 2017 (r321735)
@@ -225,7 +225,8 @@ rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc)
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);
Modified: head/sys/dev/rtwn/usb/rtwn_usb_rx.c
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_rx.c Sun Jul 30 23:15:30 2017 (r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_rx.c Sun Jul 30 23:35:21 2017 (r321735)
@@ -63,31 +63,24 @@ __FBSDID("$FreeBSD$");
#include <dev/rtwn/usb/rtwn_usb_var.h>
#include <dev/rtwn/usb/rtwn_usb_rx.h>
+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))) {
@@ -98,7 +91,7 @@ rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtw
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);
@@ -108,9 +101,31 @@ rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtw
*/
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",
@@ -134,14 +149,108 @@ fail:
}
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;
@@ -156,8 +265,8 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int le
/* 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;
}
@@ -165,9 +274,9 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int le
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. */
@@ -176,6 +285,9 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int le
len -= totlen;
}
+ if (len > 0)
+ (void)rtwn_rxeof_fragmented(uc, data, buf, len);
+
return (m0);
}
@@ -190,15 +302,19 @@ rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb
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 */
@@ -284,8 +400,8 @@ tr_setup:
* 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);
@@ -309,6 +425,8 @@ tr_setup:
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;
Modified: head/sys/dev/rtwn/usb/rtwn_usb_var.h
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_var.h Sun Jul 30 23:15:30 2017 (r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_var.h Sun Jul 30 23:35:21 2017 (r321735)
@@ -21,6 +21,12 @@
#ifndef RTWN_USBVAR_H
#define RTWN_USBVAR_H
+#include <dev/rtwn/if_rtwnreg.h> /* 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
@@ -58,6 +64,12 @@ struct rtwn_usb_softc {
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;
More information about the svn-src-all
mailing list