From owner-svn-src-head@freebsd.org Wed Apr 6 01:21:53 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 1D8E7B04550; Wed, 6 Apr 2016 01:21:53 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id DD60016E8; Wed, 6 Apr 2016 01:21:52 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u361Lq7f027688; Wed, 6 Apr 2016 01:21:52 GMT (envelope-from adrian@FreeBSD.org) Received: (from adrian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u361LpBN027682; Wed, 6 Apr 2016 01:21:51 GMT (envelope-from adrian@FreeBSD.org) Message-Id: <201604060121.u361LpBN027682@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: adrian set sender to adrian@FreeBSD.org using -f From: Adrian Chadd Date: Wed, 6 Apr 2016 01:21:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r297603 - head/sys/net80211 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 Apr 2016 01:21:53 -0000 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 *);