aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/net80211/ieee80211_ht.h4
-rw-r--r--sys/net80211/ieee80211_node.c4
-rw-r--r--sys/net80211/ieee80211_output.c123
-rw-r--r--sys/net80211/ieee80211_sta.c6
-rw-r--r--sys/net80211/ieee80211_superg.c198
-rw-r--r--sys/net80211/ieee80211_superg.h23
6 files changed, 286 insertions, 72 deletions
diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h
index e2b331854dff..c0464119aa7c 100644
--- a/sys/net80211/ieee80211_ht.h
+++ b/sys/net80211/ieee80211_ht.h
@@ -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 & \
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 520687b0a4e0..66afb7401069 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -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
/*
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index b2580d1a5c85..1882a2634401 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -191,51 +191,68 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m,
* 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.
+ * 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.
*/
-#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);
+ 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, struct ieee80211_node *ni,
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, struct ieee80211_node *ni,
} else {
#ifdef IEEE80211_SUPPORT_SUPERG
/*
- * Aggregated frame.
+ * 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".
*/
- m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key);
+ 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, struct ieee80211_node *ni,
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, struct ieee80211_node *ni,
*(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__);
}
diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
index 16d801cf0fcc..9c1707b90a82 100644
--- a/sys/net80211/ieee80211_sta.c
+++ b/sys/net80211/ieee80211_sta.c
@@ -1703,12 +1703,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
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.
*
diff --git a/sys/net80211/ieee80211_superg.c b/sys/net80211/ieee80211_superg.c
index 8be098a0fcac..1879d6204c2b 100644
--- a/sys/net80211/ieee80211_superg.c
+++ b/sys/net80211/ieee80211_superg.c
@@ -99,18 +99,22 @@ ieee80211_superg_attach(struct ieee80211com *ic)
{
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 *vap, struct mbuf *m1, int hdrspace,
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 *vap, struct mbuf *m1, int hdrspace,
/*
* 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 *vap, struct mbuf *m1, int hdrspace,
}
/*
- * 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 *vap, struct mbuf *m1, int hdrspace,
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 *ni,
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 *ni,
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 *ni, struct mbuf *m)
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)
{
diff --git a/sys/net80211/ieee80211_superg.h b/sys/net80211/ieee80211_superg.h
index 4df138632fcb..ced3c9b68243 100644
--- a/sys/net80211/ieee80211_superg.h
+++ b/sys/net80211/ieee80211_superg.h
@@ -82,6 +82,27 @@ int ieee80211_parse_athparams(struct ieee80211_node *, uint8_t *,
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 *ic, int quanta)
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 *);