mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-22 11:17:19 +00:00
22a3aee637
PS-POLL support. This implements PS-POLL awareness i nthe * Implement frame "leaking", which allows for a software queue to be scheduled even though it's asleep * Track whether a frame has been leaked or not * Leak out a single non-AMPDU frame when transmitting aggregates * Queue BAR frames if the node is asleep * Direct-dispatch the rest of control and management frames. This allows for things like re-association to occur (which involves sending probe req/resp as well as assoc request/response) when the node is asleep and then tries reassociating. * Limit how many frames can set in the software node queue whilst the node is asleep. net80211 is already buffering frames for us so this is mostly just paranoia. * Add a PS-POLL method which leaks out a frame if there's something in the software queue, else it calls net80211's ps-poll routine. Since the ath PS-POLL routine marks the node as having a single frame to leak, either a software queued frame would leak, OR the next queued frame would leak. The next queued frame could be something from the net80211 power save queue, OR it could be a NULL frame from net80211. TODO: * Don't transmit further BAR frames (eg via a timeout) if the node is currently asleep. Otherwise we may end up exhausting management frames due to the lots of queued BAR frames. I may just undo this bit later on and direct-dispatch BAR frames even if the node is asleep. * It would be nice to burst out a single A-MPDU frame if both ends support this. I may end adding a FreeBSD IE soon to negotiate this power save behaviour. * I should make STAs timeout of power save mode if they've been in power save for more than a handful of seconds. This way cards that get "stuck" in power save mode don't stay there for the "inactivity" timeout in net80211. * Move the queue depth check into the driver layer (ath_start / ath_transmit) rather than doing it in the TX path. * There could be some naughty corner cases with ps-poll leaking. Specifically, if net80211 generates a NULL data frame whilst another transmitter sends a normal data frame out net80211 output / transmit, we need to ensure that the NULL data frame goes out first. This is one of those things that should occur inside the VAP/ic TX lock. Grr, more investigations to do.. Tested: * STA: AR5416, AR9280 * AP: AR5416, AR9280, AR9160
897 lines
25 KiB
C
897 lines
25 KiB
C
/*-
|
|
* Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
|
* redistribution must be conditioned upon including a substantially
|
|
* similar Disclaimer requirement for further binary redistribution.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGES.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_ath.h"
|
|
#include "opt_wlan.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/kthread.h>
|
|
#include <sys/taskqueue.h>
|
|
#include <sys/priv.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/ethernet.h>
|
|
#include <net/if_llc.h>
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
#include <net80211/ieee80211_regdomain.h>
|
|
#ifdef IEEE80211_SUPPORT_SUPERG
|
|
#include <net80211/ieee80211_superg.h>
|
|
#endif
|
|
#ifdef IEEE80211_SUPPORT_TDMA
|
|
#include <net80211/ieee80211_tdma.h>
|
|
#endif
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#endif
|
|
|
|
#include <dev/ath/if_athvar.h>
|
|
#include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */
|
|
#include <dev/ath/ath_hal/ah_diagcodes.h>
|
|
|
|
#ifdef ATH_TX99_DIAG
|
|
#include <dev/ath/ath_tx99/ath_tx99.h>
|
|
#endif
|
|
|
|
#include <dev/ath/if_ath_tx.h> /* XXX for some support functions */
|
|
#include <dev/ath/if_ath_tx_ht.h>
|
|
#include <dev/ath/if_athrate.h>
|
|
#include <dev/ath/if_ath_debug.h>
|
|
|
|
/*
|
|
* XXX net80211?
|
|
*/
|
|
#define IEEE80211_AMPDU_SUBFRAME_DEFAULT 32
|
|
|
|
#define ATH_AGGR_DELIM_SZ 4 /* delimiter size */
|
|
#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */
|
|
/* number of delimiters for encryption padding */
|
|
#define ATH_AGGR_ENCRYPTDELIM 10
|
|
|
|
/*
|
|
* returns delimiter padding required given the packet length
|
|
*/
|
|
#define ATH_AGGR_GET_NDELIM(_len) \
|
|
(((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ? \
|
|
(ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2)
|
|
|
|
#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
|
|
|
|
int ath_max_4ms_framelen[4][32] = {
|
|
[MCS_HT20] = {
|
|
3212, 6432, 9648, 12864, 19300, 25736, 28952, 32172,
|
|
6424, 12852, 19280, 25708, 38568, 51424, 57852, 64280,
|
|
9628, 19260, 28896, 38528, 57792, 65532, 65532, 65532,
|
|
12828, 25656, 38488, 51320, 65532, 65532, 65532, 65532,
|
|
},
|
|
[MCS_HT20_SGI] = {
|
|
3572, 7144, 10720, 14296, 21444, 28596, 32172, 35744,
|
|
7140, 14284, 21428, 28568, 42856, 57144, 64288, 65532,
|
|
10700, 21408, 32112, 42816, 64228, 65532, 65532, 65532,
|
|
14256, 28516, 42780, 57040, 65532, 65532, 65532, 65532,
|
|
},
|
|
[MCS_HT40] = {
|
|
6680, 13360, 20044, 26724, 40092, 53456, 60140, 65532,
|
|
13348, 26700, 40052, 53400, 65532, 65532, 65532, 65532,
|
|
20004, 40008, 60016, 65532, 65532, 65532, 65532, 65532,
|
|
26644, 53292, 65532, 65532, 65532, 65532, 65532, 65532,
|
|
},
|
|
[MCS_HT40_SGI] = {
|
|
7420, 14844, 22272, 29696, 44544, 59396, 65532, 65532,
|
|
14832, 29668, 44504, 59340, 65532, 65532, 65532, 65532,
|
|
22232, 44464, 65532, 65532, 65532, 65532, 65532, 65532,
|
|
29616, 59232, 65532, 65532, 65532, 65532, 65532, 65532,
|
|
}
|
|
};
|
|
|
|
/*
|
|
* XXX should be in net80211
|
|
*/
|
|
static int ieee80211_mpdudensity_map[] = {
|
|
0, /* IEEE80211_HTCAP_MPDUDENSITY_NA */
|
|
25, /* IEEE80211_HTCAP_MPDUDENSITY_025 */
|
|
50, /* IEEE80211_HTCAP_MPDUDENSITY_05 */
|
|
100, /* IEEE80211_HTCAP_MPDUDENSITY_1 */
|
|
200, /* IEEE80211_HTCAP_MPDUDENSITY_2 */
|
|
400, /* IEEE80211_HTCAP_MPDUDENSITY_4 */
|
|
800, /* IEEE80211_HTCAP_MPDUDENSITY_8 */
|
|
1600, /* IEEE80211_HTCAP_MPDUDENSITY_16 */
|
|
};
|
|
|
|
/*
|
|
* XXX should be in the HAL/net80211 ?
|
|
*/
|
|
#define BITS_PER_BYTE 8
|
|
#define OFDM_PLCP_BITS 22
|
|
#define HT_RC_2_MCS(_rc) ((_rc) & 0x7f)
|
|
#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
|
|
#define L_STF 8
|
|
#define L_LTF 8
|
|
#define L_SIG 4
|
|
#define HT_SIG 8
|
|
#define HT_STF 4
|
|
#define HT_LTF(_ns) (4 * (_ns))
|
|
#define SYMBOL_TIME(_ns) ((_ns) << 2) // ns * 4 us
|
|
#define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) // ns * 3.6 us
|
|
#define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2)
|
|
#define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18)
|
|
#define IS_HT_RATE(_rate) ((_rate) & 0x80)
|
|
|
|
const uint32_t bits_per_symbol[][2] = {
|
|
/* 20MHz 40MHz */
|
|
{ 26, 54 }, // 0: BPSK
|
|
{ 52, 108 }, // 1: QPSK 1/2
|
|
{ 78, 162 }, // 2: QPSK 3/4
|
|
{ 104, 216 }, // 3: 16-QAM 1/2
|
|
{ 156, 324 }, // 4: 16-QAM 3/4
|
|
{ 208, 432 }, // 5: 64-QAM 2/3
|
|
{ 234, 486 }, // 6: 64-QAM 3/4
|
|
{ 260, 540 }, // 7: 64-QAM 5/6
|
|
{ 52, 108 }, // 8: BPSK
|
|
{ 104, 216 }, // 9: QPSK 1/2
|
|
{ 156, 324 }, // 10: QPSK 3/4
|
|
{ 208, 432 }, // 11: 16-QAM 1/2
|
|
{ 312, 648 }, // 12: 16-QAM 3/4
|
|
{ 416, 864 }, // 13: 64-QAM 2/3
|
|
{ 468, 972 }, // 14: 64-QAM 3/4
|
|
{ 520, 1080 }, // 15: 64-QAM 5/6
|
|
{ 78, 162 }, // 16: BPSK
|
|
{ 156, 324 }, // 17: QPSK 1/2
|
|
{ 234, 486 }, // 18: QPSK 3/4
|
|
{ 312, 648 }, // 19: 16-QAM 1/2
|
|
{ 468, 972 }, // 20: 16-QAM 3/4
|
|
{ 624, 1296 }, // 21: 64-QAM 2/3
|
|
{ 702, 1458 }, // 22: 64-QAM 3/4
|
|
{ 780, 1620 }, // 23: 64-QAM 5/6
|
|
{ 104, 216 }, // 24: BPSK
|
|
{ 208, 432 }, // 25: QPSK 1/2
|
|
{ 312, 648 }, // 26: QPSK 3/4
|
|
{ 416, 864 }, // 27: 16-QAM 1/2
|
|
{ 624, 1296 }, // 28: 16-QAM 3/4
|
|
{ 832, 1728 }, // 29: 64-QAM 2/3
|
|
{ 936, 1944 }, // 30: 64-QAM 3/4
|
|
{ 1040, 2160 }, // 31: 64-QAM 5/6
|
|
};
|
|
|
|
/*
|
|
* Fill in the rate array information based on the current
|
|
* node configuration and the choices made by the rate
|
|
* selection code and ath_buf setup code.
|
|
*
|
|
* Later on, this may end up also being made by the
|
|
* rate control code, but for now it can live here.
|
|
*
|
|
* This needs to be called just before the packet is
|
|
* queued to the software queue or hardware queue,
|
|
* so all of the needed fields in bf_state are setup.
|
|
*/
|
|
void
|
|
ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf)
|
|
{
|
|
struct ieee80211_node *ni = bf->bf_node;
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
const HAL_RATE_TABLE *rt = sc->sc_currates;
|
|
struct ath_rc_series *rc = bf->bf_state.bfs_rc;
|
|
uint8_t rate;
|
|
int i;
|
|
|
|
for (i = 0; i < ATH_RC_NUM; i++) {
|
|
rc[i].flags = 0;
|
|
if (rc[i].tries == 0)
|
|
continue;
|
|
|
|
rate = rt->info[rc[i].rix].rateCode;
|
|
|
|
/*
|
|
* Only enable short preamble for legacy rates
|
|
*/
|
|
if ((! IS_HT_RATE(rate)) && bf->bf_state.bfs_shpream)
|
|
rate |= rt->info[rc[i].rix].shortPreamble;
|
|
|
|
/*
|
|
* Save this, used by the TX and completion code
|
|
*/
|
|
rc[i].ratecode = rate;
|
|
|
|
if (bf->bf_state.bfs_txflags &
|
|
(HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA))
|
|
rc[i].flags |= ATH_RC_RTSCTS_FLAG;
|
|
|
|
/* Only enable shortgi, 2040, dual-stream if HT is set */
|
|
if (IS_HT_RATE(rate)) {
|
|
rc[i].flags |= ATH_RC_HT_FLAG;
|
|
|
|
if (ni->ni_chw == 40)
|
|
rc[i].flags |= ATH_RC_CW40_FLAG;
|
|
|
|
if (ni->ni_chw == 40 &&
|
|
ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 &&
|
|
ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
|
|
rc[i].flags |= ATH_RC_SGI_FLAG;
|
|
|
|
if (ni->ni_chw == 20 &&
|
|
ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20 &&
|
|
ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
|
|
rc[i].flags |= ATH_RC_SGI_FLAG;
|
|
|
|
/*
|
|
* If we have STBC TX enabled and the receiver
|
|
* can receive (at least) 1 stream STBC, AND it's
|
|
* MCS 0-7, AND we have at least two chains enabled,
|
|
* enable STBC.
|
|
*/
|
|
if (ic->ic_htcaps & IEEE80211_HTCAP_TXSTBC &&
|
|
ni->ni_htcap & IEEE80211_HTCAP_RXSTBC_1STREAM &&
|
|
(sc->sc_cur_txchainmask > 1) &&
|
|
HT_RC_2_STREAMS(rate) == 1) {
|
|
rc[i].flags |= ATH_RC_STBC_FLAG;
|
|
}
|
|
|
|
/*
|
|
* XXX TODO: LDPC
|
|
*/
|
|
|
|
/*
|
|
* Dual / Triple stream rate?
|
|
*/
|
|
if (HT_RC_2_STREAMS(rate) == 2)
|
|
rc[i].flags |= ATH_RC_DS_FLAG;
|
|
else if (HT_RC_2_STREAMS(rate) == 3)
|
|
rc[i].flags |= ATH_RC_TS_FLAG;
|
|
}
|
|
|
|
/*
|
|
* Calculate the maximum TX power cap for the current
|
|
* node.
|
|
*/
|
|
rc[i].tx_power_cap = ieee80211_get_node_txpower(ni);
|
|
|
|
/*
|
|
* Calculate the maximum 4ms frame length based
|
|
* on the MCS rate, SGI and channel width flags.
|
|
*/
|
|
if ((rc[i].flags & ATH_RC_HT_FLAG) &&
|
|
(HT_RC_2_MCS(rate) < 32)) {
|
|
int j;
|
|
if (rc[i].flags & ATH_RC_CW40_FLAG) {
|
|
if (rc[i].flags & ATH_RC_SGI_FLAG)
|
|
j = MCS_HT40_SGI;
|
|
else
|
|
j = MCS_HT40;
|
|
} else {
|
|
if (rc[i].flags & ATH_RC_SGI_FLAG)
|
|
j = MCS_HT20_SGI;
|
|
else
|
|
j = MCS_HT20;
|
|
}
|
|
rc[i].max4msframelen =
|
|
ath_max_4ms_framelen[j][HT_RC_2_MCS(rate)];
|
|
} else
|
|
rc[i].max4msframelen = 0;
|
|
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
|
|
"%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n",
|
|
__func__, i, rate, rc[i].flags, rc[i].max4msframelen);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return the number of delimiters to be added to
|
|
* meet the minimum required mpdudensity.
|
|
*
|
|
* Caller should make sure that the rate is HT.
|
|
*
|
|
* TODO: is this delimiter calculation supposed to be the
|
|
* total frame length, the hdr length, the data length (including
|
|
* delimiters, padding, CRC, etc) or ?
|
|
*
|
|
* TODO: this should ensure that the rate control information
|
|
* HAS been setup for the first rate.
|
|
*
|
|
* TODO: ensure this is only called for MCS rates.
|
|
*
|
|
* TODO: enforce MCS < 31
|
|
*/
|
|
static int
|
|
ath_compute_num_delims(struct ath_softc *sc, struct ath_buf *first_bf,
|
|
uint16_t pktlen)
|
|
{
|
|
const HAL_RATE_TABLE *rt = sc->sc_currates;
|
|
struct ieee80211_node *ni = first_bf->bf_node;
|
|
struct ieee80211vap *vap = ni->ni_vap;
|
|
int ndelim, mindelim = 0;
|
|
int mpdudensity; /* in 1/100'th of a microsecond */
|
|
uint8_t rc, rix, flags;
|
|
int width, half_gi;
|
|
uint32_t nsymbits, nsymbols;
|
|
uint16_t minlen;
|
|
|
|
/*
|
|
* vap->iv_ampdu_density is a value, rather than the actual
|
|
* density.
|
|
*/
|
|
if (vap->iv_ampdu_density > IEEE80211_HTCAP_MPDUDENSITY_16)
|
|
mpdudensity = 1600; /* maximum density */
|
|
else
|
|
mpdudensity = ieee80211_mpdudensity_map[vap->iv_ampdu_density];
|
|
|
|
/* Select standard number of delimiters based on frame length */
|
|
ndelim = ATH_AGGR_GET_NDELIM(pktlen);
|
|
|
|
/*
|
|
* If encryption is enabled, add extra delimiters to let the
|
|
* crypto hardware catch up. This could be tuned per-MAC and
|
|
* per-rate, but for now we'll simply assume encryption is
|
|
* always enabled.
|
|
*
|
|
* Also note that the Atheros reference driver inserts two
|
|
* delimiters by default for pre-AR9380 peers. This will
|
|
* include "that" required delimiter.
|
|
*/
|
|
ndelim += ATH_AGGR_ENCRYPTDELIM;
|
|
|
|
/*
|
|
* For AR9380, there's a minimum number of delimeters
|
|
* required when doing RTS.
|
|
*
|
|
* XXX TODO: this is only needed if (a) RTS/CTS is enabled, and
|
|
* XXX (b) this is the first sub-frame in the aggregate.
|
|
*/
|
|
if (sc->sc_use_ent && (sc->sc_ent_cfg & AH_ENT_RTSCTS_DELIM_WAR)
|
|
&& ndelim < AH_FIRST_DESC_NDELIMS)
|
|
ndelim = AH_FIRST_DESC_NDELIMS;
|
|
|
|
/*
|
|
* If sc_delim_min_pad is non-zero, enforce it as the minimum
|
|
* pad delimiter count.
|
|
*/
|
|
if (sc->sc_delim_min_pad != 0)
|
|
ndelim = MAX(ndelim, sc->sc_delim_min_pad);
|
|
|
|
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
|
|
"%s: pktlen=%d, ndelim=%d, mpdudensity=%d\n",
|
|
__func__, pktlen, ndelim, mpdudensity);
|
|
|
|
/*
|
|
* If the MPDU density is 0, we can return here.
|
|
* Otherwise, we need to convert the desired mpdudensity
|
|
* into a byte length, based on the rate in the subframe.
|
|
*/
|
|
if (mpdudensity == 0)
|
|
return ndelim;
|
|
|
|
/*
|
|
* Convert desired mpdu density from microeconds to bytes based
|
|
* on highest rate in rate series (i.e. first rate) to determine
|
|
* required minimum length for subframe. Take into account
|
|
* whether high rate is 20 or 40Mhz and half or full GI.
|
|
*/
|
|
rix = first_bf->bf_state.bfs_rc[0].rix;
|
|
rc = rt->info[rix].rateCode;
|
|
flags = first_bf->bf_state.bfs_rc[0].flags;
|
|
width = !! (flags & ATH_RC_CW40_FLAG);
|
|
half_gi = !! (flags & ATH_RC_SGI_FLAG);
|
|
|
|
/*
|
|
* mpdudensity is in 1/100th of a usec, so divide by 100
|
|
*/
|
|
if (half_gi)
|
|
nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity);
|
|
else
|
|
nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity);
|
|
nsymbols /= 100;
|
|
|
|
if (nsymbols == 0)
|
|
nsymbols = 1;
|
|
|
|
nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
|
|
minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
|
|
|
|
/*
|
|
* Min length is the minimum frame length for the
|
|
* required MPDU density.
|
|
*/
|
|
if (pktlen < minlen) {
|
|
mindelim = (minlen - pktlen) / ATH_AGGR_DELIM_SZ;
|
|
ndelim = MAX(mindelim, ndelim);
|
|
}
|
|
|
|
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
|
|
"%s: pktlen=%d, minlen=%d, rix=%x, rc=%x, width=%d, hgi=%d, ndelim=%d\n",
|
|
__func__, pktlen, minlen, rix, rc, width, half_gi, ndelim);
|
|
|
|
return ndelim;
|
|
}
|
|
|
|
/*
|
|
* Fetch the aggregation limit.
|
|
*
|
|
* It's the lowest of the four rate series 4ms frame length.
|
|
*/
|
|
static int
|
|
ath_get_aggr_limit(struct ath_softc *sc, struct ath_buf *bf)
|
|
{
|
|
int amin = ATH_AGGR_MAXSIZE;
|
|
int i;
|
|
|
|
if (sc->sc_aggr_limit > 0 && sc->sc_aggr_limit < ATH_AGGR_MAXSIZE)
|
|
amin = sc->sc_aggr_limit;
|
|
|
|
for (i = 0; i < ATH_RC_NUM; i++) {
|
|
if (bf->bf_state.bfs_rc[i].tries == 0)
|
|
continue;
|
|
amin = MIN(amin, bf->bf_state.bfs_rc[i].max4msframelen);
|
|
}
|
|
|
|
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: max frame len= %d\n",
|
|
__func__, amin);
|
|
|
|
return amin;
|
|
}
|
|
|
|
/*
|
|
* Setup a 11n rate series structure
|
|
*
|
|
* This should be called for both legacy and MCS rates.
|
|
*
|
|
* This uses the rate series stuf from ath_tx_rate_fill_rcflags().
|
|
*
|
|
* It, along with ath_buf_set_rate, must be called -after- a burst
|
|
* or aggregate is setup.
|
|
*/
|
|
static void
|
|
ath_rateseries_setup(struct ath_softc *sc, struct ieee80211_node *ni,
|
|
struct ath_buf *bf, HAL_11N_RATE_SERIES *series)
|
|
{
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
HAL_BOOL shortPreamble = AH_FALSE;
|
|
const HAL_RATE_TABLE *rt = sc->sc_currates;
|
|
int i;
|
|
int pktlen;
|
|
struct ath_rc_series *rc = bf->bf_state.bfs_rc;
|
|
|
|
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
|
|
(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE))
|
|
shortPreamble = AH_TRUE;
|
|
|
|
/*
|
|
* If this is the first frame in an aggregate series,
|
|
* use the aggregate length.
|
|
*/
|
|
if (bf->bf_state.bfs_aggr)
|
|
pktlen = bf->bf_state.bfs_al;
|
|
else
|
|
pktlen = bf->bf_state.bfs_pktlen;
|
|
|
|
/*
|
|
* XXX TODO: modify this routine to use the bfs_rc[x].flags
|
|
* XXX fields.
|
|
*/
|
|
memset(series, 0, sizeof(HAL_11N_RATE_SERIES) * 4);
|
|
for (i = 0; i < ATH_RC_NUM; i++) {
|
|
/* Only set flags for actual TX attempts */
|
|
if (rc[i].tries == 0)
|
|
continue;
|
|
|
|
series[i].Tries = rc[i].tries;
|
|
|
|
/*
|
|
* XXX TODO: When the NIC is capable of three stream TX,
|
|
* transmit 1/2 stream rates on two streams.
|
|
*
|
|
* This reduces the power consumption of the NIC and
|
|
* keeps it within the PCIe slot power limits.
|
|
*/
|
|
series[i].ChSel = sc->sc_cur_txchainmask;
|
|
|
|
/*
|
|
* Setup rate and TX power cap for this series.
|
|
*/
|
|
series[i].Rate = rt->info[rc[i].rix].rateCode;
|
|
series[i].RateIndex = rc[i].rix;
|
|
series[i].tx_power_cap = rc[i].tx_power_cap;
|
|
|
|
/*
|
|
* Enable RTS/CTS as appropriate.
|
|
*/
|
|
if (rc[i].flags & ATH_RC_RTSCTS_FLAG)
|
|
series[i].RateFlags |= HAL_RATESERIES_RTS_CTS;
|
|
|
|
/*
|
|
* 11n rate? Update 11n flags.
|
|
*/
|
|
if (rc[i].flags & ATH_RC_HT_FLAG) {
|
|
if (rc[i].flags & ATH_RC_CW40_FLAG)
|
|
series[i].RateFlags |= HAL_RATESERIES_2040;
|
|
|
|
if (rc[i].flags & ATH_RC_SGI_FLAG)
|
|
series[i].RateFlags |= HAL_RATESERIES_HALFGI;
|
|
|
|
if (rc[i].flags & ATH_RC_STBC_FLAG)
|
|
series[i].RateFlags |= HAL_RATESERIES_STBC;
|
|
}
|
|
|
|
/*
|
|
* PktDuration doesn't include slot, ACK, RTS, etc timing -
|
|
* it's just the packet duration
|
|
*/
|
|
if (rc[i].flags & ATH_RC_HT_FLAG) {
|
|
series[i].PktDuration =
|
|
ath_computedur_ht(pktlen
|
|
, series[i].Rate
|
|
, HT_RC_2_STREAMS(series[i].Rate)
|
|
, series[i].RateFlags & HAL_RATESERIES_2040
|
|
, series[i].RateFlags & HAL_RATESERIES_HALFGI);
|
|
} else {
|
|
if (shortPreamble)
|
|
series[i].Rate |=
|
|
rt->info[rc[i].rix].shortPreamble;
|
|
series[i].PktDuration = ath_hal_computetxtime(ah,
|
|
rt, pktlen, rc[i].rix, shortPreamble);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
ath_rateseries_print(struct ath_softc *sc, HAL_11N_RATE_SERIES *series)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ATH_RC_NUM; i++) {
|
|
device_printf(sc->sc_dev ,"series %d: rate %x; tries %d; "
|
|
"pktDuration %d; chSel %d; txpowcap %d, rateFlags %x\n",
|
|
i,
|
|
series[i].Rate,
|
|
series[i].Tries,
|
|
series[i].PktDuration,
|
|
series[i].ChSel,
|
|
series[i].tx_power_cap,
|
|
series[i].RateFlags);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Setup the 11n rate scenario and burst duration for the given TX descriptor
|
|
* list.
|
|
*
|
|
* This isn't useful for sending beacon frames, which has different needs
|
|
* wrt what's passed into the rate scenario function.
|
|
*/
|
|
void
|
|
ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni,
|
|
struct ath_buf *bf)
|
|
{
|
|
HAL_11N_RATE_SERIES series[4];
|
|
struct ath_desc *ds = bf->bf_desc;
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
int is_pspoll = (bf->bf_state.bfs_atype == HAL_PKT_TYPE_PSPOLL);
|
|
int ctsrate = bf->bf_state.bfs_ctsrate;
|
|
int flags = bf->bf_state.bfs_txflags;
|
|
|
|
/* Setup rate scenario */
|
|
memset(&series, 0, sizeof(series));
|
|
|
|
ath_rateseries_setup(sc, ni, bf, series);
|
|
|
|
#if 0
|
|
ath_rateseries_print(sc, series);
|
|
#endif
|
|
|
|
/* Set rate scenario */
|
|
/*
|
|
* Note: Don't allow hardware to override the duration on
|
|
* ps-poll packets.
|
|
*/
|
|
ath_hal_set11nratescenario(ah, ds,
|
|
!is_pspoll, /* whether to override the duration or not */
|
|
ctsrate, /* rts/cts rate */
|
|
series, /* 11n rate series */
|
|
4, /* number of series */
|
|
flags);
|
|
|
|
/* Set burst duration */
|
|
/*
|
|
* This is only required when doing 11n burst, not aggregation
|
|
* ie, if there's a second frame in a RIFS or A-MPDU burst
|
|
* w/ >1 A-MPDU frame bursting back to back.
|
|
* Normal A-MPDU doesn't do bursting -between- aggregates.
|
|
*
|
|
* .. and it's highly likely this won't ever be implemented
|
|
*/
|
|
//ath_hal_set11nburstduration(ah, ds, 8192);
|
|
}
|
|
|
|
/*
|
|
* Form an aggregate packet list.
|
|
*
|
|
* This function enforces the aggregate restrictions/requirements.
|
|
*
|
|
* These are:
|
|
*
|
|
* + The aggregate size maximum (64k for AR9160 and later, 8K for
|
|
* AR5416 when doing RTS frame protection.)
|
|
* + Maximum number of sub-frames for an aggregate
|
|
* + The aggregate delimiter size, giving MACs time to do whatever is
|
|
* needed before each frame
|
|
* + Enforce the BAW limit
|
|
*
|
|
* Each descriptor queued should have the DMA setup.
|
|
* The rate series, descriptor setup, linking, etc is all done
|
|
* externally. This routine simply chains them together.
|
|
* ath_tx_setds_11n() will take care of configuring the per-
|
|
* descriptor setup, and ath_buf_set_rate() will configure the
|
|
* rate control.
|
|
*
|
|
* The TID lock is required for the entirety of this function.
|
|
*
|
|
* If some code in another thread adds to the head of this
|
|
* list, very strange behaviour will occur. Since retransmission is the
|
|
* only reason this will occur, and this routine is designed to be called
|
|
* from within the scheduler task, it won't ever clash with the completion
|
|
* task.
|
|
*
|
|
* So if you want to call this from an upper layer context (eg, to direct-
|
|
* dispatch aggregate frames to the hardware), please keep this in mind.
|
|
*/
|
|
ATH_AGGR_STATUS
|
|
ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an,
|
|
struct ath_tid *tid, ath_bufhead *bf_q)
|
|
{
|
|
//struct ieee80211_node *ni = &an->an_node;
|
|
struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
|
|
int nframes = 0;
|
|
uint16_t aggr_limit = 0, al = 0, bpad = 0, al_delta, h_baw;
|
|
struct ieee80211_tx_ampdu *tap;
|
|
int status = ATH_AGGR_DONE;
|
|
int prev_frames = 0; /* XXX for AR5416 burst, not done here */
|
|
int prev_al = 0; /* XXX also for AR5416 burst */
|
|
|
|
ATH_TX_LOCK_ASSERT(sc);
|
|
|
|
tap = ath_tx_get_tx_tid(an, tid->tid);
|
|
if (tap == NULL) {
|
|
status = ATH_AGGR_ERROR;
|
|
goto finish;
|
|
}
|
|
|
|
h_baw = tap->txa_wnd / 2;
|
|
|
|
for (;;) {
|
|
bf = ATH_TID_FIRST(tid);
|
|
if (bf_first == NULL)
|
|
bf_first = bf;
|
|
if (bf == NULL) {
|
|
status = ATH_AGGR_DONE;
|
|
break;
|
|
} else {
|
|
/*
|
|
* It's the first frame;
|
|
* set the aggregation limit based on the
|
|
* rate control decision that has been made.
|
|
*/
|
|
aggr_limit = ath_get_aggr_limit(sc, bf_first);
|
|
}
|
|
|
|
/* Set this early just so things don't get confused */
|
|
bf->bf_next = NULL;
|
|
|
|
/*
|
|
* If the frame doesn't have a sequence number that we're
|
|
* tracking in the BAW (eg NULL QOS data frame), we can't
|
|
* aggregate it. Stop the aggregation process; the sender
|
|
* can then TX what's in the list thus far and then
|
|
* TX the frame individually.
|
|
*/
|
|
if (! bf->bf_state.bfs_dobaw) {
|
|
status = ATH_AGGR_NONAGGR;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If any of the rates are non-HT, this packet
|
|
* can't be aggregated.
|
|
* XXX TODO: add a bf_state flag which gets marked
|
|
* if any active rate is non-HT.
|
|
*/
|
|
|
|
/*
|
|
* do not exceed aggregation limit
|
|
*/
|
|
al_delta = ATH_AGGR_DELIM_SZ + bf->bf_state.bfs_pktlen;
|
|
if (nframes &&
|
|
(aggr_limit < (al + bpad + al_delta + prev_al))) {
|
|
status = ATH_AGGR_LIMITED;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If RTS/CTS is set on the first frame, enforce
|
|
* the RTS aggregate limit.
|
|
*/
|
|
if (bf_first->bf_state.bfs_txflags &
|
|
(HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) {
|
|
if (nframes &&
|
|
(sc->sc_rts_aggr_limit <
|
|
(al + bpad + al_delta + prev_al))) {
|
|
status = ATH_AGGR_8K_LIMITED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do not exceed subframe limit.
|
|
*/
|
|
if ((nframes + prev_frames) >= MIN((h_baw),
|
|
IEEE80211_AMPDU_SUBFRAME_DEFAULT)) {
|
|
status = ATH_AGGR_LIMITED;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If the current frame has an RTS/CTS configuration
|
|
* that differs from the first frame, override the
|
|
* subsequent frame with this config.
|
|
*/
|
|
if (bf != bf_first) {
|
|
bf->bf_state.bfs_txflags &=
|
|
~ (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
|
|
bf->bf_state.bfs_txflags |=
|
|
bf_first->bf_state.bfs_txflags &
|
|
(HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
|
|
}
|
|
|
|
/*
|
|
* If the packet has a sequence number, do not
|
|
* step outside of the block-ack window.
|
|
*/
|
|
if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
|
|
SEQNO(bf->bf_state.bfs_seqno))) {
|
|
status = ATH_AGGR_BAW_CLOSED;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* this packet is part of an aggregate.
|
|
*/
|
|
ATH_TID_REMOVE(tid, bf, bf_list);
|
|
|
|
/* The TID lock is required for the BAW update */
|
|
ath_tx_addto_baw(sc, an, tid, bf);
|
|
bf->bf_state.bfs_addedbaw = 1;
|
|
|
|
/*
|
|
* XXX enforce ACK for aggregate frames (this needs to be
|
|
* XXX handled more gracefully?
|
|
*/
|
|
if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) {
|
|
device_printf(sc->sc_dev,
|
|
"%s: HAL_TXDESC_NOACK set for an aggregate frame?\n",
|
|
__func__);
|
|
bf->bf_state.bfs_txflags &= (~HAL_TXDESC_NOACK);
|
|
}
|
|
|
|
/*
|
|
* Add the now owned buffer (which isn't
|
|
* on the software TXQ any longer) to our
|
|
* aggregate frame list.
|
|
*/
|
|
TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
|
|
nframes ++;
|
|
|
|
/* Completion handler */
|
|
bf->bf_comp = ath_tx_aggr_comp;
|
|
|
|
/*
|
|
* add padding for previous frame to aggregation length
|
|
*/
|
|
al += bpad + al_delta;
|
|
|
|
/*
|
|
* Calculate delimiters needed for the current frame
|
|
*/
|
|
bf->bf_state.bfs_ndelim =
|
|
ath_compute_num_delims(sc, bf_first,
|
|
bf->bf_state.bfs_pktlen);
|
|
|
|
/*
|
|
* Calculate the padding needed from this set of delimiters,
|
|
* used when calculating if the next frame will fit in
|
|
* the aggregate.
|
|
*/
|
|
bpad = PADBYTES(al_delta) + (bf->bf_state.bfs_ndelim << 2);
|
|
|
|
/*
|
|
* Chain the buffers together
|
|
*/
|
|
if (bf_prev)
|
|
bf_prev->bf_next = bf;
|
|
bf_prev = bf;
|
|
|
|
/*
|
|
* If we're leaking frames, just return at this point;
|
|
* we've queued a single frame and we don't want to add
|
|
* any more.
|
|
*/
|
|
if (tid->an->an_leak_count) {
|
|
status = ATH_AGGR_LEAK_CLOSED;
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* terminate aggregation on a small packet boundary
|
|
*/
|
|
if (bf->bf_state.bfs_pktlen < ATH_AGGR_MINPLEN) {
|
|
status = ATH_AGGR_SHORTPKT;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
finish:
|
|
/*
|
|
* Just in case the list was empty when we tried to
|
|
* dequeue a packet ..
|
|
*/
|
|
if (bf_first) {
|
|
bf_first->bf_state.bfs_al = al;
|
|
bf_first->bf_state.bfs_nframes = nframes;
|
|
}
|
|
return status;
|
|
}
|