diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c
index e0a5bdf56cdc..32de8cc95089 100644
--- a/sys/net80211/ieee80211_adhoc.c
+++ b/sys/net80211/ieee80211_adhoc.c
@@ -262,14 +262,14 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
 	uint8_t *bssid;
 	uint16_t rxseq;
 
-	if (m->m_flags & M_AMPDU) {
+	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
 		 * Fastpath for A-MPDU reorder q resubmission.  Frames
-		 * w/ M_AMPDU marked have already passed through here
-		 * but were received out of order and been held on the
-		 * reorder queue.  When resubmitted they are marked
-		 * with the M_AMPDU flag and we can bypass most of the
-		 * normal processing.
+		 * w/ M_AMPDU_MPDU marked have already passed through
+		 * here but were received out of order and been held on
+		 * the reorder queue.  When resubmitted they are marked
+		 * with the M_AMPDU_MPDU flag and we can bypass most of
+		 * the normal processing.
 		 */
 		wh = mtod(m, struct ieee80211_frame *);
 		type = IEEE80211_FC0_TYPE_DATA;
@@ -406,16 +406,12 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
 		/* XXX no power-save support */
 
 		/*
-		 * Handle A-MPDU re-ordering.  The station must be
-		 * associated and negotiated HT.  The frame must be
-		 * a QoS frame (not QoS null data) and not previously
-		 * processed for A-MPDU re-ordering.  If the frame is
-		 * to be processed directly then ieee80211_ampdu_reorder
+		 * Handle A-MPDU re-ordering.  If the frame is to be
+		 * processed directly then ieee80211_ampdu_reorder
 		 * will return 0; otherwise it has consumed the mbuf
 		 * and we should do nothing more with it.
 		 */
-		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
-		    subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+		if ((m->m_flags & M_AMPDU) &&
 		    ieee80211_ampdu_reorder(ni, m) != 0) {
 			m = NULL;
 			goto out;
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index fcd1b3fa8b8c..448bd787e3f5 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -246,13 +246,17 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
 #define	M_MORE_DATA	M_PROTO5		/* more data frames to follow */
 #define	M_FF		M_PROTO6		/* fast frame */
 #define	M_TXCB		M_PROTO7		/* do tx complete callback */
+#define	M_AMPDU_MPDU	M_PROTO8		/* ok for A-MPDU aggregation */
 #define	M_80211_TX \
-	(M_LINK0|M_WDS|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB)
+	(M_LINK0|M_WDS|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB|M_AMPDU_MPDU)
 
 /* rx path usage */
-#define	M_AMPDU		M_PROTO1		/* A-MPDU processing done */
+#define	M_AMPDU		M_PROTO1		/* A-MPDU subframe */
 #define	M_WEP		M_PROTO2		/* WEP done by hardware */
-#define	M_80211_RX	(M_AMPDU|M_WEP)
+#if 0
+#define	M_AMPDU_MPDU	M_PROTO8		/* A-MPDU re-order done */
+#endif
+#define	M_80211_RX	(M_AMPDU|M_WEP|M_AMPDU_MPDU)
 /*
  * Store WME access control bits in the vlan tag.
  * This is safe since it's done after the packet is classified
diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c
index 492ef13418cb..c876dab84fb2 100644
--- a/sys/net80211/ieee80211_hostap.c
+++ b/sys/net80211/ieee80211_hostap.c
@@ -429,14 +429,14 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m,
 	uint8_t *bssid;
 	uint16_t rxseq;
 
-	if (m->m_flags & M_AMPDU) {
+	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
 		 * Fastpath for A-MPDU reorder q resubmission.  Frames
-		 * w/ M_AMPDU marked have already passed through here
-		 * but were received out of order and been held on the
-		 * reorder queue.  When resubmitted they are marked
-		 * with the M_AMPDU flag and we can bypass most of the
-		 * normal processing.
+		 * w/ M_AMPDU_MPDU marked have already passed through
+		 * here but were received out of order and been held on
+		 * the reorder queue.  When resubmitted they are marked
+		 * with the M_AMPDU_MPDU flag and we can bypass most of
+		 * the normal processing.
 		 */
 		wh = mtod(m, struct ieee80211_frame *);
 		type = IEEE80211_FC0_TYPE_DATA;
@@ -616,16 +616,12 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m,
 		}
 
 		/*
-		 * Handle A-MPDU re-ordering.  The station must be
-		 * associated and negotiated HT.  The frame must be
-		 * a QoS frame (not QoS null data) and not previously
-		 * processed for A-MPDU re-ordering.  If the frame is
-		 * to be processed directly then ieee80211_ampdu_reorder
+		 * Handle A-MPDU re-ordering.  If the frame is to be
+		 * processed directly then ieee80211_ampdu_reorder
 		 * will return 0; otherwise it has consumed the mbuf
 		 * and we should do nothing more with it.
 		 */
-		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
-		    subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+		if ((m->m_flags & M_AMPDU) &&
 		    ieee80211_ampdu_reorder(ni, m) != 0) {
 			m = NULL;
 			goto out;
diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c
index 5356a66b5844..f3c0adf14633 100644
--- a/sys/net80211/ieee80211_ht.c
+++ b/sys/net80211/ieee80211_ht.c
@@ -335,14 +335,14 @@ ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
 /*
  * Dispatch a frame from the A-MPDU reorder queue.  The
  * frame is fed back into ieee80211_input marked with an
- * M_AMPDU flag so it doesn't come back to us (it also
+ * M_AMPDU_MPDU flag so it doesn't come back to us (it also
  * permits ieee80211_input to optimize re-processing).
  */
 static __inline void
 ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
 {
-	m->m_flags |= M_AMPDU;	/* bypass normal processing */
-	/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
+	m->m_flags |= M_AMPDU_MPDU;	/* bypass normal processing */
+	/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU_MPDU set */
 	(void) ieee80211_input(ni, m, 0, 0, 0);
 }
 
@@ -517,11 +517,19 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
 	uint8_t tid;
 	int off;
 
+	KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU,
+	    ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags));
 	KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
 
 	/* NB: m_len known to be sufficient */
 	wh = mtod(m, struct ieee80211_qosframe *);
-	KASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA, ("not QoS data"));
+	if (wh->i_fc[0] != IEEE80211_FC0_QOSDATA) {
+		/*
+		 * Not QoS data, shouldn't get here but just
+		 * return it to the caller for processing.
+		 */
+		return PROCESS;
+	}
 
 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
 		tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index b1488ca4aa20..9df35f36de22 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1050,8 +1050,13 @@ ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m)
 			if (IEEE80211_AMPDU_RUNNING(tap)) {
 				/*
 				 * Operational, mark frame for aggregation.
+				 *
+				 * NB: We support only immediate BA's for
+				 * AMPDU which means we set the QoS control
+				 * field to "normal ack" (0) to get "implicit
+				 * block ack" behaviour.
 				 */
-				qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+				m->m_flags |= M_AMPDU_MPDU;
 			} else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
 			    ic->ic_ampdu_enable(ni, tap)) {
 				/*
@@ -1066,8 +1071,23 @@ ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m)
 		qos[1] = 0;
 		wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
 
-		*(uint16_t *)wh->i_seq =
-		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+		if ((m->m_flags & M_AMPDU_MPDU) == 0) {
+			/*
+			 * NB: don't assign a sequence # to potential
+			 * aggregates; we expect this happens at the
+			 * point the frame comes off any aggregation q
+			 * as otherwise we may introduce holes in the
+			 * BA sequence space and/or make window accouting
+			 * more difficult.
+			 *
+			 * XXX may want to control this with a driver
+			 * capability; this may also change when we pull
+			 * aggregation up into net80211
+			 */
+			*(uint16_t *)wh->i_seq =
+			    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+			ni->ni_txseqs[tid]++;
+		}
 		ni->ni_txseqs[tid]++;
 	} else {
 		*(uint16_t *)wh->i_seq =
diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
index af25bb658c65..d98c6dda9706 100644
--- a/sys/net80211/ieee80211_sta.c
+++ b/sys/net80211/ieee80211_sta.c
@@ -494,14 +494,14 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
 	uint8_t *bssid;
 	uint16_t rxseq;
 
-	if (m->m_flags & M_AMPDU) {
+	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
 		 * Fastpath for A-MPDU reorder q resubmission.  Frames
-		 * w/ M_AMPDU marked have already passed through here
-		 * but were received out of order and been held on the
-		 * reorder queue.  When resubmitted they are marked
-		 * with the M_AMPDU flag and we can bypass most of the
-		 * normal processing.
+		 * w/ M_AMPDU_MPDU marked have already passed through
+		 * here but were received out of order and been held on
+		 * the reorder queue.  When resubmitted they are marked
+		 * with the M_AMPDU_MPDU flag and we can bypass most of
+		 * the normal processing.
 		 */
 		wh = mtod(m, struct ieee80211_frame *);
 		type = IEEE80211_FC0_TYPE_DATA;
@@ -595,16 +595,12 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
 			goto out;		/* XXX */
 		}
 		/*
-		 * Handle A-MPDU re-ordering.  The station must be
-		 * associated and negotiated HT.  The frame must be
-		 * a QoS frame (not QoS null data) and not previously
-		 * processed for A-MPDU re-ordering.  If the frame is
-		 * to be processed directly then ieee80211_ampdu_reorder
+		 * Handle A-MPDU re-ordering.  If the frame is to be
+		 * processed directly then ieee80211_ampdu_reorder
 		 * will return 0; otherwise it has consumed the mbuf
 		 * and we should do nothing more with it.
 		 */
-		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
-		    subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+		if ((m->m_flags & M_AMPDU) &&
 		    (dir == IEEE80211_FC1_DIR_FROMDS ||
 		     dir == IEEE80211_FC1_DIR_DSTODS) &&
 		    ieee80211_ampdu_reorder(ni, m) != 0) {
diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c
index ede739d61441..ff55ed7ab6dd 100644
--- a/sys/net80211/ieee80211_wds.c
+++ b/sys/net80211/ieee80211_wds.c
@@ -468,14 +468,14 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m,
 	uint8_t dir, type, subtype, qos;
 	uint16_t rxseq;
 
-	if (m->m_flags & M_AMPDU) {
+	if (m->m_flags & M_AMPDU_MPDU) {
 		/*
 		 * Fastpath for A-MPDU reorder q resubmission.  Frames
-		 * w/ M_AMPDU marked have already passed through here
-		 * but were received out of order and been held on the
-		 * reorder queue.  When resubmitted they are marked
-		 * with the M_AMPDU flag and we can bypass most of the
-		 * normal processing.
+		 * w/ M_AMPDU_MPDU marked have already passed through
+		 * here but were received out of order and been held on
+		 * the reorder queue.  When resubmitted they are marked
+		 * with the M_AMPDU_MPDU flag and we can bypass most of
+		 * the normal processing.
 		 */
 		wh = mtod(m, struct ieee80211_frame *);
 		type = IEEE80211_FC0_TYPE_DATA;
@@ -590,16 +590,12 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m,
 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
 			ni->ni_inact = ni->ni_inact_reload;
 		/*
-		 * Handle A-MPDU re-ordering.  The station must be
-		 * associated and negotiated HT.  The frame must be
-		 * a QoS frame (not QoS null data) and not previously
-		 * processed for A-MPDU re-ordering.  If the frame is
-		 * to be processed directly then ieee80211_ampdu_reorder
+		 * Handle A-MPDU re-ordering.  If the frame is to be
+		 * processed directly then ieee80211_ampdu_reorder
 		 * will return 0; otherwise it has consumed the mbuf
 		 * and we should do nothing more with it.
 		 */
-		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
-		    subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+		if ((m->m_flags & M_AMPDU) &&
 		    ieee80211_ampdu_reorder(ni, m) != 0) {
 			m = NULL;
 			goto out;