#include <sys/cdefs.h>
#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>
#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>
#include <dev/ath/if_ath_tx_ht.h>
#include <dev/ath/if_athrate.h>
#include <dev/ath/if_ath_debug.h>
#define IEEE80211_AMPDU_SUBFRAME_DEFAULT 32
#define ATH_AGGR_DELIM_SZ 4
#define ATH_AGGR_MINPLEN 256
#define ATH_AGGR_ENCRYPTDELIM 10
#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,
}
};
static int ieee80211_mpdudensity_map[] = {
0,
25,
50,
100,
200,
400,
800,
1600,
};
#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)
#define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5)
#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] = {
{ 26, 54 },
{ 52, 108 },
{ 78, 162 },
{ 104, 216 },
{ 156, 324 },
{ 208, 432 },
{ 234, 486 },
{ 260, 540 },
{ 52, 108 },
{ 104, 216 },
{ 156, 324 },
{ 208, 432 },
{ 312, 648 },
{ 416, 864 },
{ 468, 972 },
{ 520, 1080 },
{ 78, 162 },
{ 156, 324 },
{ 234, 486 },
{ 312, 648 },
{ 468, 972 },
{ 624, 1296 },
{ 702, 1458 },
{ 780, 1620 },
{ 104, 216 },
{ 208, 432 },
{ 312, 648 },
{ 416, 864 },
{ 624, 1296 },
{ 832, 1728 },
{ 936, 1944 },
{ 1040, 2160 },
};
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;
int do_ldpc;
int do_stbc;
do_ldpc = 0;
if ((ni->ni_vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX) &&
(ni->ni_htcap & IEEE80211_HTCAP_LDPC))
do_ldpc = 1;
if (bf->bf_flags & ATH_BUF_TOA_PROBE)
do_ldpc = 0;
do_stbc = 0;
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;
if ((! IS_HT_RATE(rate)) && bf->bf_state.bfs_shpream)
rate |= rt->info[rc[i].rix].shortPreamble;
rc[i].ratecode = rate;
if (bf->bf_state.bfs_txflags &
(HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA))
rc[i].flags |= ATH_RC_RTSCTS_FLAG;
if (! IS_HT_RATE(rate))
do_ldpc = 0;
if (IS_HT_RATE(rate)) {
rc[i].flags |= ATH_RC_HT_FLAG;
if (ni->ni_chw == NET80211_STA_RX_BW_40)
rc[i].flags |= ATH_RC_CW40_FLAG;
if (ni->ni_chw == NET80211_STA_RX_BW_40 &&
ieee80211_ht_check_tx_shortgi_40(ni) &&
(bf->bf_flags & ATH_BUF_TOA_PROBE) == 0) {
rc[i].flags |= ATH_RC_SGI_FLAG;
}
if (ni->ni_chw == NET80211_STA_RX_BW_20 &&
ieee80211_ht_check_tx_shortgi_20(ni) &&
(bf->bf_flags & ATH_BUF_TOA_PROBE) == 0) {
rc[i].flags |= ATH_RC_SGI_FLAG;
}
if (ic->ic_htcaps & IEEE80211_HTCAP_TXSTBC &&
(ni->ni_vap->iv_flags_ht & IEEE80211_FHT_STBC_TX) &&
(ni->ni_htcap & IEEE80211_HTCAP_RXSTBC) &&
(sc->sc_cur_txchainmask > 1) &&
(HT_RC_2_STREAMS(rate) == 1) &&
(bf->bf_flags & ATH_BUF_TOA_PROBE) == 0) {
rc[i].flags |= ATH_RC_STBC_FLAG;
do_stbc = 1;
}
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;
}
rc[i].tx_power_cap = ieee80211_get_node_txpower(ni);
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);
}
if (do_ldpc) {
bf->bf_state.bfs_txflags |= HAL_TXDESC_LDPC;
sc->sc_stats.ast_tx_ldpc++;
}
if (do_stbc) {
sc->sc_stats.ast_tx_stbc++;
}
}
static int
ath_compute_num_delims(struct ath_softc *sc, struct ath_buf *first_bf,
uint16_t pktlen, int is_first)
{
const HAL_RATE_TABLE *rt = sc->sc_currates;
struct ieee80211_node *ni = first_bf->bf_node;
int ndelim, mindelim = 0;
int mpdudensity;
int peer_mpdudensity;
uint8_t rc, rix, flags;
int width, half_gi;
uint32_t nsymbits, nsymbols;
uint16_t minlen;
peer_mpdudensity = ieee80211_ht_get_node_ampdu_density(ni);
if (peer_mpdudensity > IEEE80211_HTCAP_MPDUDENSITY_16)
mpdudensity = 1600;
else
mpdudensity = ieee80211_mpdudensity_map[peer_mpdudensity];
ndelim = ATH_AGGR_GET_NDELIM(pktlen);
ndelim += ATH_AGGR_ENCRYPTDELIM;
if (sc->sc_use_ent && (sc->sc_ent_cfg & AH_ENT_RTSCTS_DELIM_WAR)
&& ndelim < AH_FIRST_DESC_NDELIMS && is_first)
ndelim = AH_FIRST_DESC_NDELIMS;
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 (mpdudensity == 0)
return ndelim;
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);
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;
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;
}
static int
ath_rx_ampdu_to_byte(char a)
{
switch (a) {
case IEEE80211_HTCAP_MAXRXAMPDU_16K:
return 16384;
break;
case IEEE80211_HTCAP_MAXRXAMPDU_32K:
return 32768;
break;
case IEEE80211_HTCAP_MAXRXAMPDU_64K:
return 65536;
break;
case IEEE80211_HTCAP_MAXRXAMPDU_8K:
default:
return 8192;
break;
}
}
static int
ath_get_aggr_limit(struct ath_softc *sc, struct ieee80211_node *ni,
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;
amin = MIN(amin,
ath_rx_ampdu_to_byte(ieee80211_ht_get_node_ampdu_limit(ni)));
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: aggr_limit=%d, iv_ampdu_limit=%d, "
"peer maxrxampdu=%d, max frame len=%d\n",
__func__,
sc->sc_aggr_limit,
ni->ni_vap->iv_ampdu_limit,
_IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU),
amin);
return amin;
}
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 (bf->bf_state.bfs_aggr)
pktlen = bf->bf_state.bfs_al;
else
pktlen = bf->bf_state.bfs_pktlen;
memset(series, 0, sizeof(HAL_11N_RATE_SERIES) * 4);
for (i = 0; i < ATH_RC_NUM; i++) {
if (rc[i].tries == 0)
continue;
series[i].Tries = rc[i].tries;
series[i].ChSel = sc->sc_cur_txchainmask;
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;
if (rc[i].flags & ATH_RC_RTSCTS_FLAG)
series[i].RateFlags |= HAL_RATESERIES_RTS_CTS;
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;
}
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, AH_TRUE);
}
}
}
#ifdef ATH_DEBUG
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
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;
memset(&series, 0, sizeof(series));
ath_rateseries_setup(sc, ni, bf, series);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_XMIT)
ath_rateseries_print(sc, series);
#endif
ath_hal_set11nratescenario(ah, ds,
!is_pspoll,
ctsrate,
series,
4,
flags);
}
ATH_AGGR_STATUS
ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, ath_bufhead *bf_q)
{
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;
int prev_al = 0;
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 == NULL) {
status = ATH_AGGR_DONE;
break;
}
if (bf_first == NULL) {
bf_first = bf;
aggr_limit = ath_get_aggr_limit(sc, &an->an_node,
bf_first);
if (bf_first->bf_state.bfs_rc_maxpktlen > 0) {
aggr_limit = MIN(aggr_limit,
bf_first->bf_state.bfs_rc_maxpktlen);
}
}
bf->bf_next = NULL;
if (! bf->bf_state.bfs_dobaw) {
status = ATH_AGGR_NONAGGR;
break;
}
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 (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;
}
}
if ((nframes + prev_frames) >= MIN((h_baw),
IEEE80211_AMPDU_SUBFRAME_DEFAULT)) {
status = ATH_AGGR_LIMITED;
break;
}
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 (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
SEQNO(bf->bf_state.bfs_seqno))) {
status = ATH_AGGR_BAW_CLOSED;
break;
}
ATH_TID_REMOVE(tid, bf, bf_list);
ath_tx_addto_baw(sc, an, tid, bf);
bf->bf_state.bfs_addedbaw = 1;
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);
}
TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
nframes ++;
bf->bf_comp = ath_tx_aggr_comp;
al += bpad + al_delta;
bf->bf_state.bfs_ndelim =
ath_compute_num_delims(sc, bf_first,
bf->bf_state.bfs_pktlen, (bf_first == bf));
bpad = PADBYTES(al_delta) + (bf->bf_state.bfs_ndelim << 2);
if (bf_prev)
bf_prev->bf_next = bf;
bf_prev = bf;
if (tid->an->an_leak_count) {
status = ATH_AGGR_LEAK_CLOSED;
break;
}
#if 0
if (bf->bf_state.bfs_pktlen < ATH_AGGR_MINPLEN) {
status = ATH_AGGR_SHORTPKT;
break;
}
#endif
}
finish:
if (bf_first) {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: al=%d bytes; requested %d bytes\n",
__func__, al, bf_first->bf_state.bfs_rc_maxpktlen);
bf_first->bf_state.bfs_al = al;
bf_first->bf_state.bfs_nframes = nframes;
}
return status;
}