Index: sys/compat/linuxkpi/common/include/linux/gfp.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/gfp.h (revision 297057) +++ sys/compat/linuxkpi/common/include/linux/gfp.h (working copy) @@ -55,6 +55,7 @@ #define GFP_HIGHUSER_MOVABLE M_WAITOK #define GFP_IOFS M_NOWAIT #define GFP_NOIO M_NOWAIT +#define GFP_DMA M_NOWAIT static inline void * page_address(struct page *page) Index: sys/compat/linuxkpi/common/include/linux/kernel.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/kernel.h (revision 297057) +++ sys/compat/linuxkpi/common/include/linux/kernel.h (working copy) @@ -64,9 +64,10 @@ #define BUILD_BUG_ON(x) CTASSERT(!(x)) -#define BUG() panic("BUG") -#define BUG_ON(condition) do { if (condition) BUG(); } while(0) -#define WARN_ON BUG_ON +#define BUG() printf("BUG") +#define BUG_ON(condition) WARN(condition, "bug!") +#define WARN_ON(condition) WARN(condition, "warn!") +#define WARN_ON_ONCE(condition) WARN(condition, "warn!") #undef ALIGN #define ALIGN(x, y) roundup2((x), (y)) Index: sys/dev/otus/if_otus.c =================================================================== --- sys/dev/otus/if_otus.c (revision 297057) +++ sys/dev/otus/if_otus.c (working copy) @@ -180,6 +180,8 @@ static int otus_updateedca(struct ieee80211com *); static void otus_updateedca_locked(struct otus_softc *); static void otus_updateslot(struct otus_softc *); +static void otus_set_operating_mode(struct otus_softc *sc); +static void otus_set_rx_filter(struct otus_softc *sc); int otus_init_mac(struct otus_softc *); uint32_t otus_phy_get_def(struct otus_softc *, uint32_t); int otus_set_board_values(struct otus_softc *, @@ -949,7 +951,7 @@ for (i = 0; i < ndata; i++) { struct otus_tx_cmd *dp = &cmd[i]; - dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT | M_ZERO); dp->odata = NULL; if (dp->buf == NULL) { device_printf(sc->sc_dev, @@ -1013,7 +1015,7 @@ struct otus_data *dp = &data[i]; dp->sc = sc; dp->m = NULL; - dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT | M_ZERO); if (dp->buf == NULL) { device_printf(sc->sc_dev, "could not allocate buffer\n"); @@ -1127,6 +1129,7 @@ STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else bf = NULL; + /* XXX bzero? */ return (bf); } @@ -1209,7 +1212,6 @@ struct otus_vap *uvp = OTUS_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct otus_softc *sc = ic->ic_softc; - struct ieee80211_node *ni; enum ieee80211_state ostate; ostate = vap->iv_state; @@ -1224,18 +1226,20 @@ /* XXX TODO: more fleshing out! */ switch (nstate) { + case IEEE80211_S_INIT: + otus_set_operating_mode(sc); + otus_set_rx_filter(sc); + break; case IEEE80211_S_RUN: - ni = ieee80211_ref_node(vap->iv_bss); - if (ic->ic_opmode == IEEE80211_M_STA) { otus_updateslot(sc); - otus_set_bssid(sc, ni->ni_bssid); + otus_set_operating_mode(sc); + otus_set_rx_filter(sc); /* Start calibration timer. */ taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_to, hz); } - ieee80211_free_node(ni); break; default: break; @@ -2340,6 +2344,7 @@ otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_L, lo); otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_H, hi); r = otus_write_barrier(sc); + /* XXX operating mode? filter? */ OTUS_UNLOCK(sc); return (r); } @@ -2438,7 +2443,6 @@ otus_write(sc, AR_MAC_REG_ACK_EXTENSION, 0x40); otus_write(sc, AR_MAC_REG_RETRY_MAX, 0); - otus_write(sc, AR_MAC_REG_SNIFFER, 0x2000000); otus_write(sc, AR_MAC_REG_RX_THRESHOLD, 0xc1f80); otus_write(sc, AR_MAC_REG_RX_PE_DELAY, 0x70); otus_write(sc, AR_MAC_REG_EIFS_AND_SIFS, 0xa144000); @@ -2450,13 +2454,14 @@ otus_write(sc, AR_MAC_REG_BCN_HT1, 0x8000170); otus_write(sc, AR_MAC_REG_BACKOFF_PROTECT, 0x105); otus_write(sc, AR_MAC_REG_AMPDU_FACTOR, 0x10000a); - /* Filter any control frames, BAR is bit 24. */ -// otus_write(sc, AR_MAC_REG_FRAMETYPE_FILTER, 0x0500ffff); -// otus_write(sc, AR_MAC_REG_RX_CONTROL, 0x1); + + otus_set_rx_filter(sc); + otus_write(sc, AR_MAC_REG_BASIC_RATE, 0x150f); otus_write(sc, AR_MAC_REG_MANDATORY_RATE, 0x150f); otus_write(sc, AR_MAC_REG_RTS_CTS_RATE, 0x10b01bb); otus_write(sc, AR_MAC_REG_ACK_TPC, 0x4003c1e); + /* Enable LED0 and LED1. */ otus_write(sc, AR_GPIO_REG_PORT_TYPE, 0x3); otus_write(sc, AR_GPIO_REG_PORT_DATA, 0x3); @@ -3052,55 +3057,88 @@ #endif } +static uint8_t zero_macaddr[IEEE80211_ADDR_LEN] = { 0,0,0,0,0,0 }; + /* - * TODO: - * - * + If in monitor mode, set BSSID to all zeros, else the node BSSID. - * + Handle STA + monitor (eg tcpdump/promisc/radiotap) as well as - * pure monitor mode. + * Set up operating mode, MAC/BSS address and RX filter. */ -static int +static void otus_set_operating_mode(struct otus_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; - uint32_t rx_ctrl; - uint32_t frm_filt; - uint32_t cam_mode; - uint32_t rx_sniffer; + struct ieee80211vap *vap; + uint32_t cam_mode = AR_MAC_CAM_DEFAULTS; + uint32_t rx_ctrl = AR_MAC_RX_CTRL_DEAGG | AR_MAC_RX_CTRL_SHORT_FILTER; + uint32_t sniffer = AR_MAC_SNIFFER_DEFAULTS; + uint32_t enc_mode = 0x78; /* XXX */ + const uint8_t *macaddr; + uint8_t bssid[IEEE80211_ADDR_LEN]; + struct ieee80211_node *ni; OTUS_LOCK_ASSERT(sc); - /* XXX TODO: too many magic constants */ - rx_ctrl = 0x1; - /* Filter any control frames, BAR is bit 24. */ - frm_filt = 0x0500ffff; - cam_mode = 0x0f000002; /* XXX STA */ - rx_sniffer = 0x20000000; + /* + * If we're in sniffer mode or we don't have a MAC + * address assigned, ensure it gets reset to all-zero. + */ + IEEE80211_ADDR_COPY(bssid, zero_macaddr); + vap = TAILQ_FIRST(&ic->ic_vaps); + macaddr = ic->ic_macaddr; switch (ic->ic_opmode) { case IEEE80211_M_STA: - cam_mode = 0x0f000002; /* XXX STA */ - rx_ctrl = 0x1; - frm_filt = 0x0500ffff; - rx_sniffer = 0x20000000; + if (vap) { + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_ADDR_COPY(bssid, ni->ni_bssid); + ieee80211_free_node(ni); + } + cam_mode |= AR_MAC_CAM_STA; + rx_ctrl |= AR_MAC_RX_CTRL_PASS_TO_HOST; break; case IEEE80211_M_MONITOR: - cam_mode = 0x0f000002; /* XXX STA */ - rx_ctrl = 0x1; - frm_filt = 0xffffffff; - rx_sniffer = 0x20000001; - break; + /* + * Note: monitor mode ends up causing the MAC to + * generate ACK frames for everything it sees. + * So don't do that; instead just put it in STA mode + * and disable RX filters. + */ default: + cam_mode |= AR_MAC_CAM_STA; + rx_ctrl |= AR_MAC_RX_CTRL_PASS_TO_HOST; break; } - otus_write(sc, AR_MAC_REG_SNIFFER, rx_sniffer); + /* + * TODO: if/when we do hardware encryption, ensure it's + * disabled if the NIC is in monitor mode. + */ + otus_write(sc, AR_MAC_REG_SNIFFER, sniffer); otus_write(sc, AR_MAC_REG_CAM_MODE, cam_mode); - otus_write(sc, AR_MAC_REG_FRAMETYPE_FILTER, frm_filt); - otus_write(sc, AR_MAC_REG_RX_CONTROL, cam_mode); + otus_write(sc, AR_MAC_REG_ENCRYPTION, enc_mode); + otus_write(sc, AR_MAC_REG_RX_CONTROL, rx_ctrl); + otus_set_macaddr(sc, macaddr); + otus_set_bssid(sc, bssid); + /* XXX barrier? */ +} - (void) otus_write_barrier(sc); - return (0); +static void +otus_set_rx_filter(struct otus_softc *sc) +{ +// struct ieee80211com *ic = &sc->sc_ic; + + OTUS_LOCK_ASSERT(sc); + +#if 0 + if (ic->ic_allmulti > 0 || ic->ic_promisc > 0 || + ic->ic_opmode == IEEE80211_M_MONITOR) { + otus_write(sc, AR_MAC_REG_FRAMETYPE_FILTER, 0xff00ffff); + } else { +#endif + /* Filter any control frames, BAR is bit 24. */ + otus_write(sc, AR_MAC_REG_FRAMETYPE_FILTER, 0x0500ffff); +#if 0 + } +#endif } int @@ -3124,7 +3162,8 @@ return error; } - (void) otus_set_macaddr(sc, ic->ic_macaddr); + otus_set_operating_mode(sc); + otus_set_rx_filter(sc); (void) otus_set_operating_mode(sc); sc->bb_reset = 1; /* Force cold reset. */ Index: sys/dev/urtwn/if_urtwn.c =================================================================== --- sys/dev/urtwn/if_urtwn.c (revision 297175) +++ sys/dev/urtwn/if_urtwn.c (working copy) @@ -70,6 +70,9 @@ #include #include #include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif #include #include @@ -577,6 +580,7 @@ #endif | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_SWAMSDUTX /* Do software A-MSDU TX */ ; ic->ic_cryptocaps = @@ -1131,7 +1135,13 @@ (void)ieee80211_input_all(ic, m, rssi - nf, nf); } - +#if 0 + /* XXX TODO: this ages the frames 100ms; which is wrong */ + /* XXX TODO: we likely don't need this */ +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_ff_age_all(ic, 100); +#endif +#endif URTWN_LOCK(sc); m = next; } @@ -1161,6 +1171,9 @@ if (data->ni != NULL) /* not a beacon frame */ ieee80211_tx_complete(data->ni, data->m, status); + if (sc->sc_tx_n_active > 0) + sc->sc_tx_n_active--; + data->ni = NULL; data->m = NULL; @@ -1269,6 +1282,9 @@ urtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtwn_softc *sc = usbd_xfer_softc(xfer); +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = &sc->sc_ic; +#endif struct urtwn_data *data; URTWN_ASSERT_LOCKED(sc); @@ -1287,6 +1303,7 @@ if (data == NULL) { URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: empty pending queue\n", __func__); + sc->sc_tx_n_active = 0; goto finish; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); @@ -1293,6 +1310,7 @@ STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); + sc->sc_tx_n_active++; break; default: data = STAILQ_FIRST(&sc->sc_tx_active); @@ -1307,6 +1325,22 @@ break; } finish: +#ifdef IEEE80211_SUPPORT_SUPERG + /* + * If the TX active queue drops below a certain + * threshold, ensure we age fast-frames out so they're + * transmitted. + */ + if (sc->sc_tx_n_active <= 1) { + /* XXX ew - net80211 should defer this for us! */ + URTWN_UNLOCK(sc); + ieee80211_ff_flush(ic, WME_AC_VO); + ieee80211_ff_flush(ic, WME_AC_VI); + ieee80211_ff_flush(ic, WME_AC_BE); + ieee80211_ff_flush(ic, WME_AC_BK); + URTWN_LOCK(sc); + } +#endif /* Kick-start more transmit */ urtwn_start(sc); } @@ -3153,6 +3187,11 @@ } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; + + URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: called; m=%p\n", + __func__, + m); + if (urtwn_tx_data(sc, ni, m, bf) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); @@ -5326,6 +5365,10 @@ struct urtwn_data *bf; int error; + URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: called; m=%p\n", + __func__, + m); + /* prevent management frames from being sent if we're not ready */ URTWN_LOCK(sc); if (!(sc->sc_flags & URTWN_RUNNING)) { Index: sys/dev/urtwn/if_urtwnvar.h =================================================================== --- sys/dev/urtwn/if_urtwnvar.h (revision 297058) +++ sys/dev/urtwn/if_urtwnvar.h (working copy) @@ -22,7 +22,9 @@ #define URTWN_HOST_CMD_RING_COUNT 32 #define URTWN_RXBUFSZ (16 * 1024) -#define URTWN_TXBUFSZ (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN) +//#define URTWN_TXBUFSZ (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN) +/* Leave enough space for an A-MSDU frame */ +#define URTWN_TXBUFSZ (16 * 1024) #define URTWN_RX_DESC_SIZE (sizeof(struct r92c_rx_stat)) #define URTWN_TX_DESC_SIZE (sizeof(struct r92c_tx_desc)) @@ -195,6 +197,7 @@ urtwn_datahead sc_rx_inactive; struct urtwn_data sc_tx[URTWN_TX_LIST_COUNT]; urtwn_datahead sc_tx_active; + int sc_tx_n_active; urtwn_datahead sc_tx_inactive; urtwn_datahead sc_tx_pending; Index: sys/net80211/ieee80211.h =================================================================== --- sys/net80211/ieee80211.h (revision 297404) +++ sys/net80211/ieee80211.h (working copy) @@ -224,6 +224,23 @@ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \ (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) +/* Return the QoS field from an arbitrary frame header */ +static inline uint8_t * +ieee80211_get_qos_ctl(struct ieee80211_frame *hdr) +{ + /* Both TODS/FROMDS should be set */ + uint8_t t; + + t = IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS; + if ((hdr->i_fc[1] & t) == t) { + /* 4addr */ + return (uint8_t *) hdr + 30; + } else { + /* 3addr */ + return (uint8_t *) hdr + 24; + } +} + /* * WME/802.11e information element. */ Index: sys/net80211/ieee80211_freebsd.h =================================================================== --- sys/net80211/ieee80211_freebsd.h (revision 297405) +++ sys/net80211/ieee80211_freebsd.h (working copy) @@ -263,7 +263,7 @@ #define M_EAPOL M_PROTO3 /* PAE/EAPOL frame */ #define M_PWR_SAV M_PROTO4 /* bypass PS handling */ #define M_MORE_DATA M_PROTO5 /* more data frames to follow */ -#define M_FF M_PROTO6 /* fast frame */ +#define M_FF M_PROTO6 /* fast frame / A-MSDU */ #define M_TXCB M_PROTO7 /* do tx complete callback */ #define M_AMPDU_MPDU M_PROTO8 /* ok for A-MPDU aggregation */ #define M_FRAG M_PROTO9 /* frame fragmentation */ Index: sys/net80211/ieee80211_node.c =================================================================== --- sys/net80211/ieee80211_node.c (revision 297405) +++ sys/net80211/ieee80211_node.c (working copy) @@ -1011,8 +1011,10 @@ /* * Cleanup any HT-related state. */ - if (ni->ni_flags & IEEE80211_NODE_HT) + if (ni->ni_flags & IEEE80211_NODE_HT) { ieee80211_ht_node_cleanup(ni); + ieee80211_ff_node_cleanup(ni); + } #ifdef IEEE80211_SUPPORT_SUPERG else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) ieee80211_ff_node_cleanup(ni); Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c (revision 297404) +++ sys/net80211/ieee80211_output.c (working copy) @@ -215,26 +215,38 @@ } /* - * 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 to see if we should attempt A-MSDU / fast-frames + * encapsulation. We can do A-MSDU/FF encapsulation in + * combination with A-MPDU aggregation as well. + */ +#ifdef IEEE80211_SUPPORT_SUPERG + /* + * 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 do this /with/ the A-MPDU check above. + * It's dumb that we're double counting the packet + * count right now; we should move the counting + * to one place instead. * - * 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. + * XXX NOTE: we shouldn't do this for 802.3 encap + * drivers either! */ -#ifdef IEEE80211_SUPPORT_SUPERG - else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { + 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); } } @@ -1229,6 +1241,7 @@ ieee80211_seq seqno; int meshhdrsize, meshae; uint8_t *qos; + int is_amsdu = 0; IEEE80211_TX_LOCK_ASSERT(ic); @@ -1383,9 +1396,19 @@ } 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 +1544,13 @@ 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 +1574,14 @@ *(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__); } Index: sys/net80211/ieee80211_phy.c =================================================================== --- sys/net80211/ieee80211_phy.c (revision 297404) +++ sys/net80211/ieee80211_phy.c (working copy) @@ -590,7 +590,7 @@ #define HT_STF 4 #define HT_LTF(n) ((n) * 4) -#define HT_RC_2_MCS(_rc) ((_rc) & 0xf) +#define HT_RC_2_MCS(_rc) ((_rc) & 0x1f) #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) #define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) Index: sys/net80211/ieee80211_phy.h =================================================================== --- sys/net80211/ieee80211_phy.h (revision 297404) +++ sys/net80211/ieee80211_phy.h (working copy) @@ -194,6 +194,14 @@ */ uint8_t ieee80211_rate2plcp(int, enum ieee80211_phytype); +/* + * 802.11n rate manipulation. + */ + +#define IEEE80211_HT_RC_2_MCS(_rc) ((_rc) & 0x1f) +#define IEEE80211_HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define IEEE80211_IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) + uint32_t ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate, int streams, int isht40, int isShortGI); Index: sys/net80211/ieee80211_sta.c =================================================================== --- sys/net80211/ieee80211_sta.c (revision 297405) +++ sys/net80211/ieee80211_sta.c (working copy) @@ -1703,6 +1703,9 @@ ieee80211_setup_basic_htrates(ni, htinfo); ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); + + /* Always do this, just to cleanup */ + ieee80211_ff_node_init(ni); } else { #ifdef IEEE80211_SUPPORT_SUPERG if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH)) Index: sys/net80211/ieee80211_superg.c =================================================================== --- sys/net80211/ieee80211_superg.c (revision 297404) +++ sys/net80211/ieee80211_superg.c (working copy) @@ -99,7 +99,9 @@ { struct ieee80211_superg *sg; +#if 0 if (ic->ic_caps & IEEE80211_C_FF) { +#endif sg = (struct ieee80211_superg *) IEEE80211_MALLOC( sizeof(struct ieee80211_superg), M_80211_VAP, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); @@ -109,7 +111,9 @@ return; } ic->ic_superg = sg; +#if 0 } +#endif ieee80211_ffagemax = msecs_to_ticks(150); } @@ -353,16 +357,15 @@ 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 @@ /* * 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 @@ } /* - * 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 @@ return m1; bad: + vap->iv_stats.is_ff_encapfail++; if (m1 != NULL) m_freem(m1); if (m2 != NULL) @@ -461,6 +462,114 @@ 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 @@ 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 @@ 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 @@ 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) { Index: sys/net80211/ieee80211_superg.h =================================================================== --- sys/net80211/ieee80211_superg.h (revision 297404) +++ sys/net80211/ieee80211_superg.h (working copy) @@ -82,6 +82,27 @@ 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 @@ 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 *); Index: sys/net80211/ieee80211_var.h =================================================================== --- sys/net80211/ieee80211_var.h (revision 297404) +++ sys/net80211/ieee80211_var.h (working copy) @@ -647,6 +647,7 @@ #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ #define IEEE80211_C_SWSLEEP 0x00080000 /* CAPABILITY: do sleep here */ +#define IEEE80211_C_SWAMSDUTX 0x00100000 /* CAPABILITY: software A-MSDU TX */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */