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>
17 #include <net/mac80211.h>
28 #include "wlc_types.h"
34 #include "phy/wlc_phy_hal.h"
35 #include "wlc_antsel.h"
36 #include "wl_export.h"
38 #include "wlc_bsscfg.h"
39 #include "wlc_channel.h"
40 #include "wlc_mac80211.h"
41 #include "wlc_ampdu.h"
44 * Disable AMPDU statistics counters for now
47 #define WLCNTADD(a, b)
49 #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
50 #define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
51 #define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
52 #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
53 #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
54 #define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
55 #define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
56 #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
57 #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
58 #define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
59 #define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
60 #define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
61 #define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
63 #define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
64 #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
67 #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
68 #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
69 #define FFPLD_PLD_INCR 1000 /* increments in bytes */
70 #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
71 * accumulate between resets.
74 #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
76 /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
77 #define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\
78 AMPDU_DELIMITER_LEN + 3\
79 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
88 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
91 /* structure to hold tx fifo information and pre-loading state
92 * counters specific to tx underflows of ampdus
93 * some counters might be redundant with the ones in wlc or ampdu structures.
94 * This allows to maintain a specific state independantly of
95 * how often and/or when the wlc counters are updated.
97 typedef struct wlc_fifo_info {
98 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
99 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
100 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
101 u32 accum_txfunfl; /* num of underflows since we modified pld params */
102 u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
103 u32 prev_txampdu; /* previous reading of tx ampdu */
104 u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
107 /* AMPDU module specific state */
109 struct wlc_info *wlc; /* pointer to main wlc structure */
110 int scb_handle; /* scb cubby handle to retrieve data from scb */
111 u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
112 u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
113 u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
114 u8 retry_limit; /* mpdu transmit retry limit */
115 u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
116 u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
117 /* per-tid mpdu transmit retry limit at regular rate */
118 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
119 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
120 s8 max_pdu; /* max pdus allowed in ampdu */
121 u8 dur; /* max duration of an ampdu (in msec) */
122 u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
123 u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
124 u32 ffpld_rsvd; /* number of bytes to reserve for preload */
125 u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
126 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
127 bool mfbr; /* enable multiple fallback rate */
128 u32 tx_max_funl; /* underflows should be kept such that
129 * (tx_max_funfl*underflows) < tx frames
131 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
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(struct ampdu_info *ampdu);
142 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
143 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
145 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
146 scb_ampdu_t *scb_ampdu,
147 u8 tid, bool override);
148 static void ampdu_cleanup_tid_ini(struct ampdu_info *ampdu,
149 scb_ampdu_t *scb_ampdu,
151 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
152 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
153 static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
155 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
157 static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
159 struct sk_buff *p, tx_status_t *txs,
160 u32 frmtxstatus, u32 frmtxstatus2);
161 static bool wlc_ampdu_cap(struct ampdu_info *ampdu);
162 static int wlc_ampdu_set(struct ampdu_info *ampdu, bool on);
164 struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
166 struct ampdu_info *ampdu;
169 /* some code depends on packed structures */
170 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
171 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
172 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
173 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
174 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
176 ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
178 WL_ERROR("wl%d: wlc_ampdu_attach: out of mem\n",
184 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
185 ampdu->ini_enable[i] = true;
186 /* Disable ampdu for VO by default */
187 ampdu->ini_enable[PRIO_8021D_VO] = false;
188 ampdu->ini_enable[PRIO_8021D_NC] = false;
190 /* Disable ampdu for BK by default since not enough fifo space */
191 ampdu->ini_enable[PRIO_8021D_NONE] = false;
192 ampdu->ini_enable[PRIO_8021D_BK] = false;
194 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
195 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
196 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
197 ampdu->max_pdu = AUTO;
198 ampdu->dur = AMPDU_MAX_DUR;
199 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
201 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
202 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
203 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
204 ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K;
206 ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K;
207 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
208 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
210 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
211 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
212 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
215 ampdu_update_max_txlen(ampdu, ampdu->dur);
217 /* try to set ampdu to the default value */
218 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
220 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
221 wlc_ffpld_init(ampdu);
226 void wlc_ampdu_detach(struct ampdu_info *ampdu)
233 /* free all ini's which were to be freed on callbacks which were never called */
234 for (i = 0; i < AMPDU_INI_FREE; i++) {
235 kfree(ampdu->ini_free[i]);
238 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
242 void scb_ampdu_cleanup(struct ampdu_info *ampdu, struct scb *scb)
244 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
247 WL_AMPDU_UPDN("scb_ampdu_cleanup: enter\n");
250 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
251 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
255 /* reset the ampdu state machine so that it can gracefully handle packets that were
256 * freed from the dma and tx queues during reinit
258 void wlc_ampdu_reset(struct ampdu_info *ampdu)
260 WL_NONE("%s: Entering\n", __func__);
263 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb)
265 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
268 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
270 /* go back to legacy size if some preloading is occuring */
271 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
272 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
273 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
276 /* apply user override */
277 if (ampdu->max_pdu != AUTO)
278 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
280 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
282 if (scb_ampdu->max_rxlen)
284 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
286 scb_ampdu->release = min(scb_ampdu->release,
287 ampdu->fifo_tb[TX_AC_BE_FIFO].
288 mcs2ampdu_table[FFPLD_MAX_MCS]);
290 ASSERT(scb_ampdu->release);
293 void scb_ampdu_update_config_all(struct ampdu_info *ampdu)
295 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
298 static void wlc_ffpld_init(struct ampdu_info *ampdu)
301 wlc_fifo_info_t *fifo;
303 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
304 fifo = (ampdu->fifo_tb + j);
305 fifo->ampdu_pld_size = 0;
306 for (i = 0; i <= FFPLD_MAX_MCS; i++)
307 fifo->mcs2ampdu_table[i] = 255;
308 fifo->dmaxferrate = 0;
309 fifo->accum_txampdu = 0;
310 fifo->prev_txfunfl = 0;
311 fifo->accum_txfunfl = 0;
316 /* evaluate the dma transfer rate using the tx underflows as feedback.
317 * If necessary, increase tx fifo preloading. If not enough,
318 * decrease maximum ampdu size for each mcs till underflows stop
319 * Return 1 if pre-loading not active, -1 if not an underflow event,
320 * 0 if pre-loading module took care of the event.
322 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
324 struct ampdu_info *ampdu = wlc->ampdu;
325 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
328 u32 current_ampdu_cnt = 0;
331 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
335 /* return if we got here for a different reason than underflows */
338 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
339 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
340 if (new_txunfl == 0) {
341 WL_FFPLD("check_txunfl : TX status FRAG set but no tx underflows\n");
344 fifo->prev_txfunfl = cur_txunfl;
346 if (!ampdu->tx_max_funl)
349 /* check if fifo is big enough */
350 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
351 WL_FFPLD("check_txunfl : get xmtfifo_sz failed\n");
355 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
358 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
359 fifo->accum_txfunfl += new_txunfl;
361 /* we need to wait for at least 10 underflows */
362 if (fifo->accum_txfunfl < 10)
365 WL_FFPLD("ampdu_count %d tx_underflows %d\n",
366 current_ampdu_cnt, fifo->accum_txfunfl);
369 compute the current ratio of tx unfl per ampdu.
370 When the current ampdu count becomes too
371 big while the ratio remains small, we reset
372 the current count in order to not
373 introduce too big of a latency in detecting a
374 large amount of tx underflows later.
377 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
379 if (txunfl_ratio > ampdu->tx_max_funl) {
380 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
381 fifo->accum_txfunfl = 0;
386 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
388 /* In case max value max_pdu is already lower than
389 the fifo depth, there is nothing more we can do.
392 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
393 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
394 fifo->accum_txfunfl = 0;
398 if (fifo->ampdu_pld_size < max_pld_size) {
400 /* increment by TX_FIFO_PLD_INC bytes */
401 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
402 if (fifo->ampdu_pld_size > max_pld_size)
403 fifo->ampdu_pld_size = max_pld_size;
405 /* update scb release size */
406 scb_ampdu_update_config_all(ampdu);
409 compute a new dma xfer rate for max_mpdu @ max mcs.
410 This is the minimum dma rate that
411 can acheive no unferflow condition for the current mpdu size.
413 /* note : we divide/multiply by 100 to avoid integer overflows */
416 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
417 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
419 WL_FFPLD("DMA estimated transfer rate %d; pre-load size %d\n",
420 fifo->dmaxferrate, fifo->ampdu_pld_size);
423 /* decrease ampdu size */
424 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
425 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
426 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
427 AMPDU_NUM_MPDU_LEGACY - 1;
429 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
431 /* recompute the table */
432 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
434 /* update scb release size */
435 scb_ampdu_update_config_all(ampdu);
438 fifo->accum_txfunfl = 0;
442 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
445 u32 phy_rate, dma_rate, tmp;
447 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
449 /* recompute the dma rate */
450 /* note : we divide/multiply by 100 to avoid integer overflows */
452 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
453 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
456 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
457 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
458 fifo->dmaxferrate = dma_rate;
460 /* fill up the mcs2ampdu table; do not recalc the last mcs */
461 dma_rate = dma_rate >> 7;
462 for (i = 0; i < FFPLD_MAX_MCS; i++) {
463 /* shifting to keep it within integer range */
464 phy_rate = MCS_RATE(i, true, false) >> 7;
465 if (phy_rate > dma_rate) {
466 tmp = ((fifo->ampdu_pld_size * phy_rate) /
467 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
468 tmp = min_t(u32, tmp, 255);
469 fifo->mcs2ampdu_table[i] = (u8) tmp;
474 static void BCMFASTPATH
475 wlc_ampdu_agg(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
478 scb_ampdu_t *scb_ampdu;
479 scb_ampdu_tid_ini_t *ini;
480 u8 tid = (u8) (p->priority);
482 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
484 /* initialize initiator on first packet; sends addba req */
485 ini = SCB_AMPDU_INI(scb_ampdu, tid);
486 if (ini->magic != INI_MAGIC) {
487 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
493 wlc_sendampdu(struct ampdu_info *ampdu, struct wlc_txq_info *qi,
494 struct sk_buff **pdu, int prec)
496 struct wlc_info *wlc;
497 struct osl_info *osh;
498 struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
501 u8 preamble_type = WLC_GF_PREAMBLE;
502 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
503 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
504 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
506 bool rr = true, fbr = false;
507 uint i, count = 0, fifo, seg_cnt = 0;
508 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
509 u32 ampdu_len, maxlen = 0;
510 d11txh_t *txh = NULL;
512 struct ieee80211_hdr *h;
514 scb_ampdu_t *scb_ampdu;
515 scb_ampdu_tid_ini_t *ini;
517 bool use_rts = false, use_cts = false;
518 ratespec_t rspec = 0, rspec_fallback = 0;
519 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
520 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
521 struct ieee80211_rts *rts;
525 struct ieee80211_tx_info *tx_info;
534 tid = (u8) (p->priority);
535 ASSERT(tid < AMPDU_MAX_SCB_TID);
537 f = ampdu->fifo_tb + prio2fifo[tid];
539 scb = wlc->pub->global_scb;
540 ASSERT(scb->magic == SCB_MAGIC);
542 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
544 ini = &scb_ampdu->ini[tid];
546 /* Let pressure continue to build ... */
547 qlen = pktq_plen(&qi->q, prec);
548 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
552 wlc_ampdu_agg(ampdu, scb, p, tid);
554 if (wlc->block_datafifo) {
555 WL_ERROR("%s: Fifo blocked\n", __func__);
558 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
562 struct ieee80211_tx_rate *txrate;
564 tx_info = IEEE80211_SKB_CB(p);
565 txrate = tx_info->status.rates;
567 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
568 err = wlc_prep_pdu(wlc, p, &fifo);
570 WL_ERROR("%s: AMPDU flag is off!\n", __func__);
577 if (err == BCME_BUSY) {
578 WL_ERROR("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n",
579 wlc->pub->unit, seq);
580 WLCNTINCR(ampdu->cnt->sduretry);
585 /* error in the packet; reject it */
586 WL_AMPDU_ERR("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n",
587 wlc->pub->unit, seq);
588 WLCNTINCR(ampdu->cnt->sdurejected);
594 /* pkt is good to be aggregated */
595 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
596 txh = (d11txh_t *) p->data;
597 plcp = (u8 *) (txh + 1);
598 h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
599 seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
600 index = TX_SEQ_TO_INDEX(seq);
602 /* check mcl fields and test whether it can be agg'd */
603 mcl = le16_to_cpu(txh->MacTxControlLow);
604 mcl &= ~TXC_AMPDU_MASK;
605 fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3);
607 txh->PreloadSize = 0; /* always default to 0 */
609 /* Handle retry limits */
610 if (txrate[0].count <= rr_retry_limit) {
621 /* extract the length info */
622 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
623 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
625 /* retrieve null delimiter count */
626 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
629 WL_AMPDU_TX("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
630 wlc->pub->unit, count, len);
633 * aggregateable mpdu. For ucode/hw agg,
634 * test whether need to break or change the epoch
638 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
639 /* refill the bits since might be a retx mpdu */
640 mcl |= TXC_STARTMSDU;
641 rts = (struct ieee80211_rts *)&txh->rts_frame;
642 fc = le16_to_cpu(rts->frame_control);
643 if ((fc & FC_KIND_MASK) == FC_RTS) {
647 if ((fc & FC_KIND_MASK) == FC_CTS) {
652 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
653 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
656 len = roundup(len, 4);
657 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
659 dma_len += (u16) pkttotlen(p);
661 WL_AMPDU_TX("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n",
662 wlc->pub->unit, ampdu_len, seg_cnt, ndelim);
664 txh->MacTxControlLow = cpu_to_le16(mcl);
666 /* this packet is added */
669 /* patch the first MPDU */
671 u8 plcp0, plcp3, is40, sgi;
672 struct ieee80211_sta *sta;
674 sta = tx_info->control.sta;
680 plcp0 = txh->FragPLCPFallback[0];
681 plcp3 = txh->FragPLCPFallback[3];
684 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
685 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
686 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
687 ASSERT(mcs < MCS_TABLE_SIZE);
689 min(scb_ampdu->max_rxlen,
690 ampdu->max_txlen[mcs][is40][sgi]);
692 WL_NONE("sendampdu: sgi %d, is40 %d, mcs %d\n",
695 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
699 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
700 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
702 /* rebuild the rspec and rspec_fallback */
703 rspec = RSPEC_MIMORATE;
704 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
705 if (plcp[0] & MIMO_PLCP_40MHZ)
706 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
708 if (fbr_iscck) /* CCK */
710 CCK_RSPEC(CCK_PHY2MAC_RATE
711 (txh->FragPLCPFallback[0]));
713 rspec_fallback = RSPEC_MIMORATE;
715 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
716 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
718 (PHY_TXC1_BW_40MHZ <<
722 if (use_rts || use_cts) {
724 wlc_rspec_to_rts_rspec(wlc, rspec, false,
727 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
728 false, mimo_ctlchbw);
732 /* if (first mpdu for host agg) */
733 /* test whether to add more */
734 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
735 (count == f->mcs2ampdu_table[mcs])) {
736 WL_AMPDU_ERR("wl%d: PR 37644: stopping ampdu at %d for mcs %d\n",
737 wlc->pub->unit, count, mcs);
741 if (count == scb_ampdu->max_pdu) {
742 WL_NONE("Stop taking from q, reached %d deep\n",
747 /* check to see if the next pkt is a candidate for aggregation */
748 p = pktq_ppeek(&qi->q, prec);
749 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
752 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
753 ((u8) (p->priority) == tid)) {
756 pkttotlen(p) + AMPDU_MAX_MPDU_OVERHEAD;
757 plen = max(scb_ampdu->min_len, plen);
759 if ((plen + ampdu_len) > maxlen) {
761 WL_ERROR("%s: Bogus plen #1\n",
767 /* check if there are enough descriptors available */
768 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
769 WL_ERROR("%s: No fifo space !!!!!!\n",
774 p = pktq_pdeq(&qi->q, prec);
782 ini->tx_in_transit += count;
785 WLCNTADD(ampdu->cnt->txmpdu, count);
787 /* patch up the last txh */
788 txh = (d11txh_t *) pkt[count - 1]->data;
789 mcl = le16_to_cpu(txh->MacTxControlLow);
790 mcl &= ~TXC_AMPDU_MASK;
791 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
792 txh->MacTxControlLow = cpu_to_le16(mcl);
794 /* remove the null delimiter after last mpdu */
795 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
796 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
797 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
799 /* remove the pad len from last mpdu */
800 fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
801 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
802 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
803 ampdu_len -= roundup(len, 4) - len;
805 /* patch up the first txh & plcp */
806 txh = (d11txh_t *) pkt[0]->data;
807 plcp = (u8 *) (txh + 1);
809 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
810 /* mark plcp to indicate ampdu */
811 WLC_SET_MIMO_PLCP_AMPDU(plcp);
813 /* reset the mixed mode header durations */
816 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
817 txh->MModeLen = cpu_to_le16(mmodelen);
818 preamble_type = WLC_MM_PREAMBLE;
820 if (txh->MModeFbrLen) {
822 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
823 txh->MModeFbrLen = cpu_to_le16(mmfbrlen);
824 fbr_preamble_type = WLC_MM_PREAMBLE;
827 /* set the preload length */
828 if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
829 dma_len = min(dma_len, f->ampdu_pld_size);
830 txh->PreloadSize = cpu_to_le16(dma_len);
832 txh->PreloadSize = 0;
834 mch = le16_to_cpu(txh->MacTxControlHigh);
836 /* update RTS dur fields */
837 if (use_rts || use_cts) {
839 rts = (struct ieee80211_rts *)&txh->rts_frame;
840 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
841 TXC_PREAMBLE_RTS_MAIN_SHORT)
842 rts_preamble_type = WLC_SHORT_PREAMBLE;
844 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
845 TXC_PREAMBLE_RTS_FB_SHORT)
846 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
849 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
850 rspec, rts_preamble_type,
851 preamble_type, ampdu_len,
853 rts->duration = cpu_to_le16(durid);
854 durid = wlc_compute_rtscts_dur(wlc, use_cts,
857 rts_fbr_preamble_type,
860 txh->RTSDurFallback = cpu_to_le16(durid);
861 /* set TxFesTimeNormal */
862 txh->TxFesTimeNormal = rts->duration;
863 /* set fallback rate version of TxFesTimeNormal */
864 txh->TxFesTimeFallback = txh->RTSDurFallback;
867 /* set flag and plcp for fallback rate */
869 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
870 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
871 mch |= TXC_AMPDU_FBR;
872 txh->MacTxControlHigh = cpu_to_le16(mch);
873 WLC_SET_MIMO_PLCP_AMPDU(plcp);
874 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
877 WL_AMPDU_TX("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
878 wlc->pub->unit, count, ampdu_len);
880 /* inform rate_sel if it this is a rate probe pkt */
881 frameid = le16_to_cpu(txh->TxFrameID);
882 if (frameid & TXFID_RATE_PROBE_MASK) {
883 WL_ERROR("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n",
886 for (i = 0; i < count; i++)
887 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
888 ampdu->txpkt_weight);
896 wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
897 struct sk_buff *p, tx_status_t *txs)
899 scb_ampdu_t *scb_ampdu;
900 struct wlc_info *wlc = ampdu->wlc;
901 scb_ampdu_tid_ini_t *ini;
903 struct ieee80211_tx_info *tx_info;
905 tx_info = IEEE80211_SKB_CB(p);
906 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
908 ASSERT(scb->magic == SCB_MAGIC);
909 ASSERT(txs->status & TX_STATUS_AMPDU);
910 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
912 ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
913 ASSERT(ini->scb == scb);
915 /* BMAC_NOTE: For the split driver, second level txstatus comes later
916 * So if the ACK was received then wait for the second level else just
919 if (txs->status & TX_STATUS_ACK_RCV) {
922 /* wait till the next 8 bytes of txstatus is available */
925 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
928 if (status_delay > 10) {
929 ASSERT(status_delay <= 10);
934 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
935 ASSERT(s1 & TX_STATUS_AMPDU);
936 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
939 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
940 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
944 rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
945 tx_status_t *txs, u8 mcs)
947 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
950 /* clear the rest of the rates */
951 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
957 #define SHORTNAME "AMPDU status"
959 static void BCMFASTPATH
960 wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
961 struct sk_buff *p, tx_status_t *txs,
964 scb_ampdu_t *scb_ampdu;
965 struct wlc_info *wlc = ampdu->wlc;
966 scb_ampdu_tid_ini_t *ini;
967 u8 bitmap[8], queue, tid;
970 struct ieee80211_hdr *h;
971 u16 seq, start_seq = 0, bindex, index, mcl;
973 bool ba_recd = false, ack_recd = false;
974 u8 suc_mpdu = 0, tot_mpdu = 0;
976 bool update_rate = true, retry = true, tx_error = false;
979 u8 retry_limit, rr_retry_limit;
980 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
983 u8 hole[AMPDU_MAX_MPDU];
984 memset(hole, 0, sizeof(hole));
987 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
988 ASSERT(txs->status & TX_STATUS_AMPDU);
990 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
993 tid = (u8) (p->priority);
995 ini = SCB_AMPDU_INI(scb_ampdu, tid);
996 retry_limit = ampdu->retry_limit_tid[tid];
997 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
999 ASSERT(ini->scb == scb);
1001 memset(bitmap, 0, sizeof(bitmap));
1002 queue = txs->frameid & TXFID_QUEUE_MASK;
1003 ASSERT(queue < AC_COUNT);
1005 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1007 if (txs->status & TX_STATUS_ACK_RCV) {
1008 if (TX_STATUS_SUPR_UF == supr_status) {
1009 update_rate = false;
1012 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1013 start_seq = txs->sequence >> SEQNUM_SHIFT;
1014 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1015 TX_STATUS_BA_BMAP03_SHIFT;
1017 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1018 ASSERT(s1 & TX_STATUS_AMPDU);
1021 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1022 TX_STATUS_BA_BMAP47_SHIFT;
1023 bitmap[1] = (s1 >> 8) & 0xff;
1024 bitmap[2] = (s1 >> 16) & 0xff;
1025 bitmap[3] = (s1 >> 24) & 0xff;
1027 bitmap[4] = s2 & 0xff;
1028 bitmap[5] = (s2 >> 8) & 0xff;
1029 bitmap[6] = (s2 >> 16) & 0xff;
1030 bitmap[7] = (s2 >> 24) & 0xff;
1034 WLCNTINCR(ampdu->cnt->noba);
1036 update_rate = false;
1037 if (supr_status == TX_STATUS_SUPR_BADCH) {
1038 WL_ERROR("%s: Pkt tx suppressed, illegal channel possibly %d\n",
1040 CHSPEC_CHANNEL(wlc->default_bss->chanspec));
1042 if (supr_status == TX_STATUS_SUPR_FRAG)
1043 WL_NONE("%s: AMPDU frag err\n",
1046 WL_ERROR("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n",
1047 __func__, supr_status);
1049 /* no need to retry for badch; will fail again */
1050 if (supr_status == TX_STATUS_SUPR_BADCH ||
1051 supr_status == TX_STATUS_SUPR_EXPTIME) {
1053 wlc->pub->_cnt->txchanrej++;
1054 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1056 wlc->pub->_cnt->txexptime++;
1058 /* TX underflow : try tuning pre-loading or ampdu size */
1059 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1060 /* if there were underflows, but pre-loading is not active,
1061 notify rate adaptation.
1063 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1068 } else if (txs->phyerr) {
1069 update_rate = false;
1070 wlc->pub->_cnt->txphyerr++;
1071 WL_ERROR("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n",
1072 wlc->pub->unit, txs->phyerr);
1074 if (WL_ERROR_ON()) {
1075 prpkt("txpkt (AMPDU)", wlc->osh, p);
1076 wlc_print_txdesc((d11txh_t *) p->data);
1078 wlc_print_txstatus(txs);
1082 /* loop through all pkts and retry if not acked */
1084 tx_info = IEEE80211_SKB_CB(p);
1085 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1086 txh = (d11txh_t *) p->data;
1087 mcl = le16_to_cpu(txh->MacTxControlLow);
1088 plcp = (u8 *) (txh + 1);
1089 h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
1090 seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
1092 if (tot_mpdu == 0) {
1093 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1094 mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel);
1097 index = TX_SEQ_TO_INDEX(seq);
1100 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1102 WL_AMPDU_TX("%s: tid %d seq is %d, start_seq is %d, bindex is %d set %d, index %d\n",
1103 __func__, tid, seq, start_seq, bindex,
1104 isset(bitmap, bindex), index);
1106 /* if acked then clear bit and free packet */
1107 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1108 && isset(bitmap, bindex)) {
1109 ini->tx_in_transit--;
1110 ini->txretry[index] = 0;
1112 /* ampdu_ack_len: number of acked aggregated frames */
1113 /* ampdu_ack_map: block ack bit map for the aggregation */
1114 /* ampdu_len: number of aggregated frames */
1115 rate_status(wlc, tx_info, txs, mcs);
1116 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1117 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1119 /* XXX TODO: Make these accurate. */
1120 tx_info->status.ampdu_ack_len =
1122 status & TX_STATUS_FRM_RTX_MASK) >>
1123 TX_STATUS_FRM_RTX_SHIFT;
1124 tx_info->status.ampdu_len =
1126 status & TX_STATUS_FRM_RTX_MASK) >>
1127 TX_STATUS_FRM_RTX_SHIFT;
1129 skb_pull(p, D11_PHY_HDR_LEN);
1130 skb_pull(p, D11_TXH_LEN);
1132 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1138 /* either retransmit or send bar if ack not recd */
1140 struct ieee80211_tx_rate *txrate =
1141 tx_info->status.rates;
1142 if (retry && (txrate[0].count < (int)retry_limit)) {
1143 ini->txretry[index]++;
1144 ini->tx_in_transit--;
1145 /* Use high prededence for retransmit to give some punch */
1146 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1147 wlc_txq_enq(wlc, scb, p,
1148 WLC_PRIO_TO_HI_PREC(tid));
1151 ini->tx_in_transit--;
1152 ieee80211_tx_info_clear_status(tx_info);
1154 IEEE80211_TX_STAT_AMPDU_NO_BACK;
1155 skb_pull(p, D11_PHY_HDR_LEN);
1156 skb_pull(p, D11_TXH_LEN);
1157 WL_ERROR("%s: BA Timeout, seq %d, in_transit %d\n",
1158 SHORTNAME, seq, ini->tx_in_transit);
1159 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1165 /* break out if last packet of ampdu */
1166 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1170 p = GETNEXTTXP(wlc, queue);
1176 wlc_send_q(wlc, wlc->active_queue);
1178 /* update rate state */
1179 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1181 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1185 ampdu_cleanup_tid_ini(struct ampdu_info *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
1188 scb_ampdu_tid_ini_t *ini;
1189 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1193 WL_AMPDU_CTL("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1194 ampdu->wlc->pub->unit, tid);
1196 if (ini->tx_in_transit && !force)
1199 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1200 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1202 /* free all buffered tx packets */
1203 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
1206 /* initialize the initiator code for tid */
1207 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
1208 scb_ampdu_t *scb_ampdu,
1209 u8 tid, bool override)
1211 scb_ampdu_tid_ini_t *ini;
1214 ASSERT(scb_ampdu->scb);
1215 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1216 ASSERT(tid < AMPDU_MAX_SCB_TID);
1218 /* check for per-tid control of ampdu */
1219 if (!ampdu->ini_enable[tid]) {
1220 WL_ERROR("%s: Rejecting tid %d\n", __func__, tid);
1224 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1226 ini->scb = scb_ampdu->scb;
1227 ini->magic = INI_MAGIC;
1228 WLCNTINCR(ampdu->cnt->txaddbareq);
1233 static int wlc_ampdu_set(struct ampdu_info *ampdu, bool on)
1235 struct wlc_info *wlc = ampdu->wlc;
1237 wlc->pub->_ampdu = false;
1240 if (!N_ENAB(wlc->pub)) {
1241 WL_AMPDU_ERR("wl%d: driver not nmode enabled\n",
1243 return BCME_UNSUPPORTED;
1245 if (!wlc_ampdu_cap(ampdu)) {
1246 WL_AMPDU_ERR("wl%d: device not ampdu capable\n",
1248 return BCME_UNSUPPORTED;
1250 wlc->pub->_ampdu = on;
1256 static bool wlc_ampdu_cap(struct ampdu_info *ampdu)
1258 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1264 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
1268 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1269 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1271 rate = MCS_RATE(mcs, false, false);
1272 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1273 /* 40 MHz, No SGI */
1274 rate = MCS_RATE(mcs, true, false);
1275 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1277 rate = MCS_RATE(mcs, false, true);
1278 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1280 rate = MCS_RATE(mcs, true, true);
1281 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1286 wlc_ampdu_null_delim_cnt(struct ampdu_info *ampdu, struct scb *scb,
1287 ratespec_t rspec, int phylen)
1289 scb_ampdu_t *scb_ampdu;
1290 int bytes, cnt, tmp;
1294 ASSERT(SCB_AMPDU(scb));
1296 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1299 if (scb_ampdu->mpdu_density == 0)
1302 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1303 density x is in 2^(x-4) usec
1304 ==> # of bytes needed for req density = rate/2^(17-x)
1305 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1308 tx_density = scb_ampdu->mpdu_density;
1310 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1311 tmp = 1 << (17 - tx_density);
1312 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1314 if (bytes > phylen) {
1315 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1322 void wlc_ampdu_macaddr_upd(struct wlc_info *wlc)
1324 char template[T_RAM_ACCESS_SZ * 2];
1326 /* driver needs to write the ta in the template; ta is at offset 16 */
1327 memset(template, 0, sizeof(template));
1328 memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
1329 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1333 bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
1335 return wlc->ampdu->ini_enable[tid];
1338 void wlc_ampdu_shm_upd(struct ampdu_info *ampdu)
1340 struct wlc_info *wlc = ampdu->wlc;
1342 /* Extend ucode internal watchdog timer to match larger received frames */
1343 if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) ==
1344 IEEE80211_HT_MAX_AMPDU_64K) {
1345 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1346 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1348 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1349 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);