aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/wpi
diff options
context:
space:
mode:
authorAdrian Chadd <adrian@FreeBSD.org>2015-10-12 04:05:12 +0000
committerAdrian Chadd <adrian@FreeBSD.org>2015-10-12 04:05:12 +0000
commit940aa8080c249654c1e96546aaf3bd71554b814a (patch)
tree117dddb87f4147c32d0f9face41c998c61a26c5a /sys/dev/wpi
parentd07be335a06c19be4624487245b2acfe64f55cc3 (diff)
downloadsrc-940aa8080c249654c1e96546aaf3bd71554b814a.tar.gz
src-940aa8080c249654c1e96546aaf3bd71554b814a.zip
wpi(4): add support for TX fragmentation.
Tested: * Tested with Intel 3945BG, HOSTAP and STA modes Differential Revision: https://reviews.freebsd.org/D3770
Notes
Notes: svn path=/head/; revision=289163
Diffstat (limited to 'sys/dev/wpi')
-rw-r--r--sys/dev/wpi/if_wpi.c121
-rw-r--r--sys/dev/wpi/if_wpireg.h2
-rw-r--r--sys/dev/wpi/if_wpivar.h1
3 files changed, 96 insertions, 28 deletions
diff --git a/sys/dev/wpi/if_wpi.c b/sys/dev/wpi/if_wpi.c
index dfd6424c7109..040360f16cda 100644
--- a/sys/dev/wpi/if_wpi.c
+++ b/sys/dev/wpi/if_wpi.c
@@ -196,6 +196,7 @@ static void wpi_debug_registers(struct wpi_softc *);
#endif
static void wpi_fatal_intr(struct wpi_softc *);
static void wpi_intr(void *);
+static void wpi_free_txfrags(struct wpi_softc *, uint16_t);
static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *);
static int wpi_tx_data(struct wpi_softc *, struct mbuf *,
struct ieee80211_node *);
@@ -458,6 +459,7 @@ wpi_attach(device_t dev)
| IEEE80211_C_MONITOR /* monitor mode supported */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
| IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_TXFRAG /* handle tx frags */
| IEEE80211_C_TXPMGT /* tx power management */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* 802.11i */
@@ -1168,6 +1170,7 @@ wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, uint8_t qid)
ring->qid = qid;
ring->queued = 0;
ring->cur = 0;
+ ring->pending = 0;
ring->update = 0;
DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
@@ -1288,6 +1291,7 @@ wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring)
BUS_DMASYNC_PREWRITE);
ring->queued = 0;
ring->cur = 0;
+ ring->pending = 0;
ring->update = 0;
}
@@ -2572,6 +2576,34 @@ done:
end: WPI_UNLOCK(sc);
}
+static void
+wpi_free_txfrags(struct wpi_softc *sc, uint16_t ac)
+{
+ struct wpi_tx_ring *ring;
+ struct wpi_tx_data *data;
+ uint8_t cur;
+
+ WPI_TXQ_LOCK(sc);
+ ring = &sc->txq[ac];
+
+ while (ring->pending != 0) {
+ ring->pending--;
+ cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT;
+ data = &ring->data[cur];
+
+ bus_dmamap_sync(ring->data_dmat, data->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(ring->data_dmat, data->map);
+ m_freem(data->m);
+ data->m = NULL;
+
+ ieee80211_node_decref(data->ni);
+ data->ni = NULL;
+ }
+
+ WPI_TXQ_UNLOCK(sc);
+}
+
static int
wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
{
@@ -2582,9 +2614,9 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
struct wpi_tx_ring *ring;
struct mbuf *m1;
bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER];
- uint8_t pad;
+ uint8_t cur, pad;
uint16_t hdrlen;
- int error, i, nsegs, totlen;
+ int error, i, nsegs, totlen, frag;
WPI_TXQ_LOCK(sc);
@@ -2601,6 +2633,7 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
wh = mtod(buf->m, struct ieee80211_frame *);
hdrlen = ieee80211_anyhdrsize(wh);
totlen = buf->m->m_pkthdr.len;
+ frag = ((buf->m->m_flags & (M_FRAG | M_LASTFRAG)) == M_FRAG);
if (__predict_false(totlen < sizeof(struct ieee80211_frame_min))) {
error = EINVAL;
@@ -2614,15 +2647,16 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
pad = 0;
ring = &sc->txq[buf->ac];
- desc = &ring->desc[ring->cur];
- data = &ring->data[ring->cur];
+ cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT;
+ desc = &ring->desc[cur];
+ data = &ring->data[cur];
/* Prepare TX firmware command. */
- cmd = &ring->cmd[ring->cur];
+ cmd = &ring->cmd[cur];
cmd->code = buf->code;
cmd->flags = 0;
cmd->qid = ring->qid;
- cmd->idx = ring->cur;
+ cmd->idx = cur;
memcpy(cmd->data, buf->data, buf->size);
@@ -2662,7 +2696,8 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
if (ring->qid < WPI_CMD_QUEUE_NUM) {
if_inc_counter(buf->ni->ni_vap->iv_ifp,
IFCOUNTER_OERRORS, 1);
- ieee80211_free_node(buf->ni);
+ if (!frag)
+ ieee80211_free_node(buf->ni);
}
m_freem(buf->m);
error = 0;
@@ -2678,7 +2713,7 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
data->ni = buf->ni;
DPRINTF(sc, WPI_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
- __func__, ring->qid, ring->cur, totlen, nsegs);
+ __func__, ring->qid, cur, totlen, nsegs);
/* Fill TX descriptor. */
desc->nsegs = WPI_PAD32(totlen + pad) << 4 | (1 + nsegs);
@@ -2699,16 +2734,23 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
BUS_DMASYNC_PREWRITE);
- /* Kick TX ring. */
- ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT;
- sc->sc_update_tx_ring(sc, ring);
+ ring->pending += 1;
- if (ring->qid < WPI_CMD_QUEUE_NUM) {
- WPI_TXQ_STATE_LOCK(sc);
- ring->queued++;
- callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, sc);
- WPI_TXQ_STATE_UNLOCK(sc);
- }
+ if (!frag) {
+ if (ring->qid < WPI_CMD_QUEUE_NUM) {
+ WPI_TXQ_STATE_LOCK(sc);
+ ring->queued += ring->pending;
+ callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout,
+ sc);
+ WPI_TXQ_STATE_UNLOCK(sc);
+ }
+
+ /* Kick TX ring. */
+ ring->cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT;
+ ring->pending = 0;
+ sc->sc_update_tx_ring(sc, ring);
+ } else
+ ieee80211_node_incref(data->ni);
end: DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END,
__func__);
@@ -2793,6 +2835,8 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
tap->wt_rate = rate;
if (k != NULL)
tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+ if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
ieee80211_radiotap_tx(vap, m);
}
@@ -2808,7 +2852,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
if (!IEEE80211_QOS_HAS_SEQ(wh))
flags |= WPI_TX_AUTO_SEQ;
if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
- flags |= WPI_TX_MORE_FRAG; /* Cannot happen yet. */
+ flags |= WPI_TX_MORE_FRAG;
/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
if (!ismcast) {
@@ -2866,6 +2910,15 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
memcpy(tx->key, k->wk_key, k->wk_keylen);
}
+ if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) {
+ struct mbuf *next = m->m_nextpkt;
+
+ tx->lnext = htole16(next->m_pkthdr.len);
+ tx->fnext = htole32(tx->security |
+ (flags & WPI_TX_NEED_ACK) |
+ WPI_NEXT_STA_ID(tx->id));
+ }
+
tx->len = htole16(totlen);
tx->flags = htole32(flags);
tx->plcp = rate2plcp(rate);
@@ -2989,13 +3042,13 @@ wpi_tx_data_raw(struct wpi_softc *sc, struct mbuf *m,
}
static __inline int
-wpi_tx_ring_is_full(struct wpi_softc *sc, uint16_t ac)
+wpi_tx_ring_free_space(struct wpi_softc *sc, uint16_t ac)
{
struct wpi_tx_ring *ring = &sc->txq[ac];
int retval;
WPI_TXQ_STATE_LOCK(sc);
- retval = (ring->queued > WPI_TX_RING_HIMARK);
+ retval = WPI_TX_RING_HIMARK - ring->queued;
WPI_TXQ_STATE_UNLOCK(sc);
return retval;
@@ -3016,7 +3069,8 @@ wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
WPI_TX_LOCK(sc);
- if (sc->sc_running == 0 || wpi_tx_ring_is_full(sc, ac)) {
+ /* NB: no fragments here */
+ if (sc->sc_running == 0 || wpi_tx_ring_free_space(sc, ac) < 1) {
error = sc->sc_running ? ENOBUFS : ENETDOWN;
goto unlock;
}
@@ -3055,8 +3109,9 @@ wpi_transmit(struct ieee80211com *ic, struct mbuf *m)
{
struct wpi_softc *sc = ic->ic_softc;
struct ieee80211_node *ni;
+ struct mbuf *mnext;
uint16_t ac;
- int error;
+ int error, nmbufs;
WPI_TX_LOCK(sc);
DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__);
@@ -3067,20 +3122,30 @@ wpi_transmit(struct ieee80211com *ic, struct mbuf *m)
goto unlock;
}
+ nmbufs = 1;
+ for (mnext = m->m_nextpkt; mnext != NULL; mnext = mnext->m_nextpkt)
+ nmbufs++;
+
/* Check for available space. */
ac = M_WME_GETAC(m);
- if (wpi_tx_ring_is_full(sc, ac)) {
+ if (wpi_tx_ring_free_space(sc, ac) < nmbufs) {
error = ENOBUFS;
goto unlock;
}
error = 0;
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
- if (wpi_tx_data(sc, m, ni) != 0) {
- if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
- ieee80211_free_node(ni);
- m_freem(m);
- }
+ do {
+ mnext = m->m_nextpkt;
+ if (wpi_tx_data(sc, m, ni) != 0) {
+ if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS,
+ nmbufs);
+ wpi_free_txfrags(sc, ac);
+ ieee80211_free_mbuf(m);
+ ieee80211_free_node(ni);
+ break;
+ }
+ } while((m = mnext) != NULL);
DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__);
diff --git a/sys/dev/wpi/if_wpireg.h b/sys/dev/wpi/if_wpireg.h
index f36dd2512473..b0154f22bc5e 100644
--- a/sys/dev/wpi/if_wpireg.h
+++ b/sys/dev/wpi/if_wpireg.h
@@ -520,6 +520,8 @@ struct wpi_cmd_data {
uint8_t key[IEEE80211_KEYBUF_SIZE];
uint8_t tkip[IEEE80211_WEP_MICLEN];
uint32_t fnext;
+#define WPI_NEXT_STA_ID(id) ((id) << 8)
+
uint32_t lifetime;
#define WPI_LIFETIME_INFINITE 0xffffffff
diff --git a/sys/dev/wpi/if_wpivar.h b/sys/dev/wpi/if_wpivar.h
index fb566b50945b..1957740deaf6 100644
--- a/sys/dev/wpi/if_wpivar.h
+++ b/sys/dev/wpi/if_wpivar.h
@@ -74,6 +74,7 @@ struct wpi_tx_ring {
bus_dma_tag_t data_dmat;
uint8_t qid;
uint8_t cur;
+ uint8_t pending;
int16_t queued;
int update:1;
};