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>
19 #include <proto/802.1d.h>
30 #include "wlc_types.h"
36 #include "phy/wlc_phy_hal.h"
37 #include "wlc_antsel.h"
38 #include "wl_export.h"
40 #include "wlc_bsscfg.h"
41 #include "wlc_channel.h"
42 #include "wlc_mac80211.h"
43 #include "wlc_ampdu.h"
46 * Disable AMPDU statistics counters for now
49 #define WLCNTADD(a, b)
51 #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
52 #define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
53 #define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
54 #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
55 #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
56 #define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
57 #define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
58 #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
59 #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
60 #define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
61 #define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
62 #define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
63 #define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
65 #define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
66 #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
69 #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
70 #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
71 #define FFPLD_PLD_INCR 1000 /* increments in bytes */
72 #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
73 * accumulate between resets.
76 #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
78 /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
79 #define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\
80 AMPDU_DELIMITER_LEN + 3\
81 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
90 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
93 /* structure to hold tx fifo information and pre-loading state
94 * counters specific to tx underflows of ampdus
95 * some counters might be redundant with the ones in wlc or ampdu structures.
96 * This allows to maintain a specific state independantly of
97 * how often and/or when the wlc counters are updated.
99 typedef struct wlc_fifo_info {
100 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
101 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
102 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
103 u32 accum_txfunfl; /* num of underflows since we modified pld params */
104 u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
105 u32 prev_txampdu; /* previous reading of tx ampdu */
106 u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
109 /* AMPDU module specific state */
111 struct wlc_info *wlc; /* pointer to main wlc structure */
112 int scb_handle; /* scb cubby handle to retrieve data from scb */
113 u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
114 u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
115 u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
116 u8 retry_limit; /* mpdu transmit retry limit */
117 u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
118 u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
119 /* per-tid mpdu transmit retry limit at regular rate */
120 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
121 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
122 s8 max_pdu; /* max pdus allowed in ampdu */
123 u8 dur; /* max duration of an ampdu (in msec) */
124 u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
125 u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
126 u32 ffpld_rsvd; /* number of bytes to reserve for preload */
127 u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
128 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
129 bool mfbr; /* enable multiple fallback rate */
130 u32 tx_max_funl; /* underflows should be kept such that
131 * (tx_max_funfl*underflows) < tx frames
133 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
137 #define AMPDU_CLEANUPFLAG_RX (0x1)
138 #define AMPDU_CLEANUPFLAG_TX (0x2)
140 #define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
141 #define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
143 static void wlc_ffpld_init(struct ampdu_info *ampdu);
144 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
145 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
147 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
148 scb_ampdu_t *scb_ampdu,
149 u8 tid, bool override);
150 static void ampdu_cleanup_tid_ini(struct ampdu_info *ampdu,
151 scb_ampdu_t *scb_ampdu,
153 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
154 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
155 static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
157 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
159 static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
161 struct sk_buff *p, tx_status_t *txs,
162 u32 frmtxstatus, u32 frmtxstatus2);
163 static bool wlc_ampdu_cap(struct ampdu_info *ampdu);
164 static int wlc_ampdu_set(struct ampdu_info *ampdu, bool on);
166 struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
168 struct ampdu_info *ampdu;
171 /* some code depends on packed structures */
172 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
173 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
174 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
175 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
176 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
178 ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
180 WL_ERROR("wl%d: wlc_ampdu_attach: out of mem\n",
186 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
187 ampdu->ini_enable[i] = true;
188 /* Disable ampdu for VO by default */
189 ampdu->ini_enable[PRIO_8021D_VO] = false;
190 ampdu->ini_enable[PRIO_8021D_NC] = false;
192 /* Disable ampdu for BK by default since not enough fifo space */
193 ampdu->ini_enable[PRIO_8021D_NONE] = false;
194 ampdu->ini_enable[PRIO_8021D_BK] = false;
196 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
197 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
198 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
199 ampdu->max_pdu = AUTO;
200 ampdu->dur = AMPDU_MAX_DUR;
201 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
203 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
204 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
205 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
206 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
208 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
209 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
210 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
212 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
213 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
214 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
217 ampdu_update_max_txlen(ampdu, ampdu->dur);
219 /* try to set ampdu to the default value */
220 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
222 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
223 wlc_ffpld_init(ampdu);
228 void wlc_ampdu_detach(struct ampdu_info *ampdu)
235 /* free all ini's which were to be freed on callbacks which were never called */
236 for (i = 0; i < AMPDU_INI_FREE; i++) {
237 if (ampdu->ini_free[i]) {
238 kfree(ampdu->ini_free[i]);
242 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
246 void scb_ampdu_cleanup(struct ampdu_info *ampdu, struct scb *scb)
248 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
251 WL_AMPDU_UPDN("scb_ampdu_cleanup: enter\n");
254 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
255 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
259 /* reset the ampdu state machine so that it can gracefully handle packets that were
260 * freed from the dma and tx queues during reinit
262 void wlc_ampdu_reset(struct ampdu_info *ampdu)
264 WL_NONE("%s: Entering\n", __func__);
267 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb)
269 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
272 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
274 /* go back to legacy size if some preloading is occuring */
275 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
276 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
277 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
280 /* apply user override */
281 if (ampdu->max_pdu != AUTO)
282 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
284 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
286 if (scb_ampdu->max_rxlen)
288 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
290 scb_ampdu->release = min(scb_ampdu->release,
291 ampdu->fifo_tb[TX_AC_BE_FIFO].
292 mcs2ampdu_table[FFPLD_MAX_MCS]);
294 ASSERT(scb_ampdu->release);
297 void scb_ampdu_update_config_all(struct ampdu_info *ampdu)
299 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
302 static void wlc_ffpld_init(struct ampdu_info *ampdu)
305 wlc_fifo_info_t *fifo;
307 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
308 fifo = (ampdu->fifo_tb + j);
309 fifo->ampdu_pld_size = 0;
310 for (i = 0; i <= FFPLD_MAX_MCS; i++)
311 fifo->mcs2ampdu_table[i] = 255;
312 fifo->dmaxferrate = 0;
313 fifo->accum_txampdu = 0;
314 fifo->prev_txfunfl = 0;
315 fifo->accum_txfunfl = 0;
320 /* evaluate the dma transfer rate using the tx underflows as feedback.
321 * If necessary, increase tx fifo preloading. If not enough,
322 * decrease maximum ampdu size for each mcs till underflows stop
323 * Return 1 if pre-loading not active, -1 if not an underflow event,
324 * 0 if pre-loading module took care of the event.
326 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
328 struct ampdu_info *ampdu = wlc->ampdu;
329 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
332 u32 current_ampdu_cnt = 0;
335 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
339 /* return if we got here for a different reason than underflows */
342 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
343 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
344 if (new_txunfl == 0) {
345 WL_FFPLD("check_txunfl : TX status FRAG set but no tx underflows\n");
348 fifo->prev_txfunfl = cur_txunfl;
350 if (!ampdu->tx_max_funl)
353 /* check if fifo is big enough */
354 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
355 WL_FFPLD("check_txunfl : get xmtfifo_sz failed\n");
359 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
362 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
363 fifo->accum_txfunfl += new_txunfl;
365 /* we need to wait for at least 10 underflows */
366 if (fifo->accum_txfunfl < 10)
369 WL_FFPLD("ampdu_count %d tx_underflows %d\n",
370 current_ampdu_cnt, fifo->accum_txfunfl);
373 compute the current ratio of tx unfl per ampdu.
374 When the current ampdu count becomes too
375 big while the ratio remains small, we reset
376 the current count in order to not
377 introduce too big of a latency in detecting a
378 large amount of tx underflows later.
381 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
383 if (txunfl_ratio > ampdu->tx_max_funl) {
384 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
385 fifo->accum_txfunfl = 0;
390 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
392 /* In case max value max_pdu is already lower than
393 the fifo depth, there is nothing more we can do.
396 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
397 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
398 fifo->accum_txfunfl = 0;
402 if (fifo->ampdu_pld_size < max_pld_size) {
404 /* increment by TX_FIFO_PLD_INC bytes */
405 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
406 if (fifo->ampdu_pld_size > max_pld_size)
407 fifo->ampdu_pld_size = max_pld_size;
409 /* update scb release size */
410 scb_ampdu_update_config_all(ampdu);
413 compute a new dma xfer rate for max_mpdu @ max mcs.
414 This is the minimum dma rate that
415 can acheive no unferflow condition for the current mpdu size.
417 /* note : we divide/multiply by 100 to avoid integer overflows */
420 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
421 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
423 WL_FFPLD("DMA estimated transfer rate %d; pre-load size %d\n",
424 fifo->dmaxferrate, fifo->ampdu_pld_size);
427 /* decrease ampdu size */
428 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
429 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
430 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
431 AMPDU_NUM_MPDU_LEGACY - 1;
433 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
435 /* recompute the table */
436 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
438 /* update scb release size */
439 scb_ampdu_update_config_all(ampdu);
442 fifo->accum_txfunfl = 0;
446 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
449 u32 phy_rate, dma_rate, tmp;
451 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
453 /* recompute the dma rate */
454 /* note : we divide/multiply by 100 to avoid integer overflows */
456 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
457 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
460 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
461 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
462 fifo->dmaxferrate = dma_rate;
464 /* fill up the mcs2ampdu table; do not recalc the last mcs */
465 dma_rate = dma_rate >> 7;
466 for (i = 0; i < FFPLD_MAX_MCS; i++) {
467 /* shifting to keep it within integer range */
468 phy_rate = MCS_RATE(i, true, false) >> 7;
469 if (phy_rate > dma_rate) {
470 tmp = ((fifo->ampdu_pld_size * phy_rate) /
471 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
472 tmp = min_t(u32, tmp, 255);
473 fifo->mcs2ampdu_table[i] = (u8) tmp;
478 static void BCMFASTPATH
479 wlc_ampdu_agg(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
482 scb_ampdu_t *scb_ampdu;
483 scb_ampdu_tid_ini_t *ini;
484 u8 tid = (u8) (p->priority);
486 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
488 /* initialize initiator on first packet; sends addba req */
489 ini = SCB_AMPDU_INI(scb_ampdu, tid);
490 if (ini->magic != INI_MAGIC) {
491 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
497 wlc_sendampdu(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
498 struct sk_buff **pdu, int prec)
500 struct wlc_info *wlc;
501 struct osl_info *osh;
502 struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
505 u8 preamble_type = WLC_GF_PREAMBLE;
506 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
507 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
508 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
510 bool rr = true, fbr = false;
511 uint i, count = 0, fifo, seg_cnt = 0;
512 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
513 u32 ampdu_len, maxlen = 0;
514 d11txh_t *txh = NULL;
516 struct ieee80211_hdr *h;
518 scb_ampdu_t *scb_ampdu;
519 scb_ampdu_tid_ini_t *ini;
521 bool use_rts = false, use_cts = false;
522 ratespec_t rspec = 0, rspec_fallback = 0;
523 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
524 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
525 struct ieee80211_rts *rts;
529 struct ieee80211_tx_info *tx_info;
538 tid = (u8) (p->priority);
539 ASSERT(tid < AMPDU_MAX_SCB_TID);
541 f = ampdu->fifo_tb + prio2fifo[tid];
543 scb = wlc->pub->global_scb;
544 ASSERT(scb->magic == SCB_MAGIC);
546 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
548 ini = &scb_ampdu->ini[tid];
550 /* Let pressure continue to build ... */
551 qlen = pktq_plen(&qi->q, prec);
552 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
556 wlc_ampdu_agg(ampdu, scb, p, tid);
558 if (wlc->block_datafifo) {
559 WL_ERROR("%s: Fifo blocked\n", __func__);
562 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
566 struct ieee80211_tx_rate *txrate;
568 tx_info = IEEE80211_SKB_CB(p);
569 txrate = tx_info->status.rates;
571 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
572 err = wlc_prep_pdu(wlc, p, &fifo);
574 WL_ERROR("%s: AMPDU flag is off!\n", __func__);
581 if (err == BCME_BUSY) {
582 WL_ERROR("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n",
583 wlc->pub->unit, seq);
584 WLCNTINCR(ampdu->cnt->sduretry);
589 /* error in the packet; reject it */
590 WL_AMPDU_ERR("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n",
591 wlc->pub->unit, seq);
592 WLCNTINCR(ampdu->cnt->sdurejected);
598 /* pkt is good to be aggregated */
599 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
600 txh = (d11txh_t *) p->data;
601 plcp = (u8 *) (txh + 1);
602 h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
603 seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
604 index = TX_SEQ_TO_INDEX(seq);
606 /* check mcl fields and test whether it can be agg'd */
607 mcl = le16_to_cpu(txh->MacTxControlLow);
608 mcl &= ~TXC_AMPDU_MASK;
609 fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3);
611 txh->PreloadSize = 0; /* always default to 0 */
613 /* Handle retry limits */
614 if (txrate[0].count <= rr_retry_limit) {
625 /* extract the length info */
626 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
627 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
629 /* retrieve null delimiter count */
630 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
633 WL_AMPDU_TX("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
634 wlc->pub->unit, count, len);
637 * aggregateable mpdu. For ucode/hw agg,
638 * test whether need to break or change the epoch
642 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
643 /* refill the bits since might be a retx mpdu */
644 mcl |= TXC_STARTMSDU;
645 rts = (struct ieee80211_rts *)&txh->rts_frame;
646 fc = le16_to_cpu(rts->frame_control);
647 if ((fc & FC_KIND_MASK) == FC_RTS) {
651 if ((fc & FC_KIND_MASK) == FC_CTS) {
656 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
657 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
660 len = roundup(len, 4);
661 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
663 dma_len += (u16) pkttotlen(osh, p);
665 WL_AMPDU_TX("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n",
666 wlc->pub->unit, ampdu_len, seg_cnt, ndelim);
668 txh->MacTxControlLow = cpu_to_le16(mcl);
670 /* this packet is added */
673 /* patch the first MPDU */
675 u8 plcp0, plcp3, is40, sgi;
676 struct ieee80211_sta *sta;
678 sta = tx_info->control.sta;
684 plcp0 = txh->FragPLCPFallback[0];
685 plcp3 = txh->FragPLCPFallback[3];
688 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
689 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
690 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
691 ASSERT(mcs < MCS_TABLE_SIZE);
693 min(scb_ampdu->max_rxlen,
694 ampdu->max_txlen[mcs][is40][sgi]);
696 WL_NONE("sendampdu: sgi %d, is40 %d, mcs %d\n",
699 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
703 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
704 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
706 /* rebuild the rspec and rspec_fallback */
707 rspec = RSPEC_MIMORATE;
708 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
709 if (plcp[0] & MIMO_PLCP_40MHZ)
710 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
712 if (fbr_iscck) /* CCK */
714 CCK_RSPEC(CCK_PHY2MAC_RATE
715 (txh->FragPLCPFallback[0]));
717 rspec_fallback = RSPEC_MIMORATE;
719 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
720 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
722 (PHY_TXC1_BW_40MHZ <<
726 if (use_rts || use_cts) {
728 wlc_rspec_to_rts_rspec(wlc, rspec, false,
731 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
732 false, mimo_ctlchbw);
736 /* if (first mpdu for host agg) */
737 /* test whether to add more */
738 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
739 (count == f->mcs2ampdu_table[mcs])) {
740 WL_AMPDU_ERR("wl%d: PR 37644: stopping ampdu at %d for mcs %d\n",
741 wlc->pub->unit, count, mcs);
745 if (count == scb_ampdu->max_pdu) {
746 WL_NONE("Stop taking from q, reached %d deep\n",
751 /* check to see if the next pkt is a candidate for aggregation */
752 p = pktq_ppeek(&qi->q, prec);
753 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
756 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
757 ((u8) (p->priority) == tid)) {
760 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
761 plen = max(scb_ampdu->min_len, plen);
763 if ((plen + ampdu_len) > maxlen) {
765 WL_ERROR("%s: Bogus plen #1\n",
771 /* check if there are enough descriptors available */
772 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
773 WL_ERROR("%s: No fifo space !!!!!!\n",
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 *) pkt[count - 1]->data;
793 mcl = le16_to_cpu(txh->MacTxControlLow);
794 mcl &= ~TXC_AMPDU_MASK;
795 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
796 txh->MacTxControlLow = cpu_to_le16(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 = ((le16_to_cpu(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 *) pkt[0]->data;
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 = cpu_to_le16(mmodelen);
822 preamble_type = WLC_MM_PREAMBLE;
824 if (txh->MModeFbrLen) {
826 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
827 txh->MModeFbrLen = cpu_to_le16(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 = cpu_to_le16(dma_len);
836 txh->PreloadSize = 0;
838 mch = le16_to_cpu(txh->MacTxControlHigh);
840 /* update RTS dur fields */
841 if (use_rts || use_cts) {
843 rts = (struct ieee80211_rts *)&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->duration = cpu_to_le16(durid);
858 durid = wlc_compute_rtscts_dur(wlc, use_cts,
861 rts_fbr_preamble_type,
864 txh->RTSDurFallback = cpu_to_le16(durid);
865 /* set TxFesTimeNormal */
866 txh->TxFesTimeNormal = rts->duration;
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 = cpu_to_le16(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 = le16_to_cpu(txh->TxFrameID);
886 if (frameid & TXFID_RATE_PROBE_MASK) {
887 WL_ERROR("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n",
890 for (i = 0; i < count; i++)
891 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
892 ampdu->txpkt_weight);
900 wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
901 struct sk_buff *p, tx_status_t *txs)
903 scb_ampdu_t *scb_ampdu;
904 struct wlc_info *wlc = ampdu->wlc;
905 scb_ampdu_tid_ini_t *ini;
907 struct ieee80211_tx_info *tx_info;
909 tx_info = IEEE80211_SKB_CB(p);
910 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
912 ASSERT(scb->magic == SCB_MAGIC);
913 ASSERT(txs->status & TX_STATUS_AMPDU);
914 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
916 ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
917 ASSERT(ini->scb == scb);
919 /* BMAC_NOTE: For the split driver, second level txstatus comes later
920 * So if the ACK was received then wait for the second level else just
923 if (txs->status & TX_STATUS_ACK_RCV) {
926 /* wait till the next 8 bytes of txstatus is available */
929 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
932 if (status_delay > 10) {
933 ASSERT(status_delay <= 10);
938 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
939 ASSERT(s1 & TX_STATUS_AMPDU);
940 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
943 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
944 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
948 rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
949 tx_status_t *txs, u8 mcs)
951 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
954 /* clear the rest of the rates */
955 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
961 #define SHORTNAME "AMPDU status"
963 static void BCMFASTPATH
964 wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
965 struct sk_buff *p, tx_status_t *txs,
968 scb_ampdu_t *scb_ampdu;
969 struct wlc_info *wlc = ampdu->wlc;
970 scb_ampdu_tid_ini_t *ini;
971 u8 bitmap[8], queue, tid;
974 struct ieee80211_hdr *h;
975 u16 seq, start_seq = 0, bindex, index, mcl;
977 bool ba_recd = false, ack_recd = false;
978 u8 suc_mpdu = 0, tot_mpdu = 0;
980 bool update_rate = true, retry = true, tx_error = false;
983 u8 retry_limit, rr_retry_limit;
984 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
987 u8 hole[AMPDU_MAX_MPDU];
988 memset(hole, 0, sizeof(hole));
991 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
992 ASSERT(txs->status & TX_STATUS_AMPDU);
994 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
997 tid = (u8) (p->priority);
999 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1000 retry_limit = ampdu->retry_limit_tid[tid];
1001 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
1003 ASSERT(ini->scb == scb);
1005 memset(bitmap, 0, sizeof(bitmap));
1006 queue = txs->frameid & TXFID_QUEUE_MASK;
1007 ASSERT(queue < AC_COUNT);
1009 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1011 if (txs->status & TX_STATUS_ACK_RCV) {
1012 if (TX_STATUS_SUPR_UF == supr_status) {
1013 update_rate = false;
1016 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1017 start_seq = txs->sequence >> SEQNUM_SHIFT;
1018 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1019 TX_STATUS_BA_BMAP03_SHIFT;
1021 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1022 ASSERT(s1 & TX_STATUS_AMPDU);
1025 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1026 TX_STATUS_BA_BMAP47_SHIFT;
1027 bitmap[1] = (s1 >> 8) & 0xff;
1028 bitmap[2] = (s1 >> 16) & 0xff;
1029 bitmap[3] = (s1 >> 24) & 0xff;
1031 bitmap[4] = s2 & 0xff;
1032 bitmap[5] = (s2 >> 8) & 0xff;
1033 bitmap[6] = (s2 >> 16) & 0xff;
1034 bitmap[7] = (s2 >> 24) & 0xff;
1038 WLCNTINCR(ampdu->cnt->noba);
1040 update_rate = false;
1041 if (supr_status == TX_STATUS_SUPR_BADCH) {
1042 WL_ERROR("%s: Pkt tx suppressed, illegal channel possibly %d\n",
1044 CHSPEC_CHANNEL(wlc->default_bss->chanspec));
1046 if (supr_status == TX_STATUS_SUPR_FRAG)
1047 WL_NONE("%s: AMPDU frag err\n",
1050 WL_ERROR("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n",
1051 __func__, supr_status);
1053 /* no need to retry for badch; will fail again */
1054 if (supr_status == TX_STATUS_SUPR_BADCH ||
1055 supr_status == TX_STATUS_SUPR_EXPTIME) {
1057 wlc->pub->_cnt->txchanrej++;
1058 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1060 wlc->pub->_cnt->txexptime++;
1062 /* TX underflow : try tuning pre-loading or ampdu size */
1063 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1064 /* if there were underflows, but pre-loading is not active,
1065 notify rate adaptation.
1067 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1072 } else if (txs->phyerr) {
1073 update_rate = false;
1074 wlc->pub->_cnt->txphyerr++;
1075 WL_ERROR("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n",
1076 wlc->pub->unit, txs->phyerr);
1078 if (WL_ERROR_ON()) {
1079 prpkt("txpkt (AMPDU)", wlc->osh, p);
1080 wlc_print_txdesc((d11txh_t *) p->data);
1082 wlc_print_txstatus(txs);
1086 /* loop through all pkts and retry if not acked */
1088 tx_info = IEEE80211_SKB_CB(p);
1089 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1090 txh = (d11txh_t *) p->data;
1091 mcl = le16_to_cpu(txh->MacTxControlLow);
1092 plcp = (u8 *) (txh + 1);
1093 h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
1094 seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
1096 if (tot_mpdu == 0) {
1097 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1098 mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel);
1101 index = TX_SEQ_TO_INDEX(seq);
1104 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1106 WL_AMPDU_TX("%s: tid %d seq is %d, start_seq is %d, bindex is %d set %d, index %d\n",
1107 __func__, tid, seq, start_seq, bindex,
1108 isset(bitmap, bindex), index);
1110 /* if acked then clear bit and free packet */
1111 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1112 && isset(bitmap, bindex)) {
1113 ini->tx_in_transit--;
1114 ini->txretry[index] = 0;
1116 /* ampdu_ack_len: number of acked aggregated frames */
1117 /* ampdu_ack_map: block ack bit map for the aggregation */
1118 /* ampdu_len: number of aggregated frames */
1119 rate_status(wlc, tx_info, txs, mcs);
1120 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1121 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1123 /* XXX TODO: Make these accurate. */
1124 tx_info->status.ampdu_ack_len =
1126 status & TX_STATUS_FRM_RTX_MASK) >>
1127 TX_STATUS_FRM_RTX_SHIFT;
1128 tx_info->status.ampdu_len =
1130 status & TX_STATUS_FRM_RTX_MASK) >>
1131 TX_STATUS_FRM_RTX_SHIFT;
1133 skb_pull(p, D11_PHY_HDR_LEN);
1134 skb_pull(p, D11_TXH_LEN);
1136 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1142 /* either retransmit or send bar if ack not recd */
1144 struct ieee80211_tx_rate *txrate =
1145 tx_info->status.rates;
1146 if (retry && (txrate[0].count < (int)retry_limit)) {
1147 ini->txretry[index]++;
1148 ini->tx_in_transit--;
1149 /* Use high prededence for retransmit to give some punch */
1150 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1151 wlc_txq_enq(wlc, scb, p,
1152 WLC_PRIO_TO_HI_PREC(tid));
1155 ini->tx_in_transit--;
1156 ieee80211_tx_info_clear_status(tx_info);
1158 IEEE80211_TX_STAT_AMPDU_NO_BACK;
1159 skb_pull(p, D11_PHY_HDR_LEN);
1160 skb_pull(p, D11_TXH_LEN);
1161 WL_ERROR("%s: BA Timeout, seq %d, in_transit %d\n",
1162 SHORTNAME, seq, ini->tx_in_transit);
1163 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1169 /* break out if last packet of ampdu */
1170 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1174 p = GETNEXTTXP(wlc, queue);
1180 wlc_send_q(wlc, wlc->active_queue);
1182 /* update rate state */
1183 if (WLANTSEL_ENAB(wlc))
1184 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1186 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1190 ampdu_cleanup_tid_ini(struct ampdu_info *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
1193 scb_ampdu_tid_ini_t *ini;
1194 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1198 WL_AMPDU_CTL("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1199 ampdu->wlc->pub->unit, tid);
1201 if (ini->tx_in_transit && !force)
1204 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1205 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1207 /* free all buffered tx packets */
1208 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
1211 /* initialize the initiator code for tid */
1212 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
1213 scb_ampdu_t *scb_ampdu,
1214 u8 tid, bool override)
1216 scb_ampdu_tid_ini_t *ini;
1219 ASSERT(scb_ampdu->scb);
1220 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1221 ASSERT(tid < AMPDU_MAX_SCB_TID);
1223 /* check for per-tid control of ampdu */
1224 if (!ampdu->ini_enable[tid]) {
1225 WL_ERROR("%s: Rejecting tid %d\n", __func__, tid);
1229 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1231 ini->scb = scb_ampdu->scb;
1232 ini->magic = INI_MAGIC;
1233 WLCNTINCR(ampdu->cnt->txaddbareq);
1238 static int wlc_ampdu_set(struct ampdu_info *ampdu, bool on)
1240 struct wlc_info *wlc = ampdu->wlc;
1242 wlc->pub->_ampdu = false;
1245 if (!N_ENAB(wlc->pub)) {
1246 WL_AMPDU_ERR("wl%d: driver not nmode enabled\n",
1248 return BCME_UNSUPPORTED;
1250 if (!wlc_ampdu_cap(ampdu)) {
1251 WL_AMPDU_ERR("wl%d: device not ampdu capable\n",
1253 return BCME_UNSUPPORTED;
1255 wlc->pub->_ampdu = on;
1261 static bool wlc_ampdu_cap(struct ampdu_info *ampdu)
1263 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1269 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
1273 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1274 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1276 rate = MCS_RATE(mcs, false, false);
1277 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1278 /* 40 MHz, No SGI */
1279 rate = MCS_RATE(mcs, true, false);
1280 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1282 rate = MCS_RATE(mcs, false, true);
1283 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1285 rate = MCS_RATE(mcs, true, true);
1286 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1291 wlc_ampdu_null_delim_cnt(struct ampdu_info *ampdu, struct scb *scb,
1292 ratespec_t rspec, int phylen)
1294 scb_ampdu_t *scb_ampdu;
1295 int bytes, cnt, tmp;
1299 ASSERT(SCB_AMPDU(scb));
1301 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1304 if (scb_ampdu->mpdu_density == 0)
1307 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1308 density x is in 2^(x-4) usec
1309 ==> # of bytes needed for req density = rate/2^(17-x)
1310 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1313 tx_density = scb_ampdu->mpdu_density;
1315 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1316 tmp = 1 << (17 - tx_density);
1317 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1319 if (bytes > phylen) {
1320 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1327 void wlc_ampdu_macaddr_upd(struct wlc_info *wlc)
1329 char template[T_RAM_ACCESS_SZ * 2];
1331 /* driver needs to write the ta in the template; ta is at offset 16 */
1332 memset(template, 0, sizeof(template));
1333 memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
1334 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1338 bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
1340 return wlc->ampdu->ini_enable[tid];
1343 void wlc_ampdu_shm_upd(struct ampdu_info *ampdu)
1345 struct wlc_info *wlc = ampdu->wlc;
1347 /* Extend ucode internal watchdog timer to match larger received frames */
1348 if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1349 AMPDU_RX_FACTOR_64K) {
1350 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1351 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1353 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1354 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);