Index: sys/dev/wpi/if_wpi.c =================================================================== --- sys/dev/wpi/if_wpi.c +++ sys/dev/wpi/if_wpi.c @@ -196,6 +196,7 @@ #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 @@ | 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 @@ 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 @@ BUS_DMASYNC_PREWRITE); ring->queued = 0; ring->cur = 0; + ring->pending = 0; ring->update = 0; } @@ -2572,6 +2576,41 @@ end: WPI_UNLOCK(sc); } +static void +wpi_free_txfrags(struct wpi_softc *sc, uint16_t ac) +{ + struct wpi_tx_ring *ring; + + WPI_TXQ_LOCK(sc); + ring = &sc->txq[ac]; + + WPI_TXQ_STATE_LOCK(sc); + ring->queued -= ring->pending; + if (ring->queued == 0) + callout_stop(&sc->tx_timeout); + WPI_TXQ_STATE_UNLOCK(sc); + + while (ring->pending--) { + if (ring->cur == 0) + ring->cur = WPI_TX_RING_COUNT - 1; + else + ring->cur--; + + struct wpi_tx_data *data = &ring->data[ring->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) { @@ -2584,7 +2623,7 @@ bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER]; uint8_t pad; uint16_t hdrlen; - int error, i, nsegs, totlen; + int error, i, nsegs, totlen, frag; WPI_TXQ_LOCK(sc); @@ -2601,6 +2640,7 @@ 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; @@ -2699,17 +2739,26 @@ 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); - 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); + if (!frag) { + callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, + sc); + } WPI_TXQ_STATE_UNLOCK(sc); } + /* Kick TX ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + if (frag) { + ring->pending += 1; + ieee80211_node_incref(data->ni); + } else { + ring->pending = 0; + sc->sc_update_tx_ring(sc, ring); + } + end: DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STD_END_ERR : TRACE_STR_END, __func__); @@ -2793,6 +2842,8 @@ 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 +2859,7 @@ 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 +2917,15 @@ 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 +3049,13 @@ } 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 +3076,8 @@ 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 +3116,9 @@ { 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 +3129,30 @@ 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__); Index: sys/dev/wpi/if_wpireg.h =================================================================== --- sys/dev/wpi/if_wpireg.h +++ sys/dev/wpi/if_wpireg.h @@ -520,6 +520,8 @@ 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 Index: sys/dev/wpi/if_wpivar.h =================================================================== --- sys/dev/wpi/if_wpivar.h +++ sys/dev/wpi/if_wpivar.h @@ -74,6 +74,7 @@ bus_dma_tag_t data_dmat; uint8_t qid; uint8_t cur; + uint8_t pending; int16_t queued; int update:1; };