wpi(4): add support for TX fragmentation.
Tested: * Tested with Intel 3945BG, HOSTAP and STA modes Differential Revision: https://reviews.freebsd.org/D3770
This commit is contained in:
parent
d07be335a0
commit
940aa8080c
@ -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 @@ wpi_intr(void *arg)
|
||||
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__);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user