Index: sys/net80211/ieee80211_node.h =================================================================== --- sys/net80211/ieee80211_node.h (revision 312853) +++ sys/net80211/ieee80211_node.h (working copy) @@ -249,6 +249,11 @@ struct ieee80211vap *ni_wdsvap; /* associated WDS vap */ void *ni_rctls; /* private ratectl state */ + + /* quiet time IE state for the given node */ + uint32_t ni_quiet_ie_set; /* Quiet time IE was seen */ + struct ieee80211_quiet_ie ni_quiet_ie; /* last seen quiet IE */ + uint64_t ni_spare[3]; }; MALLOC_DECLARE(M_80211_NODE); Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c (revision 313137) +++ sys/net80211/ieee80211_output.c (working copy) @@ -2128,7 +2128,6 @@ * Send a probe request frame with the specified ssid * and any optional information element data. */ -/* XXX VHT? */ int ieee80211_send_probereq(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], @@ -2438,7 +2437,6 @@ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: - /* XXX VHT? */ /* * asreq frame format * [2] capability information @@ -2700,7 +2698,6 @@ * Space is left to prepend and 802.11 header at the * front but it's left to the caller to fill in. */ -/* XXX VHT? */ struct mbuf * ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) { @@ -3035,7 +3032,6 @@ } } -/* XXX VHT? */ static void ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, struct ieee80211_node *ni) @@ -3163,15 +3159,23 @@ } else bo->bo_csa = frm; + bo->bo_quiet = NULL; if (vap->iv_flags & IEEE80211_F_DOTH) { - bo->bo_quiet = frm; if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && - (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { - if (vap->iv_quiet) + (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && + (vap->iv_quiet == 1)) { + /* + * We only insert the quiet IE offset if + * the quiet IE is enabled. Otherwise don't + * put it here or we'll just overwrite + * some other beacon contents. + */ + if (vap->iv_quiet) { + bo->bo_quiet = frm; frm = ieee80211_add_quiet(frm,vap, 0); + } } - } else - bo->bo_quiet = frm; + } if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { bo->bo_erp = frm; @@ -3239,7 +3243,6 @@ /* * Allocate a beacon frame and fillin the appropriate bits. */ -/* XXX VHT? */ struct mbuf * ieee80211_beacon_alloc(struct ieee80211_node *ni) { @@ -3252,6 +3255,14 @@ uint8_t *frm; /* + * Update the "We're putting the quiet IE in the beacon" state. + */ + if (vap->iv_quiet == 1) + vap->iv_flags_ext |= IEEE80211_FEXT_QUIET_IE; + else if (vap->iv_quiet == 0) + vap->iv_flags_ext &= ~IEEE80211_FEXT_QUIET_IE; + + /* * beacon frame format * * Note: This needs updating for 802.11-2012. @@ -3286,7 +3297,6 @@ * NB: we allocate the max space required for the TIM bitmap. * XXX how big is this? */ - /* XXX VHT? */ pktlen = 8 /* time stamp */ + sizeof(uint16_t) /* beacon interval */ + sizeof(uint16_t) /* capabilities */ @@ -3392,6 +3402,42 @@ return 1; /* just assume length changed */ } + /* + * Handle the quiet time element being added and removed. + * Again, for now we just cheat and reconstruct the whole + * beacon - that way the gap is provided as appropriate. + * + * So, track whether we have already added the IE versus + * whether we want to be adding the IE. + */ + if ((vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE) && + (vap->iv_quiet == 0)) { + /* + * Quiet time beacon IE enabled, but it's disabled; + * recalc + */ + vap->iv_flags_ext &= ~IEEE80211_FEXT_QUIET_IE; + ieee80211_beacon_construct(m, + mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); + /* XXX do WME aggressive mode processing? */ + IEEE80211_UNLOCK(ic); + return 1; /* just assume length changed */ + } + + if (((vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE) == 0) && + (vap->iv_quiet == 1)) { + /* + * Quiet time beacon IE disabled, but it's now enabled; + * recalc + */ + vap->iv_flags_ext |= IEEE80211_FEXT_QUIET_IE; + ieee80211_beacon_construct(m, + mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); + /* XXX do WME aggressive mode processing? */ + IEEE80211_UNLOCK(ic); + return 1; /* just assume length changed */ + } + wh = mtod(m, struct ieee80211_frame *); /* @@ -3600,10 +3646,17 @@ vap->iv_csa_count++; /* NB: don't clear IEEE80211_BEACON_CSA */ } + + /* + * Only add the quiet time IE if we've enabled it + * as appropriate. + */ if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && - (vap->iv_flags_ext & IEEE80211_FEXT_DFS) ){ - if (vap->iv_quiet) + (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { + if (vap->iv_quiet && + (vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE)) { ieee80211_add_quiet(bo->bo_quiet, vap, 1); + } } if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { /* Index: sys/net80211/ieee80211_proto.h =================================================================== --- sys/net80211/ieee80211_proto.h (revision 312853) +++ sys/net80211/ieee80211_proto.h (working copy) @@ -391,6 +391,8 @@ IEEE80211_BEACON_TDMA = 9, /* TDMA Info */ IEEE80211_BEACON_ATH = 10, /* ATH parameters */ IEEE80211_BEACON_MESHCONF = 11, /* Mesh Configuration */ + IEEE80211_BEACON_QUIET = 12, /* Quiet time IE */ + IEEE80211_BEACON_VHTINFO = 13, /* VHT information */ }; int ieee80211_beacon_update(struct ieee80211_node *, struct mbuf *, int mcast); Index: sys/net80211/ieee80211_sta.c =================================================================== --- sys/net80211/ieee80211_sta.c (revision 312853) +++ sys/net80211/ieee80211_sta.c (working copy) @@ -1319,6 +1319,27 @@ ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle))); } +#ifdef notyet +/* + * Compare two quiet IEs and return if they are equivalent. + * + * The tbttcount isnt checked - that's not part of the configuration. + */ +static int +compare_quiet_ie(const struct ieee80211_quiet_ie *q1, + const struct ieee80211_quiet_ie *q2) +{ + + if (q1->period != q2->period) + return (0); + if (le16dec(&q1->duration) != le16dec(&q2->duration)) + return (0); + if (le16dec(&q1->offset) != le16dec(&q2->offset)) + return (0); + return (1); +} +#endif + static void sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, const struct ieee80211_rx_stats *rxs, @@ -1449,8 +1470,26 @@ ht_state_change = 1; } - if (scan.quiet) + /* + * If we have a quiet time IE then report it up to + * the driver. + * + * Otherwise, inform the driver that the quiet time + * IE has disappeared - only do that once rather than + * spamming it each time. + */ + if (scan.quiet) { ic->ic_set_quiet(ni, scan.quiet); + ni->ni_quiet_ie_set = 1; + memcpy(&ni->ni_quiet_ie, scan.quiet, + sizeof(struct ieee80211_quiet_ie)); + } else { + if (ni->ni_quiet_ie_set == 1) + ic->ic_set_quiet(ni, NULL); + ni->ni_quiet_ie_set = 0; + bzero(&ni->ni_quiet_ie, + sizeof(struct ieee80211_quiet_ie)); + } if (scan.tim != NULL) { struct ieee80211_tim_ie *tim = Index: sys/net80211/ieee80211_var.h =================================================================== --- sys/net80211/ieee80211_var.h (revision 312854) +++ sys/net80211/ieee80211_var.h (working copy) @@ -638,12 +638,13 @@ #define IEEE80211_FEXT_SEQNO_OFFLOAD 0x00100000 /* CONF: driver does seqno insertion/allocation */ #define IEEE80211_FEXT_FRAG_OFFLOAD 0x00200000 /* CONF: hardware does 802.11 fragmentation + assignment */ #define IEEE80211_FEXT_VHT 0x00400000 /* CONF: VHT support */ +#define IEEE80211_FEXT_QUIET_IE 0x00800000 /* STATUS: quiet IE in a beacon has been added */ #define IEEE80211_FEXT_BITS \ "\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \ "\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \ "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC\24SCAN_OFFLOAD\25SEQNO_OFFLOAD" \ - "\26VHT" + "\26VHT\27QUIET_IE" /* ic_flags_ht/iv_flags_ht */ #define IEEE80211_FHT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */ Index: sys/dev/ath/ath_dfs/null/dfs_null.c =================================================================== --- sys/dev/ath/ath_dfs/null/dfs_null.c (revision 312862) +++ sys/dev/ath/ath_dfs/null/dfs_null.c (working copy) @@ -102,7 +102,7 @@ int ath_dfs_radar_enable(struct ath_softc *sc, struct ieee80211_channel *chan) { -#if 0 +#if 1 HAL_PHYERR_PARAM pe; /* Check if the hardware supports radar reporting */ @@ -155,7 +155,7 @@ int ath_dfs_radar_disable(struct ath_softc *sc) { -#if 0 +#if 1 HAL_PHYERR_PARAM pe; (void) ath_hal_getdfsthresh(sc->sc_ah, &pe); Index: sys/dev/ath/ath_rate/sample/sample.c =================================================================== --- sys/dev/ath/ath_rate/sample/sample.c (revision 312862) +++ sys/dev/ath/ath_rate/sample/sample.c (working copy) @@ -775,6 +775,10 @@ * XXX Don't mark the higher bit rates as also having failed; as this * unfortunately stops those rates from being tasted when trying to * TX. This happens with 11n aggregation. + * + * This is valid for higher CCK rates, higher OFDM rates, and higher + * HT rates within the current number of streams (eg MCS0..7, 8..15, + * etc.) */ if (nframes == nbad) { #if 0 Index: sys/dev/ath/if_ath.c =================================================================== --- sys/dev/ath/if_ath.c (revision 312862) +++ sys/dev/ath/if_ath.c (working copy) @@ -199,6 +199,7 @@ #ifdef ATH_ENABLE_11N static void ath_update_chw(struct ieee80211com *); #endif /* ATH_ENABLE_11N */ +static int ath_set_quiet_ie(struct ieee80211_node *, uint8_t *); static void ath_calibrate(void *); static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ath_setup_stationkey(struct ieee80211_node *); @@ -1325,6 +1326,7 @@ ic->ic_update_chw = ath_update_chw; #endif /* ATH_ENABLE_11N */ + ic->ic_set_quiet = ath_set_quiet_ie; #ifdef ATH_ENABLE_RADIOTAP_VENDOR_EXT /* @@ -2523,6 +2525,20 @@ } } +static void +ath_vap_clear_quiet_ie(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap; + struct ath_vap *avp; + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + avp = ATH_VAP(vap); + /* Quiet time handling - ensure we resync */ + memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); + } +} + static int ath_init(struct ath_softc *sc) { @@ -2569,6 +2585,9 @@ sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); + /* Clear quiet IE state for each VAP */ + ath_vap_clear_quiet_ie(sc); + ath_chan_change(sc, ic->ic_curchan); /* Let DFS at it in case it's a DFS channel */ @@ -2950,6 +2969,9 @@ sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); + /* Quiet time handling - ensure we resync */ + ath_vap_clear_quiet_ie(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); @@ -4157,9 +4179,12 @@ sc->sc_ant_tx[txant]++; if (ts->ts_finaltsi != 0) sc->sc_stats.ast_tx_altrate++; + + /* XXX TODO: should do per-pri conuters */ pri = M_WME_GETAC(bf->bf_m); if (pri >= WME_AC_VO) ic->ic_wme.wme_hipri_traffic++; + if ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ni->ni_inact = ni->ni_inact_reload; } else { @@ -5243,6 +5268,9 @@ sc->sc_rx_resetted = 1; ATH_RX_UNLOCK(sc); + /* Quiet time handling - ensure we resync */ + ath_vap_clear_quiet_ie(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, chan); @@ -5516,11 +5544,137 @@ { struct ath_softc *sc = ic->ic_softc; - DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); + //DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__); + device_printf(sc->sc_dev, "%s: called\n", __func__); + + /* + * XXX TODO: schedule a tasklet that stops things without freeing, + * walks the now stopped TX queue(s) looking for frames to retry + * as if we TX filtered them (whch may mean dropping non-ampdu frames!) + * but okay) then place them back on the software queue so they + * can have the rate control lookup done again. + */ ath_set_channel(ic); } #endif /* ATH_ENABLE_11N */ +/* + * This is called by the beacon parsing routine in the receive + * path to update the current quiet time information provided by + * an AP. + * + * This is STA specific, it doesn't take the AP TBTT/beacon slot + * offset into account. + * + * The quiet IE doesn't control the /now/ beacon interval - it + * controls the upcoming beacon interval. So, when tbtt=1, + * the quiet element programming shall be for the next beacon + * interval. There's no tbtt=0 behaviour defined, so don't. + * + * Since we're programming the next quiet interval, we have + * to keep in mind what we will see when the next beacon + * is received with potentially a quiet IE. For example, if + * quiet_period is 1, then we are always getting a quiet interval + * each TBTT - so if we just program it in upon each beacon received, + * it will constantly reflect the "next" TBTT and we will never + * let the counter stay programmed correctly. + * + * So: + * + the first time we see the quiet IE, program it and store + * the details somewhere; + * + if the quiet parameters don't change (ie, period/duration/offset) + * then just leave the programming enabled; + * + (we can "skip" beacons, so don't try to enforce tbttcount unless + * you're willing to also do the skipped beacon math); + * + if the quiet IE is removed, then halt quiet time. + */ +static int +ath_set_quiet_ie(struct ieee80211_node *ni, uint8_t *ie) +{ + struct ieee80211_quiet_ie *q; + struct ieee80211vap *vap = ni->ni_vap; + struct ath_vap *avp = ATH_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ath_softc *sc = ic->ic_softc; + + /* Verify we have a quiet time IE */ + if (ie == NULL) { + device_printf(sc->sc_dev, + "%s: called; NULL IE, disabling\n", __func__); + + ath_hal_set_quiet(sc->sc_ah, 0, 0, 0, HAL_QUIET_DISABLE); + memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); + return (0); + } + + /* If we do, verify it's actually legit */ + if (ie[0] != IEEE80211_ELEMID_QUIET) + return 0; + if (ie[1] != 6) + return 0; + + /* Note: this belongs in net80211, parsed out and everything */ + q = (void *) ie; + + /* + * Compare what we have stored to what we last saw. + * If they're the same then don't program in anything. + */ + if ((q->period == avp->quiet_ie.period) && + (le16dec(&q->duration) == le16dec(&avp->quiet_ie.duration)) && + (le16dec(&q->offset) == le16dec(&avp->quiet_ie.offset))) + return (0); + + device_printf(sc->sc_dev, + "%s: called; tbttcount=%d, period=%d, duration=%d, offset=%d\n", + __func__, + (int) q->tbttcount, + (int) q->period, + (int) le16dec(&q->duration), + (int) le16dec(&q->offset)); + + /* + * Don't program in garbage values. + */ + if (le16dec(&q->duration) >= ni->ni_intval) { + device_printf(sc->sc_dev, + "%s: invalid duration (%d)\n", __func__, + le16dec(&q->duration)); + return (0); + } + if (le16dec(&q->duration) + le16dec(&q->offset) >= ni->ni_intval) { + device_printf(sc->sc_dev, + "%s: invalid duration + offset (%d+%d)\n", __func__, + le16dec(&q->duration), + le16dec(&q->offset)); + return (0); + } + if (q->tbttcount == 0) { + device_printf(sc->sc_dev, + "%s: invalid tbttcount (0)\n", __func__); + return (0); + } + + /* + * This is a new quiet time IE config, so wait until tbttcount + * is equal to 1, and program it in. + */ + if (q->tbttcount == 1) { + device_printf(sc->sc_dev, "%s: programming\n", __func__); + ath_hal_set_quiet(sc->sc_ah, + q->period * ni->ni_intval, /* convert to TU */ + le16dec(&q->duration), /* already in TU */ + le16dec(&q->offset) + ni->ni_intval, + HAL_QUIET_ENABLE | HAL_QUIET_ADD_CURRENT_TSF); + /* note: no HAL_QUIET_ADD_SWBA_RESP_TIME; as this is for STA mode */ + + /* Update local state */ + memcpy(&avp->quiet_ie, ie, sizeof(struct ieee80211_quiet_ie)); + } + + return (0); +} + static void ath_set_channel(struct ieee80211com *ic) { @@ -5826,6 +5980,9 @@ "%s: STA; syncbeacon=1\n", __func__); sc->sc_syncbeacon = 1; + /* Quiet time handling - ensure we resync */ + memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); + if (csa_run_transition) ath_beacon_config(sc, vap); @@ -5891,6 +6048,10 @@ taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { + + /* Quiet time handling - ensure we resync */ + memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); + /* * If there are no vaps left in RUN state then * shutdown host/driver operation: @@ -5934,6 +6095,9 @@ } ATH_UNLOCK(sc); } + } else if (nstate == IEEE80211_S_SCAN) { + /* Quiet time handling - ensure we resync */ + memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); } bad: ieee80211_free_node(ni); Index: sys/dev/ath/if_ath_beacon.c =================================================================== --- sys/dev/ath/if_ath_beacon.c (revision 312862) +++ sys/dev/ath/if_ath_beacon.c (working copy) @@ -761,6 +761,12 @@ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); /* + * XXX TODO: tie into net80211 for quiet time IE update and program + * local AP timer if we require it. The process of updating the + * beacon will also update the IE with the relevant counters. + */ + + /* * Enable the CAB queue before the beacon queue to * insure cab frames are triggered by this beacon. */ @@ -917,6 +923,7 @@ ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) #define FUDGE 2 struct ath_hal *ah = sc->sc_ah; + struct ath_vap *avp = ATH_VAP(vap); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; u_int32_t nexttbtt, intval, tsftu; @@ -941,6 +948,10 @@ ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); + /* Always clear the quiet IE timers; let the next update program them */ + ath_hal_set_quiet(ah, 0, 0, 0, HAL_QUIET_DISABLE); + memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); + /* extract tstamp from last beacon and convert to TU */ nexttbtt = TSF_TO_TU(le32dec(ni->ni_tstamp.data + 4), le32dec(ni->ni_tstamp.data)); Index: sys/dev/ath/if_ath_debug.h =================================================================== --- sys/dev/ath/if_ath_debug.h (revision 312862) +++ sys/dev/ath/if_ath_debug.h (working copy) @@ -70,6 +70,7 @@ ATH_DEBUG_DIVERSITY = 0x1000000000ULL, /* Diversity logic */ ATH_DEBUG_PWRSAVE = 0x2000000000ULL, ATH_DEBUG_BTCOEX = 0x4000000000ULL, /* BT Coex */ + ATH_DEBUG_QUIETIE = 0x8000000000ULL, /* Quiet time handling */ ATH_DEBUG_ANY = 0xffffffffffffffffULL }; Index: sys/dev/ath/if_ath_tx.c =================================================================== --- sys/dev/ath/if_ath_tx.c (revision 312862) +++ sys/dev/ath/if_ath_tx.c (working copy) @@ -174,21 +174,22 @@ /* * Obtain the current TID from the given frame. * - * Non-QoS frames need to go into TID 16 (IEEE80211_NONQOS_TID.) - * This has implications for which AC/priority the packet is placed - * in. + * Non-QoS frames get mapped to a TID so frames consistently + * go on a sensible queue. */ static int ath_tx_gettid(struct ath_softc *sc, const struct mbuf *m0) { const struct ieee80211_frame *wh; - int pri = M_WME_GETAC(m0); wh = mtod(m0, const struct ieee80211_frame *); + + /* Non-QoS: map frame to a TID queue for software queueing */ if (! IEEE80211_QOS_HAS_SEQ(wh)) - return IEEE80211_NONQOS_TID; - else - return WME_AC_TO_TID(pri); + return (WME_AC_TO_TID(M_WME_GETAC(m0))); + + /* QoS - fetch the TID from the header, ignore mbuf WME */ + return (ieee80211_gettid(wh)); } static void @@ -211,30 +212,42 @@ * Determine what the correct AC queue for the given frame * should be. * - * This code assumes that the TIDs map consistently to - * the underlying hardware (or software) ath_txq. - * Since the sender may try to set an AC which is - * arbitrary, non-QoS TIDs may end up being put on - * completely different ACs. There's no way to put a - * TID into multiple ath_txq's for scheduling, so - * for now we override the AC/TXQ selection and set - * non-QOS TID frames into the BE queue. + * For QoS frames, obey the TID. That way things like + * management frames that are related to a given TID + * are thus serialised with the rest of the TID traffic, + * regardless of net80211 overriding priority. * - * This may be completely incorrect - specifically, - * some management frames may end up out of order - * compared to the QoS traffic they're controlling. - * I'll look into this later. + * For non-QoS frames, return the mbuf WMI priority. + * + * This has implications that higher priority non-QoS traffic + * may end up being scheduled before other non-QoS traffic, + * leading to out-of-sequence packets being emitted. + * + * (It'd be nice to log/count this so we can see if it + * really is a problem.) + * + * TODO: maybe we should throw multicast traffic, QoS or + * otherwise, into a separate TX queue? */ static int ath_tx_getac(struct ath_softc *sc, const struct mbuf *m0) { const struct ieee80211_frame *wh; - int pri = M_WME_GETAC(m0); + wh = mtod(m0, const struct ieee80211_frame *); + + /* + * QoS data frame (sequence number or otherwise) - + * return hardware queue mapping for the underlying + * TID. + */ if (IEEE80211_QOS_HAS_SEQ(wh)) - return pri; + return TID_TO_WME_AC(ieee80211_gettid(wh)); - return ATH_NONQOS_TID_AC; + /* + * Otherwise - return mbuf QoS pri. + */ + return (M_WME_GETAC(m0)); } void @@ -1550,6 +1563,8 @@ const HAL_RATE_TABLE *rt; HAL_BOOL shortPreamble; struct ath_node *an; + + /* XXX TODO: this pri is only used for non-QoS check, right? */ u_int pri; /* @@ -1618,7 +1633,8 @@ //flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ flags = 0; ismrr = 0; /* default no multi-rate retry*/ - pri = M_WME_GETAC(m0); /* honor classification */ + + pri = ath_tx_getac(sc, m0); /* honor classification */ /* XXX use txparams instead of fixed values */ /* * Calculate Atheros packet type from IEEE80211 packet header, @@ -1899,8 +1915,19 @@ * Determine the target hardware queue. * * For multicast frames, the txq gets overridden appropriately - * depending upon the state of PS. + * depending upon the state of PS. If powersave is enabled + * then they get added to the cabq for later transmit. * + * The "fun" issue here is that group addressed frames should + * have the sequence number from a different pool, rather than + * the per-TID pool. That means that even QoS group addressed + * frames will have a sequence number from that global value, + * which means if we transmit different group addressed frames + * at different traffic priorities, the sequence numbers will + * all be out of whack. So - chances are, the right thing + * to do here is to always put group addressed frames into the BE + * queue, and ignore the TID for queue selection. + * * For any other frame, we do a TID/QoS lookup inside the frame * to see what the TID should be. If it's a non-QoS frame, the * AC and TID are overridden. The TID/TXQ code assumes the @@ -1999,21 +2026,26 @@ /* * Don't do it whilst pending; the net80211 layer still * assigns them. + * + * Don't assign A-MPDU sequence numbers to group address + * frames; they come from a different sequence number space. */ - if (is_ampdu_tx) { + if (is_ampdu_tx && (! IEEE80211_IS_MULTICAST(wh->i_addr1))) { /* * Always call; this function will * handle making sure that null data frames - * don't get a sequence number from the current - * TID and thus mess with the BAW. + * and group-addressed frames don't get a sequence number + * from the current TID and thus mess with the BAW. */ seqno = ath_tx_tid_seqno_assign(sc, ni, bf, m0); /* - * Don't add QoS NULL frames to the BAW. + * Don't add QoS NULL frames and group-addressed frames + * to the BAW. */ if (IEEE80211_QOS_HAS_SEQ(wh) && - subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL) { + (! IEEE80211_IS_MULTICAST(wh->i_addr1)) && + (subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL)) { bf->bf_state.bfs_dobaw = 1; } } @@ -2030,7 +2062,7 @@ "%s: tid %d: ampdu pending, seqno %d\n", __func__, tid, M_SEQNO_GET(m0)); - /* This also sets up the DMA map */ + /* This also sets up the DMA map; crypto; frame parameters, etc */ r = ath_tx_normal_setup(sc, ni, bf, m0, txq); if (r != 0) @@ -2148,7 +2180,7 @@ /* Map ADDBA to the correct priority */ if (do_override) { -#if 0 +#if 1 DPRINTF(sc, ATH_DEBUG_XMIT, "%s: overriding tid %d pri %d -> %d\n", __func__, o_tid, pri, TID_TO_WME_AC(o_tid)); @@ -2156,6 +2188,14 @@ pri = TID_TO_WME_AC(o_tid); } + /* + * "pri" is the hardware queue to transmit on. + * + * Look at the description in ath_tx_start() to understand + * what needs to be "fixed" here so we just use the TID + * for QoS frames. + */ + /* Handle encryption twiddling if needed */ if (! ath_tx_tag_crypto(sc, ni, m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0, @@ -2931,6 +2971,10 @@ * Assign a sequence number manually to the given frame. * * This should only be called for A-MPDU TX frames. + * + * XXX TODO: for group addressed frames, the sequence number + * should be from NONQOS_TID, and net80211 should have + * already assigned it for us. */ static ieee80211_seq ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni, @@ -2937,17 +2981,16 @@ struct ath_buf *bf, struct mbuf *m0) { struct ieee80211_frame *wh; - int tid, pri; + int tid; ieee80211_seq seqno; uint8_t subtype; - /* TID lookup */ wh = mtod(m0, struct ieee80211_frame *); - pri = M_WME_GETAC(m0); /* honor classification */ - tid = WME_AC_TO_TID(pri); - DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pri=%d, tid=%d, qos has seq=%d\n", - __func__, pri, tid, IEEE80211_QOS_HAS_SEQ(wh)); + tid = ieee80211_gettid(wh); + DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, qos has seq=%d\n", + __func__, tid, IEEE80211_QOS_HAS_SEQ(wh)); + /* XXX Is it a control frame? Ignore */ /* Does the packet require a sequence number? */ @@ -2970,6 +3013,13 @@ /* XXX no locking for this TID? This is a bit of a problem. */ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]; INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE); + } else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* + * group addressed frames get a sequence number from + * a different sequence number space. + */ + seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]; + INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE); } else { /* Manually assign sequence number */ seqno = ni->ni_txseqs[tid];