Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 Apr 2016 01:21:51 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r297603 - head/sys/net80211
Message-ID:  <201604060121.u361LpBN027682@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Wed Apr  6 01:21:51 2016
New Revision: 297603
URL: https://svnweb.freebsd.org/changeset/base/297603

Log:
  [net80211] Initial A-MSDU support for testing / evaluation
  
  A-MSDU is another 11n aggregation mechanism where multiple ethernet
  frames get LLC encapsulated (so they have a length field), padded,
  and put in a single MPDU (802.11 MAC frame.)  This means it gets sent
  out as a single frame, with a single seqno, it's acked as one frame, etc.
  
  It turns out that, hah, atheros fast frames is almost but not quite
  like this, so I'm reusing all of the current superg/fast-frames stuff
  in order to actually transmit A-MSDU.  Yes, this means that A-MSDU
  frames are also only aggregated two at a time, so it's not necessarily
  a huge win, but it's better than nothing.
  
  This doesn't do anything by default - the driver needs to say it does
  A-MSDU as well as set the AMSDU software TX capability so this code path
  gets exercised.
  
  For now, the only driver that enables this is urtwn.  I'll enable it
  for rsu at some point soon.
  Tested:
  
  * Add an amsdu encap path to aggregate two frames, same as the
    fast-frames path.
  
  * Always do the superg init/teardown and node init/teardown stuff,
    regardless of whether the nodes are doing fast-frames (the ATH
    capability stuff.)  That way we can reuse it for amsdu.
  
  * Don't do AMSDU for multicast/broadcast and EAPOL frames.
  
  * If we're doing A-MPDU, then don't bother doing FF/A-MSDU.
    We can likely do both together, but I don't want to change
    behaviour.
  
  * Teach the fast frames approx txtime logic to support the 11n
    rates.  But, since we don't currently have a full "current rate"
    support, assume it's HT20, long-gi, etc.  That way we overshoot
    on the TX time estimation, so we're always inside the requirements.
    (And we only aggregate two frames for now, so we're not really
    going to exceed that.)
  
  * Drop the maximum FF age default down to 2ms, otherwise we end up
    with some very annoyingly large latencies.
  
  TODO:
  
  * We only aggregate two ethernet frames, so I'm not checking the max
    A-MSDU size.  But when it comes time to support >2 frames, we should
    obey that.
  
  Tested:
  
  * urtwn(4)

Modified:
  head/sys/net80211/ieee80211_ht.h
  head/sys/net80211/ieee80211_node.c
  head/sys/net80211/ieee80211_output.c
  head/sys/net80211/ieee80211_sta.c
  head/sys/net80211/ieee80211_superg.c
  head/sys/net80211/ieee80211_superg.h

Modified: head/sys/net80211/ieee80211_ht.h
==============================================================================
--- head/sys/net80211/ieee80211_ht.h	Wed Apr  6 00:55:39 2016	(r297602)
+++ head/sys/net80211/ieee80211_ht.h	Wed Apr  6 01:21:51 2016	(r297603)
@@ -65,6 +65,10 @@ struct ieee80211_tx_ampdu {
 #define	IEEE80211_AMPDU_RUNNING(tap) \
 	(((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0)
 
+/* return non-zero if AMPDU tx for the TID was NACKed */
+#define	IEEE80211_AMPDU_NACKED(tap)\
+	(!! ((tap)->txa_flags & IEEE80211_AGGR_NAK))
+
 /* return non-zero if AMPDU tx for the TID is running or started */
 #define	IEEE80211_AMPDU_REQUESTED(tap) \
 	(((tap)->txa_flags & \

Modified: head/sys/net80211/ieee80211_node.c
==============================================================================
--- head/sys/net80211/ieee80211_node.c	Wed Apr  6 00:55:39 2016	(r297602)
+++ head/sys/net80211/ieee80211_node.c	Wed Apr  6 01:21:51 2016	(r297603)
@@ -1014,8 +1014,8 @@ node_cleanup(struct ieee80211_node *ni)
 	if (ni->ni_flags & IEEE80211_NODE_HT)
 		ieee80211_ht_node_cleanup(ni);
 #ifdef IEEE80211_SUPPORT_SUPERG
-	else if (ni->ni_ath_flags & IEEE80211_NODE_ATH)
-		ieee80211_ff_node_cleanup(ni);
+	/* Always do FF node cleanup; for A-MSDU */
+	ieee80211_ff_node_cleanup(ni);
 #endif
 #ifdef IEEE80211_SUPPORT_MESH
 	/*

Modified: head/sys/net80211/ieee80211_output.c
==============================================================================
--- head/sys/net80211/ieee80211_output.c	Wed Apr  6 00:55:39 2016	(r297602)
+++ head/sys/net80211/ieee80211_output.c	Wed Apr  6 01:21:51 2016	(r297603)
@@ -191,51 +191,68 @@ ieee80211_vap_pkt_send_dest(struct ieee8
 	 * otherwise unable to establish a BA stream.
 	 */
 	if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
-	    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) &&
-	    (m->m_flags & M_EAPOL) == 0) {
-		int tid = WME_AC_TO_TID(M_WME_GETAC(m));
-		struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
-
-		ieee80211_txampdu_count_packet(tap);
-		if (IEEE80211_AMPDU_RUNNING(tap)) {
-			/*
-			 * Operational, mark frame for aggregation.
-			 *
-			 * XXX do tx aggregation here
-			 */
-			m->m_flags |= M_AMPDU_MPDU;
-		} else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
-		    ic->ic_ampdu_enable(ni, tap)) {
-			/*
-			 * Not negotiated yet, request service.
-			 */
-			ieee80211_ampdu_request(ni, tap);
-			/* XXX hold frame for reply? */
+	    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) {
+		if ((m->m_flags & M_EAPOL) == 0) {
+			int tid = WME_AC_TO_TID(M_WME_GETAC(m));
+			struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
+
+			ieee80211_txampdu_count_packet(tap);
+			if (IEEE80211_AMPDU_RUNNING(tap)) {
+				/*
+				 * Operational, mark frame for aggregation.
+				 *
+				 * XXX do tx aggregation here
+				 */
+				m->m_flags |= M_AMPDU_MPDU;
+			} else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
+			    ic->ic_ampdu_enable(ni, tap)) {
+				/*
+				 * Not negotiated yet, request service.
+				 */
+				ieee80211_ampdu_request(ni, tap);
+				/* XXX hold frame for reply? */
+			}
 		}
 	}
 
+#ifdef IEEE80211_SUPPORT_SUPERG
 	/*
-	 * XXX If we aren't doing AMPDU TX then we /could/ do
-	 * fast-frames encapsulation, however right now this
-	 * output logic doesn't handle that case.
+	 * Check for AMSDU/FF; queue for aggregation
 	 *
-	 * So we'll be limited to "fast-frames" xmit for non-11n STA
-	 * and "no fast frames" xmit for 11n STAs.
-	 * It'd be nice to eventually test fast-frames out by
-	 * gracefully falling from failing A-MPDU transmission
-	 * (driver says no, fail to negotiate it with peer) to
-	 * using fast-frames.
+	 * Note: we don't bother trying to do fast frames or
+	 * A-MSDU encapsulation for 802.3 drivers.  Now, we
+	 * likely could do it for FF (because it's a magic
+	 * atheros tunnel LLC type) but I don't think we're going
+	 * to really need to.  For A-MSDU we'd have to set the
+	 * A-MSDU QoS bit in the wifi header, so we just plain
+	 * can't do it.
 	 *
-	 * Note: we can actually put A-MSDU's inside an A-MPDU,
-	 * so hopefully we can figure out how to make that particular
-	 * combination work right.
-	 */
-#ifdef IEEE80211_SUPPORT_SUPERG
-	else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) {
-		m = ieee80211_ff_check(ni, m);
-		if (m == NULL) {
-			/* NB: any ni ref held on stageq */
-			return (0);
+	 * Strictly speaking, we could actually /do/ A-MSDU / FF
+	 * with A-MPDU together which for certain circumstances
+	 * is beneficial (eg A-MSDU of TCK ACKs.)  However,
+	 * I'll ignore that for now so existing behaviour is maintained.
+	 * Later on it would be good to make "amsdu + ampdu" configurable.
+	 */
+	else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) {
+		if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) {
+			m = ieee80211_amsdu_check(ni, m);
+			if (m == NULL) {
+				/* NB: any ni ref held on stageq */
+				IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
+				    "%s: amsdu_check queued frame\n",
+				    __func__);
+				return (0);
+			}
+		} else if ((! mcast) && IEEE80211_ATH_CAP(vap, ni,
+		    IEEE80211_NODE_FF)) {
+			m = ieee80211_ff_check(ni, m);
+			if (m == NULL) {
+				/* NB: any ni ref held on stageq */
+				IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
+				    "%s: ff_check queued frame\n",
+				    __func__);
+				return (0);
+			}
 		}
 	}
 #endif /* IEEE80211_SUPPORT_SUPERG */
@@ -1229,6 +1246,7 @@ ieee80211_encap(struct ieee80211vap *vap
 	ieee80211_seq seqno;
 	int meshhdrsize, meshae;
 	uint8_t *qos;
+	int is_amsdu = 0;
 	
 	IEEE80211_TX_LOCK_ASSERT(ic);
 
@@ -1383,9 +1401,19 @@ ieee80211_encap(struct ieee80211vap *vap
 	} else {
 #ifdef IEEE80211_SUPPORT_SUPERG
 		/*
-		 * Aggregated frame.
-		 */
-		m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key);
+		 * Aggregated frame.  Check if it's for AMSDU or FF.
+		 *
+		 * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented
+		 * anywhere for some reason.  But, since 11n requires
+		 * AMSDU RX, we can just assume "11n" == "AMSDU".
+		 */
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__);
+		if (ieee80211_amsdu_tx_ok(ni)) {
+			m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key);
+			is_amsdu = 1;
+		} else {
+			m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key);
+		}
 		if (m == NULL)
 #endif
 			goto bad;
@@ -1521,6 +1549,13 @@ ieee80211_encap(struct ieee80211vap *vap
 			qos[1] = 0;
 		wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
 
+		/*
+		 * If this is an A-MSDU then ensure we set the
+		 * relevant field.
+		 */
+		if (is_amsdu)
+			qos[0] |= IEEE80211_QOS_AMSDU;
+
 		if ((m->m_flags & M_AMPDU_MPDU) == 0) {
 			/*
 			 * NB: don't assign a sequence # to potential
@@ -1544,6 +1579,14 @@ ieee80211_encap(struct ieee80211vap *vap
 		*(uint16_t *)wh->i_seq =
 		    htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
 		M_SEQNO_SET(m, seqno);
+
+		/*
+		 * XXX TODO: we shouldn't allow EAPOL, etc that would
+		 * be forced to be non-QoS traffic to be A-MSDU encapsulated.
+		 */
+		if (is_amsdu)
+			printf("%s: XXX ERROR: is_amsdu set; not QoS!\n",
+			    __func__);
 	}
 
 

Modified: head/sys/net80211/ieee80211_sta.c
==============================================================================
--- head/sys/net80211/ieee80211_sta.c	Wed Apr  6 00:55:39 2016	(r297602)
+++ head/sys/net80211/ieee80211_sta.c	Wed Apr  6 01:21:51 2016	(r297603)
@@ -1703,12 +1703,8 @@ sta_recv_mgmt(struct ieee80211_node *ni,
 			ieee80211_setup_basic_htrates(ni, htinfo);
 			ieee80211_node_setuptxparms(ni);
 			ieee80211_ratectl_node_init(ni);
-		} else {
-#ifdef IEEE80211_SUPPORT_SUPERG
-			if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH))
-				ieee80211_ff_node_init(ni);
-#endif
 		}
+
 		/*
 		 * Configure state now that we are associated.
 		 *

Modified: head/sys/net80211/ieee80211_superg.c
==============================================================================
--- head/sys/net80211/ieee80211_superg.c	Wed Apr  6 00:55:39 2016	(r297602)
+++ head/sys/net80211/ieee80211_superg.c	Wed Apr  6 01:21:51 2016	(r297603)
@@ -99,18 +99,22 @@ ieee80211_superg_attach(struct ieee80211
 {
 	struct ieee80211_superg *sg;
 
-	if (ic->ic_caps & IEEE80211_C_FF) {
-		sg = (struct ieee80211_superg *) IEEE80211_MALLOC(
-		     sizeof(struct ieee80211_superg), M_80211_VAP,
-		     IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
-		if (sg == NULL) {
-			printf("%s: cannot allocate SuperG state block\n",
-			    __func__);
-			return;
-		}
-		ic->ic_superg = sg;
+	sg = (struct ieee80211_superg *) IEEE80211_MALLOC(
+	     sizeof(struct ieee80211_superg), M_80211_VAP,
+	     IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+	if (sg == NULL) {
+		printf("%s: cannot allocate SuperG state block\n",
+		    __func__);
+		return;
 	}
-	ieee80211_ffagemax = msecs_to_ticks(150);
+	ic->ic_superg = sg;
+
+	/*
+	 * Default to not being so aggressive for FF/AMSDU
+	 * aging, otherwise we may hold a frame around
+	 * for way too long before we expire it out.
+	 */
+	ieee80211_ffagemax = msecs_to_ticks(2);
 }
 
 void
@@ -353,16 +357,15 @@ ieee80211_ff_encap(struct ieee80211vap *
 		goto bad;
 	}
 	m1->m_nextpkt = NULL;
+
 	/*
-	 * Include fast frame headers in adjusting header layout.
+	 * Adjust to include 802.11 header requirement.
 	 */
 	KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!"));
 	ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t));
-	m1 = ieee80211_mbuf_adjust(vap,
-		hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 +
-		    sizeof(struct ether_header),
-		key, m1);
+	m1 = ieee80211_mbuf_adjust(vap, hdrspace, key, m1);
 	if (m1 == NULL) {
+		printf("%s: failed initial mbuf_adjust\n", __func__);
 		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
 		m_freem(m2);
 		goto bad;
@@ -370,17 +373,15 @@ ieee80211_ff_encap(struct ieee80211vap *
 
 	/*
 	 * Copy second frame's Ethernet header out of line
-	 * and adjust for encapsulation headers.  Note that
-	 * we make room for padding in case there isn't room
+	 * and adjust for possible padding in case there isn't room
 	 * at the end of first frame.
 	 */
 	KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
 	ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t));
-	m2 = ieee80211_mbuf_adjust(vap,
-		ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
-		NULL, m2);
+	m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2);
 	if (m2 == NULL) {
 		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+		printf("%s: failed second \n", __func__);
 		goto bad;
 	}
 
@@ -418,9 +419,8 @@ ieee80211_ff_encap(struct ieee80211vap *
 	}
 
 	/*
-	 * Now, stick 'em together and prepend the tunnel headers;
-	 * first the Atheros tunnel header (all zero for now) and
-	 * then a special fast frame LLC.
+	 * A-MSDU's are just appended; the "I'm A-MSDU!" bit is in the
+	 * QoS header.
 	 *
 	 * XXX optimize by prepending together
 	 */
@@ -454,6 +454,7 @@ ieee80211_ff_encap(struct ieee80211vap *
 
 	return m1;
 bad:
+	vap->iv_stats.is_ff_encapfail++;
 	if (m1 != NULL)
 		m_freem(m1);
 	if (m2 != NULL)
@@ -461,6 +462,114 @@ bad:
 	return NULL;
 }
 
+/*
+ * A-MSDU encapsulation.
+ *
+ * This assumes just two frames for now, since we're borrowing the
+ * same queuing code and infrastructure as fast-frames.
+ *
+ * There must be two packets chained with m_nextpkt.
+ * We do header adjustment for each, and then concatenate the mbuf chains
+ * to form a single frame for transmission.
+ */
+struct mbuf *
+ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
+	struct ieee80211_key *key)
+{
+	struct mbuf *m2;
+	struct ether_header eh1, eh2;
+	struct mbuf *m;
+	int pad;
+
+	m2 = m1->m_nextpkt;
+	if (m2 == NULL) {
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
+		    "%s: only one frame\n", __func__);
+		goto bad;
+	}
+	m1->m_nextpkt = NULL;
+
+	/*
+	 * Include A-MSDU header in adjusting header layout.
+	 */
+	KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!"));
+	ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t));
+	m1 = ieee80211_mbuf_adjust(vap,
+		hdrspace + sizeof(struct llc) + sizeof(uint32_t) +
+		    sizeof(struct ether_header),
+		key, m1);
+	if (m1 == NULL) {
+		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+		m_freem(m2);
+		goto bad;
+	}
+
+	/*
+	 * Copy second frame's Ethernet header out of line
+	 * and adjust for encapsulation headers.  Note that
+	 * we make room for padding in case there isn't room
+	 * at the end of first frame.
+	 */
+	KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
+	ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t));
+	m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2);
+	if (m2 == NULL) {
+		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+		goto bad;
+	}
+
+	/*
+	 * Now do tunnel encapsulation.  First, each
+	 * frame gets a standard encapsulation.
+	 */
+	m1 = ieee80211_ff_encap1(vap, m1, &eh1);
+	if (m1 == NULL)
+		goto bad;
+	m2 = ieee80211_ff_encap1(vap, m2, &eh2);
+	if (m2 == NULL)
+		goto bad;
+
+	/*
+	 * Pad leading frame to a 4-byte boundary.  If there
+	 * is space at the end of the first frame, put it
+	 * there; otherwise prepend to the front of the second
+	 * frame.  We know doing the second will always work
+	 * because we reserve space above.  We prefer appending
+	 * as this typically has better DMA alignment properties.
+	 */
+	for (m = m1; m->m_next != NULL; m = m->m_next)
+		;
+	pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len;
+	if (pad) {
+		if (M_TRAILINGSPACE(m) < pad) {		/* prepend to second */
+			m2->m_data -= pad;
+			m2->m_len += pad;
+			m2->m_pkthdr.len += pad;
+		} else {				/* append to first */
+			m->m_len += pad;
+			m1->m_pkthdr.len += pad;
+		}
+	}
+
+	/*
+	 * Now, stick 'em together.
+	 */
+	m->m_next = m2;			/* NB: last mbuf from above */
+	m1->m_pkthdr.len += m2->m_pkthdr.len;
+
+	vap->iv_stats.is_amsdu_encap++;
+
+	return m1;
+bad:
+	vap->iv_stats.is_amsdu_encapfail++;
+	if (m1 != NULL)
+		m_freem(m1);
+	if (m2 != NULL)
+		m_freem(m2);
+	return NULL;
+}
+
+
 static void
 ff_transmit(struct ieee80211_node *ni, struct mbuf *m)
 {
@@ -605,6 +714,7 @@ ff_approx_txtime(struct ieee80211_node *
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211vap *vap = ni->ni_vap;
 	uint32_t framelen;
+	uint32_t frame_time;
 
 	/*
 	 * Approximate the frame length to be transmitted. A swag to add
@@ -621,7 +731,21 @@ ff_approx_txtime(struct ieee80211_node *
 		framelen += 24;
 	if (m2 != NULL)
 		framelen += m2->m_pkthdr.len;
-	return ieee80211_compute_duration(ic->ic_rt, framelen, ni->ni_txrate, 0);
+
+	/*
+	 * For now, we assume non-shortgi, 20MHz, just because I want to
+	 * at least test 802.11n.
+	 */
+	if (ni->ni_txrate & IEEE80211_RATE_MCS)
+		frame_time = ieee80211_compute_duration_ht(framelen,
+		    ni->ni_txrate,
+		    IEEE80211_HT_RC_2_STREAMS(ni->ni_txrate),
+		    0, /* isht40 */
+		    0); /* isshortgi */
+	else
+		frame_time = ieee80211_compute_duration(ic->ic_rt, framelen,
+			    ni->ni_txrate, 0);
+	return (frame_time);
 }
 
 /*
@@ -753,6 +877,30 @@ ieee80211_ff_check(struct ieee80211_node
 	return mstaged;
 }
 
+struct mbuf *
+ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m)
+{
+	/*
+	 * XXX TODO: actually enforce the node support
+	 * and HTCAP requirements for the maximum A-MSDU
+	 * size.
+	 */
+
+	/* First: software A-MSDU transmit? */
+	if (! ieee80211_amsdu_tx_ok(ni))
+		return (m);
+
+	/* Next - EAPOL? Nope, don't aggregate; we don't QoS encap them */
+	if (m->m_flags & (M_EAPOL | M_MCAST | M_BCAST))
+		return (m);
+
+	/* Next - needs to be a data frame, non-broadcast, etc */
+	if (ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost))
+		return (m);
+
+	return (ieee80211_ff_check(ni, m));
+}
+
 void
 ieee80211_ff_node_init(struct ieee80211_node *ni)
 {

Modified: head/sys/net80211/ieee80211_superg.h
==============================================================================
--- head/sys/net80211/ieee80211_superg.h	Wed Apr  6 00:55:39 2016	(r297602)
+++ head/sys/net80211/ieee80211_superg.h	Wed Apr  6 01:21:51 2016	(r297603)
@@ -82,6 +82,27 @@ int	ieee80211_parse_athparams(struct iee
 void	ieee80211_ff_node_init(struct ieee80211_node *);
 void	ieee80211_ff_node_cleanup(struct ieee80211_node *);
 
+static inline int
+ieee80211_amsdu_tx_ok(struct ieee80211_node *ni)
+{
+
+	/* First: software A-MSDU transmit? */
+	if ((ni->ni_ic->ic_caps & IEEE80211_C_SWAMSDUTX) == 0)
+		return (0);
+
+	/* Next: does the VAP have AMSDU TX enabled? */
+	if ((ni->ni_vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX) == 0)
+		return (0);
+
+	/* Next: 11n node? (assumed that A-MSDU TX to HT nodes is ok */
+	if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
+		return (0);
+
+	/* ok, we can at least /do/ AMSDU to this node */
+	return (1);
+}
+
+struct mbuf * ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m);
 struct mbuf *ieee80211_ff_check(struct ieee80211_node *, struct mbuf *);
 void	ieee80211_ff_age(struct ieee80211com *, struct ieee80211_stageq *,
 	     int quanta);
@@ -122,6 +143,8 @@ ieee80211_ff_age_all(struct ieee80211com
 
 struct mbuf *ieee80211_ff_encap(struct ieee80211vap *, struct mbuf *,
 	    int, struct ieee80211_key *);
+struct mbuf * ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1,
+	    int hdrspace, struct ieee80211_key *key);
 
 struct mbuf *ieee80211_ff_decap(struct ieee80211_node *, struct mbuf *);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201604060121.u361LpBN027682>