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 @@ 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__); 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; };