svn commit: r289163 - head/sys/dev/wpi

Adrian Chadd adrian at FreeBSD.org
Mon Oct 12 04:05:14 UTC 2015


Author: adrian
Date: Mon Oct 12 04:05:12 2015
New Revision: 289163
URL: https://svnweb.freebsd.org/changeset/base/289163

Log:
  wpi(4): add support for TX fragmentation.
  
  Tested:
  
  * Tested with Intel 3945BG, HOSTAP and STA modes
  
  Differential Revision:	https://reviews.freebsd.org/D3770

Modified:
  head/sys/dev/wpi/if_wpi.c
  head/sys/dev/wpi/if_wpireg.h
  head/sys/dev/wpi/if_wpivar.h

Modified: head/sys/dev/wpi/if_wpi.c
==============================================================================
--- head/sys/dev/wpi/if_wpi.c	Mon Oct 12 03:27:08 2015	(r289162)
+++ head/sys/dev/wpi/if_wpi.c	Mon Oct 12 04:05:12 2015	(r289163)
@@ -196,6 +196,7 @@ static void	wpi_debug_registers(struct w
 #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, 
 	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, 
 	    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 wp
 	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 wp
 	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 wp
 		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 wp
 			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 wp
 	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 wp
 	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
 		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
 	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
 		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, st
 }
 
 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, 
 
 	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, st
 {
 	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, st
 		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__);
 

Modified: head/sys/dev/wpi/if_wpireg.h
==============================================================================
--- head/sys/dev/wpi/if_wpireg.h	Mon Oct 12 03:27:08 2015	(r289162)
+++ head/sys/dev/wpi/if_wpireg.h	Mon Oct 12 04:05:12 2015	(r289163)
@@ -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
 

Modified: head/sys/dev/wpi/if_wpivar.h
==============================================================================
--- head/sys/dev/wpi/if_wpivar.h	Mon Oct 12 03:27:08 2015	(r289162)
+++ head/sys/dev/wpi/if_wpivar.h	Mon Oct 12 04:05:12 2015	(r289163)
@@ -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;
 };


More information about the svn-src-head mailing list