2
* Copyright (c) 2010 Broadcom Corporation
4
* Permission to use, copy, modify, and/or distribute this software for any
5
* purpose with or without fee is hereby granted, provided that the above
6
* copyright notice and this permission notice appear in all copies.
8
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
#include <linux/kernel.h>
24
#include <bcmendian.h>
32
#include <wlc_mac80211.h>
33
#include <wlc_phy_hal.h>
34
#include <wlc_antsel.h>
36
#include <net/mac80211.h>
37
#include <wlc_ampdu.h>
38
#include <wl_export.h>
41
#include <bcm_rpc_tp.h>
42
#include <wlc_rpctx.h>
45
#define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
46
#define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
47
#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
48
#define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
49
#define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
50
#define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
51
#define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
52
#define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
53
#define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
54
#define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
55
#define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
56
#define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
57
#define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
59
#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
60
#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
63
#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
64
#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
65
#define FFPLD_PLD_INCR 1000 /* increments in bytes */
66
#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
67
* accumulate between resets.
70
#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
72
/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
73
#define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
74
+ DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
83
WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
86
/* structure to hold tx fifo information and pre-loading state
87
* counters specific to tx underflows of ampdus
88
* some counters might be redundant with the ones in wlc or ampdu structures.
89
* This allows to maintain a specific state independantly of
90
* how often and/or when the wlc counters are updated.
92
typedef struct wlc_fifo_info {
93
u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
94
u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
95
u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
96
u32 accum_txfunfl; /* num of underflows since we modified pld params */
97
u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
98
u32 prev_txampdu; /* previous reading of tx ampdu */
99
u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
102
/* AMPDU module specific state */
104
wlc_info_t *wlc; /* pointer to main wlc structure */
105
int scb_handle; /* scb cubby handle to retrieve data from scb */
106
u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
107
u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
108
u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
109
u8 retry_limit; /* mpdu transmit retry limit */
110
u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
111
u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
112
/* per-tid mpdu transmit retry limit at regular rate */
113
u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
114
u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
115
s8 max_pdu; /* max pdus allowed in ampdu */
116
u8 dur; /* max duration of an ampdu (in msec) */
117
u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
118
u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
119
u32 ffpld_rsvd; /* number of bytes to reserve for preload */
120
u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
121
void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
122
bool mfbr; /* enable multiple fallback rate */
123
u32 tx_max_funl; /* underflows should be kept such that
124
* (tx_max_funfl*underflows) < tx frames
126
wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
131
bool waiting_status; /* To help sanity checks */
135
#define AMPDU_CLEANUPFLAG_RX (0x1)
136
#define AMPDU_CLEANUPFLAG_TX (0x2)
138
#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
139
#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
141
static void wlc_ffpld_init(ampdu_info_t *ampdu);
142
static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int f);
143
static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
145
static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
146
scb_ampdu_t *scb_ampdu,
147
u8 tid, bool override);
148
static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
150
static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur);
151
static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
152
static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
154
#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
156
static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
157
void *p, tx_status_t *txs,
161
static inline u16 pkt_txh_seqnum(wlc_info_t *wlc, void *p)
164
struct dot11_header *h;
165
txh = (d11txh_t *) PKTDATA(p);
166
h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
167
return ltoh16(h->seq) >> SEQNUM_SHIFT;
170
ampdu_info_t *wlc_ampdu_attach(wlc_info_t *wlc)
175
/* some code depends on packed structures */
176
ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
177
ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
178
ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
179
ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
180
ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
182
ampdu = kzalloc(sizeof(ampdu_info_t), GFP_ATOMIC);
184
WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem\n", wlc->pub->unit));
189
for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
190
ampdu->ini_enable[i] = true;
191
/* Disable ampdu for VO by default */
192
ampdu->ini_enable[PRIO_8021D_VO] = false;
193
ampdu->ini_enable[PRIO_8021D_NC] = false;
195
/* Disable ampdu for BK by default since not enough fifo space */
196
ampdu->ini_enable[PRIO_8021D_NONE] = false;
197
ampdu->ini_enable[PRIO_8021D_BK] = false;
199
ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
200
ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
201
ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
202
ampdu->max_pdu = AUTO;
203
ampdu->dur = AMPDU_MAX_DUR;
204
ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
206
ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
207
/* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
208
if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
209
ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
211
ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
213
/* Restrict to smaller rcv size for BMAC dongle */
214
ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
216
ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
217
ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
219
for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
220
ampdu->retry_limit_tid[i] = ampdu->retry_limit;
221
ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
224
ampdu_update_max_txlen(ampdu, ampdu->dur);
226
/* try to set ampdu to the default value */
227
wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
229
ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
230
wlc_ffpld_init(ampdu);
235
void wlc_ampdu_detach(ampdu_info_t *ampdu)
242
/* free all ini's which were to be freed on callbacks which were never called */
243
for (i = 0; i < AMPDU_INI_FREE; i++) {
244
if (ampdu->ini_free[i]) {
245
kfree(ampdu->ini_free[i]);
249
wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
253
void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
255
scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
258
WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
261
for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
262
ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
266
/* reset the ampdu state machine so that it can gracefully handle packets that were
267
* freed from the dma and tx queues during reinit
269
void wlc_ampdu_reset(ampdu_info_t *ampdu)
271
WL_NONE(("%s: Entering\n", __func__));
274
static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
276
scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
279
scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
281
/* go back to legacy size if some preloading is occuring */
282
for (i = 0; i < NUM_FFPLD_FIFO; i++) {
283
if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
284
scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
287
/* apply user override */
288
if (ampdu->max_pdu != AUTO)
289
scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
291
scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
293
if (scb_ampdu->max_rxlen)
295
min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
297
scb_ampdu->release = min(scb_ampdu->release,
298
ampdu->fifo_tb[TX_AC_BE_FIFO].
299
mcs2ampdu_table[FFPLD_MAX_MCS]);
301
ASSERT(scb_ampdu->release);
304
void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
306
scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
309
static void wlc_ffpld_init(ampdu_info_t *ampdu)
312
wlc_fifo_info_t *fifo;
314
for (j = 0; j < NUM_FFPLD_FIFO; j++) {
315
fifo = (ampdu->fifo_tb + j);
316
fifo->ampdu_pld_size = 0;
317
for (i = 0; i <= FFPLD_MAX_MCS; i++)
318
fifo->mcs2ampdu_table[i] = 255;
319
fifo->dmaxferrate = 0;
320
fifo->accum_txampdu = 0;
321
fifo->prev_txfunfl = 0;
322
fifo->accum_txfunfl = 0;
327
/* evaluate the dma transfer rate using the tx underflows as feedback.
328
* If necessary, increase tx fifo preloading. If not enough,
329
* decrease maximum ampdu size for each mcs till underflows stop
330
* Return 1 if pre-loading not active, -1 if not an underflow event,
331
* 0 if pre-loading module took care of the event.
333
static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int fid)
335
ampdu_info_t *ampdu = wlc->ampdu;
336
u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
339
u32 current_ampdu_cnt = 0;
342
wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
346
/* return if we got here for a different reason than underflows */
349
M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
350
new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
351
if (new_txunfl == 0) {
352
WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
355
fifo->prev_txfunfl = cur_txunfl;
357
if (!ampdu->tx_max_funl)
360
/* check if fifo is big enough */
361
if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
362
WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
366
if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
369
max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
370
fifo->accum_txfunfl += new_txunfl;
372
/* we need to wait for at least 10 underflows */
373
if (fifo->accum_txfunfl < 10)
376
WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
377
current_ampdu_cnt, fifo->accum_txfunfl));
380
compute the current ratio of tx unfl per ampdu.
381
When the current ampdu count becomes too
382
big while the ratio remains small, we reset
383
the current count in order to not
384
introduce too big of a latency in detecting a
385
large amount of tx underflows later.
388
txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
390
if (txunfl_ratio > ampdu->tx_max_funl) {
391
if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
392
fifo->accum_txfunfl = 0;
397
min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
399
/* In case max value max_pdu is already lower than
400
the fifo depth, there is nothing more we can do.
403
if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
404
WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
405
fifo->accum_txfunfl = 0;
409
if (fifo->ampdu_pld_size < max_pld_size) {
411
/* increment by TX_FIFO_PLD_INC bytes */
412
fifo->ampdu_pld_size += FFPLD_PLD_INCR;
413
if (fifo->ampdu_pld_size > max_pld_size)
414
fifo->ampdu_pld_size = max_pld_size;
416
/* update scb release size */
417
scb_ampdu_update_config_all(ampdu);
420
compute a new dma xfer rate for max_mpdu @ max mcs.
421
This is the minimum dma rate that
422
can acheive no unferflow condition for the current mpdu size.
424
/* note : we divide/multiply by 100 to avoid integer overflows */
427
(max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
428
/ (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
430
WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
431
fifo->dmaxferrate, fifo->ampdu_pld_size));
434
/* decrease ampdu size */
435
if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
436
if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
437
fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
438
AMPDU_NUM_MPDU_LEGACY - 1;
440
fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
442
/* recompute the table */
443
wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
445
/* update scb release size */
446
scb_ampdu_update_config_all(ampdu);
449
fifo->accum_txfunfl = 0;
453
static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
456
u32 phy_rate, dma_rate, tmp;
458
wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
460
/* recompute the dma rate */
461
/* note : we divide/multiply by 100 to avoid integer overflows */
463
min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
464
phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
467
(max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
468
/ (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
469
fifo->dmaxferrate = dma_rate;
471
/* fill up the mcs2ampdu table; do not recalc the last mcs */
472
dma_rate = dma_rate >> 7;
473
for (i = 0; i < FFPLD_MAX_MCS; i++) {
474
/* shifting to keep it within integer range */
475
phy_rate = MCS_RATE(i, true, false) >> 7;
476
if (phy_rate > dma_rate) {
477
tmp = ((fifo->ampdu_pld_size * phy_rate) /
478
((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
479
tmp = min_t(u32, tmp, 255);
480
fifo->mcs2ampdu_table[i] = (u8) tmp;
485
static void BCMFASTPATH
486
wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, void *p, uint prec)
488
scb_ampdu_t *scb_ampdu;
489
scb_ampdu_tid_ini_t *ini;
490
u8 tid = (u8) PKTPRIO(p);
492
scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
494
/* initialize initiator on first packet; sends addba req */
495
ini = SCB_AMPDU_INI(scb_ampdu, tid);
496
if (ini->magic != INI_MAGIC) {
497
ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
503
wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, void **pdu, int prec)
507
void *p, *pkt[AMPDU_MAX_MPDU];
510
u8 preamble_type = WLC_GF_PREAMBLE;
511
u8 fbr_preamble_type = WLC_GF_PREAMBLE;
512
u8 rts_preamble_type = WLC_LONG_PREAMBLE;
513
u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
515
bool rr = true, fbr = false;
516
uint i, count = 0, fifo, seg_cnt = 0;
517
u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
518
u32 ampdu_len, maxlen = 0;
519
d11txh_t *txh = NULL;
521
struct dot11_header *h;
523
scb_ampdu_t *scb_ampdu;
524
scb_ampdu_tid_ini_t *ini;
526
bool use_rts = false, use_cts = false;
527
ratespec_t rspec = 0, rspec_fallback = 0;
528
ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
529
u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
530
struct dot11_rts_frame *rts;
534
struct ieee80211_tx_info *tx_info;
543
tid = (u8) PKTPRIO(p);
544
ASSERT(tid < AMPDU_MAX_SCB_TID);
546
f = ampdu->fifo_tb + prio2fifo[tid];
548
scb = wlc->pub->global_scb;
549
ASSERT(scb->magic == SCB_MAGIC);
551
scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
553
ini = &scb_ampdu->ini[tid];
555
/* Let pressure continue to build ... */
556
qlen = pktq_plen(&qi->q, prec);
557
if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
561
wlc_ampdu_agg(ampdu, scb, p, tid);
563
if (wlc->block_datafifo) {
564
WL_ERROR(("%s: Fifo blocked\n", __func__));
567
rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
571
struct ieee80211_tx_rate *txrate;
573
tx_info = IEEE80211_SKB_CB(p);
574
txrate = tx_info->status.rates;
576
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
577
err = wlc_prep_pdu(wlc, p, &fifo);
579
WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
586
if (err == BCME_BUSY) {
587
WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq));
588
WLCNTINCR(ampdu->cnt->sduretry);
593
/* error in the packet; reject it */
594
WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc->pub->unit, seq));
595
WLCNTINCR(ampdu->cnt->sdurejected);
601
/* pkt is good to be aggregated */
602
ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
603
txh = (d11txh_t *) PKTDATA(p);
604
plcp = (u8 *) (txh + 1);
605
h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
606
seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
607
index = TX_SEQ_TO_INDEX(seq);
609
/* check mcl fields and test whether it can be agg'd */
610
mcl = ltoh16(txh->MacTxControlLow);
611
mcl &= ~TXC_AMPDU_MASK;
612
fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
614
txh->PreloadSize = 0; /* always default to 0 */
616
/* Handle retry limits */
617
if (txrate[0].count <= rr_retry_limit) {
628
/* extract the length info */
629
len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
630
: WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
632
/* retrieve null delimiter count */
633
ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
636
WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
637
wlc->pub->unit, count, len));
640
* aggregateable mpdu. For ucode/hw agg,
641
* test whether need to break or change the epoch
645
mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
646
/* refill the bits since might be a retx mpdu */
647
mcl |= TXC_STARTMSDU;
648
rts = (struct dot11_rts_frame *)&txh->rts_frame;
649
fc = ltoh16(rts->fc);
650
if ((fc & FC_KIND_MASK) == FC_RTS) {
654
if ((fc & FC_KIND_MASK) == FC_CTS) {
659
mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
660
mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
663
len = roundup(len, 4);
664
ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
666
dma_len += (u16) pkttotlen(osh, p);
668
WL_AMPDU_TX(("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n", wlc->pub->unit, ampdu_len, seg_cnt, ndelim));
670
txh->MacTxControlLow = htol16(mcl);
672
/* this packet is added */
675
/* patch the first MPDU */
677
u8 plcp0, plcp3, is40, sgi;
678
struct ieee80211_sta *sta;
680
sta = tx_info->control.sta;
686
plcp0 = txh->FragPLCPFallback[0];
687
plcp3 = txh->FragPLCPFallback[3];
690
is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
691
sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
692
mcs = plcp0 & ~MIMO_PLCP_40MHZ;
693
ASSERT(mcs < MCS_TABLE_SIZE);
695
min(scb_ampdu->max_rxlen,
696
ampdu->max_txlen[mcs][is40][sgi]);
698
WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
701
maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
705
CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
706
? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
708
/* rebuild the rspec and rspec_fallback */
709
rspec = RSPEC_MIMORATE;
710
rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
711
if (plcp[0] & MIMO_PLCP_40MHZ)
712
rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
714
if (fbr_iscck) /* CCK */
716
CCK_RSPEC(CCK_PHY2MAC_RATE
717
(txh->FragPLCPFallback[0]));
719
rspec_fallback = RSPEC_MIMORATE;
721
txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
722
if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
724
(PHY_TXC1_BW_40MHZ <<
728
if (use_rts || use_cts) {
730
wlc_rspec_to_rts_rspec(wlc, rspec, false,
733
wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
734
false, mimo_ctlchbw);
738
/* if (first mpdu for host agg) */
739
/* test whether to add more */
740
if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
741
(count == f->mcs2ampdu_table[mcs])) {
742
WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc->pub->unit, count, mcs));
746
if (count == scb_ampdu->max_pdu) {
747
WL_NONE(("Stop taking from q, reached %d deep\n",
748
scb_ampdu->max_pdu));
752
/* check to see if the next pkt is a candidate for aggregation */
753
p = pktq_ppeek(&qi->q, prec);
754
tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
757
if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
758
((u8) PKTPRIO(p) == tid)) {
761
pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
762
plen = max(scb_ampdu->min_len, plen);
764
if ((plen + ampdu_len) > maxlen) {
766
WL_ERROR(("%s: Bogus plen #1\n",
772
/* check if there are enough descriptors available */
773
if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
774
WL_ERROR(("%s: No fifo space !!!!!!\n", __func__));
778
p = pktq_pdeq(&qi->q, prec);
786
ini->tx_in_transit += count;
789
WLCNTADD(ampdu->cnt->txmpdu, count);
791
/* patch up the last txh */
792
txh = (d11txh_t *) PKTDATA(pkt[count - 1]);
793
mcl = ltoh16(txh->MacTxControlLow);
794
mcl &= ~TXC_AMPDU_MASK;
795
mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
796
txh->MacTxControlLow = htol16(mcl);
798
/* remove the null delimiter after last mpdu */
799
ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
800
txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
801
ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
803
/* remove the pad len from last mpdu */
804
fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
805
len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
806
: WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
807
ampdu_len -= roundup(len, 4) - len;
809
/* patch up the first txh & plcp */
810
txh = (d11txh_t *) PKTDATA(pkt[0]);
811
plcp = (u8 *) (txh + 1);
813
WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
814
/* mark plcp to indicate ampdu */
815
WLC_SET_MIMO_PLCP_AMPDU(plcp);
817
/* reset the mixed mode header durations */
820
wlc_calc_lsig_len(wlc, rspec, ampdu_len);
821
txh->MModeLen = htol16(mmodelen);
822
preamble_type = WLC_MM_PREAMBLE;
824
if (txh->MModeFbrLen) {
826
wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
827
txh->MModeFbrLen = htol16(mmfbrlen);
828
fbr_preamble_type = WLC_MM_PREAMBLE;
831
/* set the preload length */
832
if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
833
dma_len = min(dma_len, f->ampdu_pld_size);
834
txh->PreloadSize = htol16(dma_len);
836
txh->PreloadSize = 0;
838
mch = ltoh16(txh->MacTxControlHigh);
840
/* update RTS dur fields */
841
if (use_rts || use_cts) {
843
rts = (struct dot11_rts_frame *)&txh->rts_frame;
844
if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
845
TXC_PREAMBLE_RTS_MAIN_SHORT)
846
rts_preamble_type = WLC_SHORT_PREAMBLE;
848
if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
849
TXC_PREAMBLE_RTS_FB_SHORT)
850
rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
853
wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
854
rspec, rts_preamble_type,
855
preamble_type, ampdu_len,
857
rts->durid = htol16(durid);
858
durid = wlc_compute_rtscts_dur(wlc, use_cts,
861
rts_fbr_preamble_type,
864
txh->RTSDurFallback = htol16(durid);
865
/* set TxFesTimeNormal */
866
txh->TxFesTimeNormal = rts->durid;
867
/* set fallback rate version of TxFesTimeNormal */
868
txh->TxFesTimeFallback = txh->RTSDurFallback;
871
/* set flag and plcp for fallback rate */
873
WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
874
WLCNTINCR(ampdu->cnt->txfbr_ampdu);
875
mch |= TXC_AMPDU_FBR;
876
txh->MacTxControlHigh = htol16(mch);
877
WLC_SET_MIMO_PLCP_AMPDU(plcp);
878
WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
881
WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
882
wlc->pub->unit, count, ampdu_len));
884
/* inform rate_sel if it this is a rate probe pkt */
885
frameid = ltoh16(txh->TxFrameID);
886
if (frameid & TXFID_RATE_PROBE_MASK) {
887
WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__));
890
if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
891
bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
892
BCM_RPC_TP_HOST_AGG_AMPDU, true);
894
for (i = 0; i < count; i++)
895
wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
896
ampdu->txpkt_weight);
898
if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
899
bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
900
BCM_RPC_TP_HOST_AGG_AMPDU, false);
909
wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, void *p,
912
scb_ampdu_t *scb_ampdu;
913
wlc_info_t *wlc = ampdu->wlc;
914
scb_ampdu_tid_ini_t *ini;
916
struct ieee80211_tx_info *tx_info;
918
tx_info = IEEE80211_SKB_CB(p);
919
ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
921
ASSERT(scb->magic == SCB_MAGIC);
922
ASSERT(txs->status & TX_STATUS_AMPDU);
923
scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
925
ini = SCB_AMPDU_INI(scb_ampdu, PKTPRIO(p));
926
ASSERT(ini->scb == scb);
928
/* BMAC_NOTE: For the split driver, second level txstatus comes later
929
* So if the ACK was received then wait for the second level else just
932
if (txs->status & TX_STATUS_ACK_RCV) {
936
/* wait till the next 8 bytes of txstatus is available */
939
&wlc->regs->frmtxstatus)) & TXS_V) == 0) {
942
if (status_delay > 10) {
943
ASSERT(status_delay <= 10);
948
ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
949
ASSERT(s1 & TX_STATUS_AMPDU);
950
s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
953
/* Store the relevant information in ampdu structure */
954
WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
959
bcopy(txs, &du->txs, sizeof(tx_status_t));
960
ampdu->waiting_status = true;
965
wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
966
wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
970
void wlc_ampdu_txstatus_complete(ampdu_info_t *ampdu, u32 s1, u32 s2)
972
WL_AMPDU_TX(("wl%d: wlc_ampdu_txstatus_complete: High Recvd 0x%x 0x%x p:%p\n", ampdu->wlc->pub->unit, s1, s2, ampdu->p));
974
ASSERT(ampdu->waiting_status);
976
/* The packet may have been freed if the SCB went away, if so, then still free the
980
struct ieee80211_tx_info *tx_info;
983
tx_info = IEEE80211_SKB_CB(ampdu->p);
984
scb = (struct scb *)tx_info->control.sta->drv_priv;
986
wlc_ampdu_dotxstatus_complete(ampdu, scb, ampdu->p, &du->txs,
991
ampdu->waiting_status = false;
993
#endif /* WLC_HIGH_ONLY */
994
void rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
995
tx_status_t *txs, u8 mcs);
998
rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
999
tx_status_t *txs, u8 mcs)
1001
struct ieee80211_tx_rate *txrate = tx_info->status.rates;
1004
/* clear the rest of the rates */
1005
for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
1007
txrate[i].count = 0;
1011
extern void wlc_txq_enq(wlc_info_t *wlc, struct scb *scb, void *sdu,
1014
#define SHORTNAME "AMPDU status"
1016
static void BCMFASTPATH
1017
wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb, void *p,
1018
tx_status_t *txs, u32 s1, u32 s2)
1020
scb_ampdu_t *scb_ampdu;
1021
wlc_info_t *wlc = ampdu->wlc;
1022
scb_ampdu_tid_ini_t *ini;
1023
u8 bitmap[8], queue, tid;
1026
struct dot11_header *h;
1027
u16 seq, start_seq = 0, bindex, index, mcl;
1029
bool ba_recd = false, ack_recd = false;
1030
u8 suc_mpdu = 0, tot_mpdu = 0;
1032
bool update_rate = true, retry = true, tx_error = false;
1035
u8 retry_limit, rr_retry_limit;
1036
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
1039
u8 hole[AMPDU_MAX_MPDU];
1040
bzero(hole, sizeof(hole));
1043
ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1044
ASSERT(txs->status & TX_STATUS_AMPDU);
1046
scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1049
tid = (u8) PKTPRIO(p);
1051
ini = SCB_AMPDU_INI(scb_ampdu, tid);
1052
retry_limit = ampdu->retry_limit_tid[tid];
1053
rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
1055
ASSERT(ini->scb == scb);
1057
bzero(bitmap, sizeof(bitmap));
1058
queue = txs->frameid & TXFID_QUEUE_MASK;
1059
ASSERT(queue < AC_COUNT);
1061
supr_status = txs->status & TX_STATUS_SUPR_MASK;
1063
if (txs->status & TX_STATUS_ACK_RCV) {
1064
if (TX_STATUS_SUPR_UF == supr_status) {
1065
update_rate = false;
1068
ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1069
start_seq = txs->sequence >> SEQNUM_SHIFT;
1070
bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1071
TX_STATUS_BA_BMAP03_SHIFT;
1073
ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1074
ASSERT(s1 & TX_STATUS_AMPDU);
1077
(s1 & TX_STATUS_BA_BMAP47_MASK) <<
1078
TX_STATUS_BA_BMAP47_SHIFT;
1079
bitmap[1] = (s1 >> 8) & 0xff;
1080
bitmap[2] = (s1 >> 16) & 0xff;
1081
bitmap[3] = (s1 >> 24) & 0xff;
1083
bitmap[4] = s2 & 0xff;
1084
bitmap[5] = (s2 >> 8) & 0xff;
1085
bitmap[6] = (s2 >> 16) & 0xff;
1086
bitmap[7] = (s2 >> 24) & 0xff;
1090
WLCNTINCR(ampdu->cnt->noba);
1092
update_rate = false;
1093
if (supr_status == TX_STATUS_SUPR_BADCH) {
1094
WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
1096
if (supr_status == TX_STATUS_SUPR_FRAG)
1097
WL_NONE(("%s: AMPDU frag err\n",
1100
WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
1102
/* no need to retry for badch; will fail again */
1103
if (supr_status == TX_STATUS_SUPR_BADCH ||
1104
supr_status == TX_STATUS_SUPR_EXPTIME) {
1106
WLCNTINCR(wlc->pub->_cnt->txchanrej);
1107
} else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1109
WLCNTINCR(wlc->pub->_cnt->txexptime);
1111
/* TX underflow : try tuning pre-loading or ampdu size */
1112
} else if (supr_status == TX_STATUS_SUPR_FRAG) {
1113
/* if there were underflows, but pre-loading is not active,
1114
notify rate adaptation.
1116
if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1119
#ifdef WLC_HIGH_ONLY
1120
/* With BMAC, TX Underflows should not happen */
1121
WL_ERROR(("wl%d: BMAC TX Underflow?",
1126
} else if (txs->phyerr) {
1127
update_rate = false;
1128
WLCNTINCR(wlc->pub->_cnt->txphyerr);
1129
WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc->pub->unit, txs->phyerr));
1132
if (WL_ERROR_ON()) {
1133
prpkt("txpkt (AMPDU)", wlc->osh, p);
1134
wlc_print_txdesc((d11txh_t *) PKTDATA(p));
1135
wlc_print_txstatus(txs);
1141
/* loop through all pkts and retry if not acked */
1143
tx_info = IEEE80211_SKB_CB(p);
1144
ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1145
txh = (d11txh_t *) PKTDATA(p);
1146
mcl = ltoh16(txh->MacTxControlLow);
1147
plcp = (u8 *) (txh + 1);
1148
h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1149
seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1151
if (tot_mpdu == 0) {
1152
mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1153
mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1156
index = TX_SEQ_TO_INDEX(seq);
1159
bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1161
WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1162
"bindex is %d set %d, index %d\n",
1163
__func__, tid, seq, start_seq, bindex,
1164
isset(bitmap, bindex), index));
1166
/* if acked then clear bit and free packet */
1167
if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1168
&& isset(bitmap, bindex)) {
1169
ini->tx_in_transit--;
1170
ini->txretry[index] = 0;
1172
/* ampdu_ack_len: number of acked aggregated frames */
1173
/* ampdu_ack_map: block ack bit map for the aggregation */
1174
/* ampdu_len: number of aggregated frames */
1175
rate_status(wlc, tx_info, txs, mcs);
1176
tx_info->flags |= IEEE80211_TX_STAT_ACK;
1177
tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1179
/* XXX TODO: Make these accurate. */
1180
tx_info->status.ampdu_ack_len =
1182
status & TX_STATUS_FRM_RTX_MASK) >>
1183
TX_STATUS_FRM_RTX_SHIFT;
1184
tx_info->status.ampdu_len =
1186
status & TX_STATUS_FRM_RTX_MASK) >>
1187
TX_STATUS_FRM_RTX_SHIFT;
1189
PKTPULL(p, D11_PHY_HDR_LEN);
1190
PKTPULL(p, D11_TXH_LEN);
1192
ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1198
/* either retransmit or send bar if ack not recd */
1200
struct ieee80211_tx_rate *txrate =
1201
tx_info->status.rates;
1202
if (retry && (txrate[0].count < (int)retry_limit)) {
1203
ini->txretry[index]++;
1204
ini->tx_in_transit--;
1205
/* Use high prededence for retransmit to give some punch */
1206
/* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1207
wlc_txq_enq(wlc, scb, p,
1208
WLC_PRIO_TO_HI_PREC(tid));
1211
ini->tx_in_transit--;
1212
ieee80211_tx_info_clear_status(tx_info);
1214
IEEE80211_TX_STAT_AMPDU_NO_BACK;
1215
PKTPULL(p, D11_PHY_HDR_LEN);
1216
PKTPULL(p, D11_TXH_LEN);
1217
WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME, seq, ini->tx_in_transit));
1218
ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1224
/* break out if last packet of ampdu */
1225
if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1229
p = GETNEXTTXP(wlc, queue);
1235
wlc_send_q(wlc, wlc->active_queue);
1237
/* update rate state */
1238
if (WLANTSEL_ENAB(wlc))
1239
antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1241
wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1245
ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
1248
scb_ampdu_tid_ini_t *ini;
1249
ini = SCB_AMPDU_INI(scb_ampdu, tid);
1253
WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1254
ampdu->wlc->pub->unit, tid));
1256
if (ini->tx_in_transit && !force)
1259
scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1260
ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1262
/* free all buffered tx packets */
1263
pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
1266
/* initialize the initiator code for tid */
1267
static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
1268
scb_ampdu_t *scb_ampdu,
1269
u8 tid, bool override)
1271
scb_ampdu_tid_ini_t *ini;
1274
ASSERT(scb_ampdu->scb);
1275
ASSERT(SCB_AMPDU(scb_ampdu->scb));
1276
ASSERT(tid < AMPDU_MAX_SCB_TID);
1278
/* check for per-tid control of ampdu */
1279
if (!ampdu->ini_enable[tid]) {
1280
WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
1284
ini = SCB_AMPDU_INI(scb_ampdu, tid);
1286
ini->scb = scb_ampdu->scb;
1287
ini->magic = INI_MAGIC;
1288
WLCNTINCR(ampdu->cnt->txaddbareq);
1293
int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
1295
wlc_info_t *wlc = ampdu->wlc;
1297
wlc->pub->_ampdu = false;
1300
if (!N_ENAB(wlc->pub)) {
1301
WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1303
return BCME_UNSUPPORTED;
1305
if (!wlc_ampdu_cap(ampdu)) {
1306
WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1308
return BCME_UNSUPPORTED;
1310
wlc->pub->_ampdu = on;
1316
bool wlc_ampdu_cap(ampdu_info_t *ampdu)
1318
if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1324
static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur)
1328
for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1329
/* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1331
rate = MCS_RATE(mcs, false, false);
1332
ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1333
/* 40 MHz, No SGI */
1334
rate = MCS_RATE(mcs, true, false);
1335
ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1337
rate = MCS_RATE(mcs, false, true);
1338
ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1340
rate = MCS_RATE(mcs, true, true);
1341
ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1346
wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
1347
ratespec_t rspec, int phylen)
1349
scb_ampdu_t *scb_ampdu;
1350
int bytes, cnt, tmp;
1354
ASSERT(SCB_AMPDU(scb));
1356
scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1359
if (scb_ampdu->mpdu_density == 0)
1362
/* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1363
density x is in 2^(x-4) usec
1364
==> # of bytes needed for req density = rate/2^(17-x)
1365
==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1368
tx_density = scb_ampdu->mpdu_density;
1370
ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1371
tmp = 1 << (17 - tx_density);
1372
bytes = CEIL(RSPEC2RATE(rspec), tmp);
1374
if (bytes > phylen) {
1375
cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1382
void wlc_ampdu_macaddr_upd(wlc_info_t *wlc)
1384
char template[T_RAM_ACCESS_SZ * 2];
1386
/* driver needs to write the ta in the template; ta is at offset 16 */
1387
bzero(template, sizeof(template));
1388
bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETHER_ADDR_LEN);
1389
wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1393
bool wlc_aggregatable(wlc_info_t *wlc, u8 tid)
1395
return wlc->ampdu->ini_enable[tid];
1398
void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
1400
wlc_info_t *wlc = ampdu->wlc;
1402
/* Extend ucode internal watchdog timer to match larger received frames */
1403
if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1404
AMPDU_RX_FACTOR_64K) {
1405
wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1406
wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1408
wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1409
wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);