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;
 };
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to