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
This commit is contained in:
parent
e39a96781e
commit
9dba612805
@ -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 */
|
||||
|
@ -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 ieee80211vap *vap)
|
||||
|
||||
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
|
||||
@ -258,6 +264,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)
|
||||
{
|
||||
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 rtwn_rx_stat_common *stat,
|
||||
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 rtwn_rx_stat_common *stat,
|
||||
*/
|
||||
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,7 +149,95 @@ 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;
|
||||
@ -142,6 +245,12 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
|
||||
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 len)
|
||||
/* 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 len)
|
||||
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 len)
|
||||
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_xfer *xfer,
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user