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>
22 #include <bcmendian.h>
31 #include <wlc_event.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>
42 #include <bcm_rpc_tp.h>
43 #include <wlc_rpctx.h>
46 #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
47 #define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
48 #define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
49 #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
50 #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
51 #define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
52 #define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
53 #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
54 #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
55 #define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
56 #define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
57 #define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
58 #define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
60 #define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
61 #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
64 #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
65 #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
66 #define FFPLD_PLD_INCR 1000 /* increments in bytes */
67 #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
68 * accumulate between resets.
71 #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
73 /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
74 #define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
75 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
84 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
87 /* structure to hold tx fifo information and pre-loading state
88 * counters specific to tx underflows of ampdus
89 * some counters might be redundant with the ones in wlc or ampdu structures.
90 * This allows to maintain a specific state independantly of
91 * how often and/or when the wlc counters are updated.
93 typedef struct wlc_fifo_info {
94 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
95 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
96 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
97 u32 accum_txfunfl; /* num of underflows since we modified pld params */
98 u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
99 u32 prev_txampdu; /* previous reading of tx ampdu */
100 u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
103 /* AMPDU module specific state */
105 wlc_info_t *wlc; /* pointer to main wlc structure */
106 int scb_handle; /* scb cubby handle to retrieve data from scb */
107 u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
108 u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
109 u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
110 u8 retry_limit; /* mpdu transmit retry limit */
111 u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
112 u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
113 /* per-tid mpdu transmit retry limit at regular rate */
114 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
115 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
116 s8 max_pdu; /* max pdus allowed in ampdu */
117 u8 dur; /* max duration of an ampdu (in msec) */
118 u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
119 u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
120 u32 ffpld_rsvd; /* number of bytes to reserve for preload */
121 u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
122 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
123 bool mfbr; /* enable multiple fallback rate */
124 u32 tx_max_funl; /* underflows should be kept such that
125 * (tx_max_funfl*underflows) < tx frames
127 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
132 bool waiting_status; /* To help sanity checks */
136 #define AMPDU_CLEANUPFLAG_RX (0x1)
137 #define AMPDU_CLEANUPFLAG_TX (0x2)
139 #define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
140 #define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
142 static void wlc_ffpld_init(ampdu_info_t *ampdu);
143 static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int f);
144 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
146 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
147 scb_ampdu_t *scb_ampdu,
148 u8 tid, bool override);
149 static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
151 static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur);
152 static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
153 static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
155 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
157 static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
158 void *p, tx_status_t *txs,
162 static inline u16 pkt_txh_seqnum(wlc_info_t *wlc, void *p)
165 struct dot11_header *h;
166 txh = (d11txh_t *) PKTDATA(p);
167 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
168 return ltoh16(h->seq) >> SEQNUM_SHIFT;
171 ampdu_info_t *wlc_ampdu_attach(wlc_info_t *wlc)
176 /* some code depends on packed structures */
177 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
178 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
179 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
180 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
181 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
183 ampdu = kzalloc(sizeof(ampdu_info_t), GFP_ATOMIC);
185 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem\n", wlc->pub->unit));
190 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
191 ampdu->ini_enable[i] = true;
192 /* Disable ampdu for VO by default */
193 ampdu->ini_enable[PRIO_8021D_VO] = false;
194 ampdu->ini_enable[PRIO_8021D_NC] = false;
196 /* Disable ampdu for BK by default since not enough fifo space */
197 ampdu->ini_enable[PRIO_8021D_NONE] = false;
198 ampdu->ini_enable[PRIO_8021D_BK] = false;
200 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
201 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
202 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
203 ampdu->max_pdu = AUTO;
204 ampdu->dur = AMPDU_MAX_DUR;
205 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
207 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
208 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
209 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
210 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
212 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
214 /* Restrict to smaller rcv size for BMAC dongle */
215 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
217 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
218 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
220 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
221 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
222 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
225 ampdu_update_max_txlen(ampdu, ampdu->dur);
227 /* try to set ampdu to the default value */
228 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
230 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
231 wlc_ffpld_init(ampdu);
236 void wlc_ampdu_detach(ampdu_info_t *ampdu)
243 /* free all ini's which were to be freed on callbacks which were never called */
244 for (i = 0; i < AMPDU_INI_FREE; i++) {
245 if (ampdu->ini_free[i]) {
246 kfree(ampdu->ini_free[i]);
250 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
254 void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
256 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
259 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
262 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
263 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
267 /* reset the ampdu state machine so that it can gracefully handle packets that were
268 * freed from the dma and tx queues during reinit
270 void wlc_ampdu_reset(ampdu_info_t *ampdu)
272 WL_NONE(("%s: Entering\n", __func__));
275 static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
277 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
280 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
282 /* go back to legacy size if some preloading is occuring */
283 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
284 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
285 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
288 /* apply user override */
289 if (ampdu->max_pdu != AUTO)
290 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
292 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
294 if (scb_ampdu->max_rxlen)
296 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
298 scb_ampdu->release = min(scb_ampdu->release,
299 ampdu->fifo_tb[TX_AC_BE_FIFO].
300 mcs2ampdu_table[FFPLD_MAX_MCS]);
302 ASSERT(scb_ampdu->release);
305 void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
307 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
310 static void wlc_ffpld_init(ampdu_info_t *ampdu)
313 wlc_fifo_info_t *fifo;
315 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
316 fifo = (ampdu->fifo_tb + j);
317 fifo->ampdu_pld_size = 0;
318 for (i = 0; i <= FFPLD_MAX_MCS; i++)
319 fifo->mcs2ampdu_table[i] = 255;
320 fifo->dmaxferrate = 0;
321 fifo->accum_txampdu = 0;
322 fifo->prev_txfunfl = 0;
323 fifo->accum_txfunfl = 0;
328 /* evaluate the dma transfer rate using the tx underflows as feedback.
329 * If necessary, increase tx fifo preloading. If not enough,
330 * decrease maximum ampdu size for each mcs till underflows stop
331 * Return 1 if pre-loading not active, -1 if not an underflow event,
332 * 0 if pre-loading module took care of the event.
334 static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int fid)
336 ampdu_info_t *ampdu = wlc->ampdu;
337 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
340 u32 current_ampdu_cnt = 0;
343 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
347 /* return if we got here for a different reason than underflows */
350 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
351 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
352 if (new_txunfl == 0) {
353 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
356 fifo->prev_txfunfl = cur_txunfl;
358 if (!ampdu->tx_max_funl)
361 /* check if fifo is big enough */
362 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
363 WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
367 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
370 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
371 fifo->accum_txfunfl += new_txunfl;
373 /* we need to wait for at least 10 underflows */
374 if (fifo->accum_txfunfl < 10)
377 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
378 current_ampdu_cnt, fifo->accum_txfunfl));
381 compute the current ratio of tx unfl per ampdu.
382 When the current ampdu count becomes too
383 big while the ratio remains small, we reset
384 the current count in order to not
385 introduce too big of a latency in detecting a
386 large amount of tx underflows later.
389 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
391 if (txunfl_ratio > ampdu->tx_max_funl) {
392 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
393 fifo->accum_txfunfl = 0;
398 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
400 /* In case max value max_pdu is already lower than
401 the fifo depth, there is nothing more we can do.
404 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
405 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
406 fifo->accum_txfunfl = 0;
410 if (fifo->ampdu_pld_size < max_pld_size) {
412 /* increment by TX_FIFO_PLD_INC bytes */
413 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
414 if (fifo->ampdu_pld_size > max_pld_size)
415 fifo->ampdu_pld_size = max_pld_size;
417 /* update scb release size */
418 scb_ampdu_update_config_all(ampdu);
421 compute a new dma xfer rate for max_mpdu @ max mcs.
422 This is the minimum dma rate that
423 can acheive no unferflow condition for the current mpdu size.
425 /* note : we divide/multiply by 100 to avoid integer overflows */
428 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
429 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
431 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
432 fifo->dmaxferrate, fifo->ampdu_pld_size));
435 /* decrease ampdu size */
436 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
437 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
438 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
439 AMPDU_NUM_MPDU_LEGACY - 1;
441 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
443 /* recompute the table */
444 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
446 /* update scb release size */
447 scb_ampdu_update_config_all(ampdu);
450 fifo->accum_txfunfl = 0;
454 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
457 u32 phy_rate, dma_rate, tmp;
459 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
461 /* recompute the dma rate */
462 /* note : we divide/multiply by 100 to avoid integer overflows */
464 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
465 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
468 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
469 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
470 fifo->dmaxferrate = dma_rate;
472 /* fill up the mcs2ampdu table; do not recalc the last mcs */
473 dma_rate = dma_rate >> 7;
474 for (i = 0; i < FFPLD_MAX_MCS; i++) {
475 /* shifting to keep it within integer range */
476 phy_rate = MCS_RATE(i, true, false) >> 7;
477 if (phy_rate > dma_rate) {
478 tmp = ((fifo->ampdu_pld_size * phy_rate) /
479 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
480 tmp = min_t(u32, tmp, 255);
481 fifo->mcs2ampdu_table[i] = (u8) tmp;
486 static void BCMFASTPATH
487 wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, void *p, uint prec)
489 scb_ampdu_t *scb_ampdu;
490 scb_ampdu_tid_ini_t *ini;
491 u8 tid = (u8) PKTPRIO(p);
493 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
495 /* initialize initiator on first packet; sends addba req */
496 ini = SCB_AMPDU_INI(scb_ampdu, tid);
497 if (ini->magic != INI_MAGIC) {
498 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
504 wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, void **pdu, int prec)
507 struct osl_info *osh;
508 void *p, *pkt[AMPDU_MAX_MPDU];
511 u8 preamble_type = WLC_GF_PREAMBLE;
512 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
513 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
514 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
516 bool rr = true, fbr = false;
517 uint i, count = 0, fifo, seg_cnt = 0;
518 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
519 u32 ampdu_len, maxlen = 0;
520 d11txh_t *txh = NULL;
522 struct dot11_header *h;
524 scb_ampdu_t *scb_ampdu;
525 scb_ampdu_tid_ini_t *ini;
527 bool use_rts = false, use_cts = false;
528 ratespec_t rspec = 0, rspec_fallback = 0;
529 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
530 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
531 struct dot11_rts_frame *rts;
535 struct ieee80211_tx_info *tx_info;
544 tid = (u8) PKTPRIO(p);
545 ASSERT(tid < AMPDU_MAX_SCB_TID);
547 f = ampdu->fifo_tb + prio2fifo[tid];
549 scb = wlc->pub->global_scb;
550 ASSERT(scb->magic == SCB_MAGIC);
552 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
554 ini = &scb_ampdu->ini[tid];
556 /* Let pressure continue to build ... */
557 qlen = pktq_plen(&qi->q, prec);
558 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
562 wlc_ampdu_agg(ampdu, scb, p, tid);
564 if (wlc->block_datafifo) {
565 WL_ERROR(("%s: Fifo blocked\n", __func__));
568 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
572 struct ieee80211_tx_rate *txrate;
574 tx_info = IEEE80211_SKB_CB(p);
575 txrate = tx_info->status.rates;
577 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
578 err = wlc_prep_pdu(wlc, p, &fifo);
580 WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
587 if (err == BCME_BUSY) {
588 WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq));
589 WLCNTINCR(ampdu->cnt->sduretry);
594 /* error in the packet; reject it */
595 WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc->pub->unit, seq));
596 WLCNTINCR(ampdu->cnt->sdurejected);
602 /* pkt is good to be aggregated */
603 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
604 txh = (d11txh_t *) PKTDATA(p);
605 plcp = (u8 *) (txh + 1);
606 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
607 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
608 index = TX_SEQ_TO_INDEX(seq);
610 /* check mcl fields and test whether it can be agg'd */
611 mcl = ltoh16(txh->MacTxControlLow);
612 mcl &= ~TXC_AMPDU_MASK;
613 fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
615 txh->PreloadSize = 0; /* always default to 0 */
617 /* Handle retry limits */
618 if (txrate[0].count <= rr_retry_limit) {
629 /* extract the length info */
630 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
631 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
633 /* retrieve null delimiter count */
634 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
637 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
638 wlc->pub->unit, count, len));
641 * aggregateable mpdu. For ucode/hw agg,
642 * test whether need to break or change the epoch
646 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
647 /* refill the bits since might be a retx mpdu */
648 mcl |= TXC_STARTMSDU;
649 rts = (struct dot11_rts_frame *)&txh->rts_frame;
650 fc = ltoh16(rts->fc);
651 if ((fc & FC_KIND_MASK) == FC_RTS) {
655 if ((fc & FC_KIND_MASK) == FC_CTS) {
660 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
661 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
664 len = roundup(len, 4);
665 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
667 dma_len += (u16) pkttotlen(osh, p);
669 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));
671 txh->MacTxControlLow = htol16(mcl);
673 /* this packet is added */
676 /* patch the first MPDU */
678 u8 plcp0, plcp3, is40, sgi;
679 struct ieee80211_sta *sta;
681 sta = tx_info->control.sta;
687 plcp0 = txh->FragPLCPFallback[0];
688 plcp3 = txh->FragPLCPFallback[3];
691 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
692 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
693 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
694 ASSERT(mcs < MCS_TABLE_SIZE);
696 min(scb_ampdu->max_rxlen,
697 ampdu->max_txlen[mcs][is40][sgi]);
699 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
702 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
706 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
707 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
709 /* rebuild the rspec and rspec_fallback */
710 rspec = RSPEC_MIMORATE;
711 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
712 if (plcp[0] & MIMO_PLCP_40MHZ)
713 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
715 if (fbr_iscck) /* CCK */
717 CCK_RSPEC(CCK_PHY2MAC_RATE
718 (txh->FragPLCPFallback[0]));
720 rspec_fallback = RSPEC_MIMORATE;
722 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
723 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
725 (PHY_TXC1_BW_40MHZ <<
729 if (use_rts || use_cts) {
731 wlc_rspec_to_rts_rspec(wlc, rspec, false,
734 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
735 false, mimo_ctlchbw);
739 /* if (first mpdu for host agg) */
740 /* test whether to add more */
741 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
742 (count == f->mcs2ampdu_table[mcs])) {
743 WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc->pub->unit, count, mcs));
747 if (count == scb_ampdu->max_pdu) {
748 WL_NONE(("Stop taking from q, reached %d deep\n",
749 scb_ampdu->max_pdu));
753 /* check to see if the next pkt is a candidate for aggregation */
754 p = pktq_ppeek(&qi->q, prec);
755 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
758 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
759 ((u8) PKTPRIO(p) == tid)) {
762 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
763 plen = max(scb_ampdu->min_len, plen);
765 if ((plen + ampdu_len) > maxlen) {
767 WL_ERROR(("%s: Bogus plen #1\n",
773 /* check if there are enough descriptors available */
774 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
775 WL_ERROR(("%s: No fifo space !!!!!!\n", __func__));
779 p = pktq_pdeq(&qi->q, prec);
787 ini->tx_in_transit += count;
790 WLCNTADD(ampdu->cnt->txmpdu, count);
792 /* patch up the last txh */
793 txh = (d11txh_t *) PKTDATA(pkt[count - 1]);
794 mcl = ltoh16(txh->MacTxControlLow);
795 mcl &= ~TXC_AMPDU_MASK;
796 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
797 txh->MacTxControlLow = htol16(mcl);
799 /* remove the null delimiter after last mpdu */
800 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
801 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
802 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
804 /* remove the pad len from last mpdu */
805 fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
806 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
807 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
808 ampdu_len -= roundup(len, 4) - len;
810 /* patch up the first txh & plcp */
811 txh = (d11txh_t *) PKTDATA(pkt[0]);
812 plcp = (u8 *) (txh + 1);
814 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
815 /* mark plcp to indicate ampdu */
816 WLC_SET_MIMO_PLCP_AMPDU(plcp);
818 /* reset the mixed mode header durations */
821 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
822 txh->MModeLen = htol16(mmodelen);
823 preamble_type = WLC_MM_PREAMBLE;
825 if (txh->MModeFbrLen) {
827 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
828 txh->MModeFbrLen = htol16(mmfbrlen);
829 fbr_preamble_type = WLC_MM_PREAMBLE;
832 /* set the preload length */
833 if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
834 dma_len = min(dma_len, f->ampdu_pld_size);
835 txh->PreloadSize = htol16(dma_len);
837 txh->PreloadSize = 0;
839 mch = ltoh16(txh->MacTxControlHigh);
841 /* update RTS dur fields */
842 if (use_rts || use_cts) {
844 rts = (struct dot11_rts_frame *)&txh->rts_frame;
845 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
846 TXC_PREAMBLE_RTS_MAIN_SHORT)
847 rts_preamble_type = WLC_SHORT_PREAMBLE;
849 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
850 TXC_PREAMBLE_RTS_FB_SHORT)
851 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
854 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
855 rspec, rts_preamble_type,
856 preamble_type, ampdu_len,
858 rts->durid = htol16(durid);
859 durid = wlc_compute_rtscts_dur(wlc, use_cts,
862 rts_fbr_preamble_type,
865 txh->RTSDurFallback = htol16(durid);
866 /* set TxFesTimeNormal */
867 txh->TxFesTimeNormal = rts->durid;
868 /* set fallback rate version of TxFesTimeNormal */
869 txh->TxFesTimeFallback = txh->RTSDurFallback;
872 /* set flag and plcp for fallback rate */
874 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
875 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
876 mch |= TXC_AMPDU_FBR;
877 txh->MacTxControlHigh = htol16(mch);
878 WLC_SET_MIMO_PLCP_AMPDU(plcp);
879 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
882 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
883 wlc->pub->unit, count, ampdu_len));
885 /* inform rate_sel if it this is a rate probe pkt */
886 frameid = ltoh16(txh->TxFrameID);
887 if (frameid & TXFID_RATE_PROBE_MASK) {
888 WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__));
891 if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
892 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
893 BCM_RPC_TP_HOST_AGG_AMPDU, true);
895 for (i = 0; i < count; i++)
896 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
897 ampdu->txpkt_weight);
899 if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
900 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
901 BCM_RPC_TP_HOST_AGG_AMPDU, false);
910 wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, void *p,
913 scb_ampdu_t *scb_ampdu;
914 wlc_info_t *wlc = ampdu->wlc;
915 scb_ampdu_tid_ini_t *ini;
917 struct ieee80211_tx_info *tx_info;
919 tx_info = IEEE80211_SKB_CB(p);
920 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
922 ASSERT(scb->magic == SCB_MAGIC);
923 ASSERT(txs->status & TX_STATUS_AMPDU);
924 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
926 ini = SCB_AMPDU_INI(scb_ampdu, PKTPRIO(p));
927 ASSERT(ini->scb == scb);
929 /* BMAC_NOTE: For the split driver, second level txstatus comes later
930 * So if the ACK was received then wait for the second level else just
933 if (txs->status & TX_STATUS_ACK_RCV) {
937 /* wait till the next 8 bytes of txstatus is available */
940 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
943 if (status_delay > 10) {
944 ASSERT(status_delay <= 10);
949 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
950 ASSERT(s1 & TX_STATUS_AMPDU);
951 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
954 /* Store the relevant information in ampdu structure */
955 WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
960 bcopy(txs, &du->txs, sizeof(tx_status_t));
961 ampdu->waiting_status = true;
966 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
967 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
971 void wlc_ampdu_txstatus_complete(ampdu_info_t *ampdu, u32 s1, u32 s2)
973 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));
975 ASSERT(ampdu->waiting_status);
977 /* The packet may have been freed if the SCB went away, if so, then still free the
981 struct ieee80211_tx_info *tx_info;
984 tx_info = IEEE80211_SKB_CB(ampdu->p);
985 scb = (struct scb *)tx_info->control.sta->drv_priv;
987 wlc_ampdu_dotxstatus_complete(ampdu, scb, ampdu->p, &du->txs,
992 ampdu->waiting_status = false;
994 #endif /* WLC_HIGH_ONLY */
995 void rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
996 tx_status_t *txs, u8 mcs);
999 rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
1000 tx_status_t *txs, u8 mcs)
1002 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
1005 /* clear the rest of the rates */
1006 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
1008 txrate[i].count = 0;
1012 extern void wlc_txq_enq(wlc_info_t *wlc, struct scb *scb, void *sdu,
1015 #define SHORTNAME "AMPDU status"
1017 static void BCMFASTPATH
1018 wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb, void *p,
1019 tx_status_t *txs, u32 s1, u32 s2)
1021 scb_ampdu_t *scb_ampdu;
1022 wlc_info_t *wlc = ampdu->wlc;
1023 scb_ampdu_tid_ini_t *ini;
1024 u8 bitmap[8], queue, tid;
1027 struct dot11_header *h;
1028 u16 seq, start_seq = 0, bindex, index, mcl;
1030 bool ba_recd = false, ack_recd = false;
1031 u8 suc_mpdu = 0, tot_mpdu = 0;
1033 bool update_rate = true, retry = true, tx_error = false;
1036 u8 retry_limit, rr_retry_limit;
1037 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
1040 u8 hole[AMPDU_MAX_MPDU];
1041 bzero(hole, sizeof(hole));
1044 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1045 ASSERT(txs->status & TX_STATUS_AMPDU);
1047 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1050 tid = (u8) PKTPRIO(p);
1052 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1053 retry_limit = ampdu->retry_limit_tid[tid];
1054 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
1056 ASSERT(ini->scb == scb);
1058 bzero(bitmap, sizeof(bitmap));
1059 queue = txs->frameid & TXFID_QUEUE_MASK;
1060 ASSERT(queue < AC_COUNT);
1062 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1064 if (txs->status & TX_STATUS_ACK_RCV) {
1065 if (TX_STATUS_SUPR_UF == supr_status) {
1066 update_rate = false;
1069 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1070 start_seq = txs->sequence >> SEQNUM_SHIFT;
1071 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1072 TX_STATUS_BA_BMAP03_SHIFT;
1074 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1075 ASSERT(s1 & TX_STATUS_AMPDU);
1078 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1079 TX_STATUS_BA_BMAP47_SHIFT;
1080 bitmap[1] = (s1 >> 8) & 0xff;
1081 bitmap[2] = (s1 >> 16) & 0xff;
1082 bitmap[3] = (s1 >> 24) & 0xff;
1084 bitmap[4] = s2 & 0xff;
1085 bitmap[5] = (s2 >> 8) & 0xff;
1086 bitmap[6] = (s2 >> 16) & 0xff;
1087 bitmap[7] = (s2 >> 24) & 0xff;
1091 WLCNTINCR(ampdu->cnt->noba);
1093 update_rate = false;
1094 if (supr_status == TX_STATUS_SUPR_BADCH) {
1095 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
1097 if (supr_status == TX_STATUS_SUPR_FRAG)
1098 WL_NONE(("%s: AMPDU frag err\n",
1101 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
1103 /* no need to retry for badch; will fail again */
1104 if (supr_status == TX_STATUS_SUPR_BADCH ||
1105 supr_status == TX_STATUS_SUPR_EXPTIME) {
1107 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1108 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1110 WLCNTINCR(wlc->pub->_cnt->txexptime);
1112 /* TX underflow : try tuning pre-loading or ampdu size */
1113 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1114 /* if there were underflows, but pre-loading is not active,
1115 notify rate adaptation.
1117 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1120 #ifdef WLC_HIGH_ONLY
1121 /* With BMAC, TX Underflows should not happen */
1122 WL_ERROR(("wl%d: BMAC TX Underflow?",
1127 } else if (txs->phyerr) {
1128 update_rate = false;
1129 WLCNTINCR(wlc->pub->_cnt->txphyerr);
1130 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc->pub->unit, txs->phyerr));
1133 if (WL_ERROR_ON()) {
1134 prpkt("txpkt (AMPDU)", wlc->osh, p);
1135 wlc_print_txdesc((d11txh_t *) PKTDATA(p));
1136 wlc_print_txstatus(txs);
1142 /* loop through all pkts and retry if not acked */
1144 tx_info = IEEE80211_SKB_CB(p);
1145 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1146 txh = (d11txh_t *) PKTDATA(p);
1147 mcl = ltoh16(txh->MacTxControlLow);
1148 plcp = (u8 *) (txh + 1);
1149 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1150 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1152 if (tot_mpdu == 0) {
1153 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1154 mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1157 index = TX_SEQ_TO_INDEX(seq);
1160 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1162 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1163 "bindex is %d set %d, index %d\n",
1164 __func__, tid, seq, start_seq, bindex,
1165 isset(bitmap, bindex), index));
1167 /* if acked then clear bit and free packet */
1168 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1169 && isset(bitmap, bindex)) {
1170 ini->tx_in_transit--;
1171 ini->txretry[index] = 0;
1173 /* ampdu_ack_len: number of acked aggregated frames */
1174 /* ampdu_ack_map: block ack bit map for the aggregation */
1175 /* ampdu_len: number of aggregated frames */
1176 rate_status(wlc, tx_info, txs, mcs);
1177 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1178 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1180 /* XXX TODO: Make these accurate. */
1181 tx_info->status.ampdu_ack_len =
1183 status & TX_STATUS_FRM_RTX_MASK) >>
1184 TX_STATUS_FRM_RTX_SHIFT;
1185 tx_info->status.ampdu_len =
1187 status & TX_STATUS_FRM_RTX_MASK) >>
1188 TX_STATUS_FRM_RTX_SHIFT;
1190 PKTPULL(p, D11_PHY_HDR_LEN);
1191 PKTPULL(p, D11_TXH_LEN);
1193 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1199 /* either retransmit or send bar if ack not recd */
1201 struct ieee80211_tx_rate *txrate =
1202 tx_info->status.rates;
1203 if (retry && (txrate[0].count < (int)retry_limit)) {
1204 ini->txretry[index]++;
1205 ini->tx_in_transit--;
1206 /* Use high prededence for retransmit to give some punch */
1207 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1208 wlc_txq_enq(wlc, scb, p,
1209 WLC_PRIO_TO_HI_PREC(tid));
1212 ini->tx_in_transit--;
1213 ieee80211_tx_info_clear_status(tx_info);
1215 IEEE80211_TX_STAT_AMPDU_NO_BACK;
1216 PKTPULL(p, D11_PHY_HDR_LEN);
1217 PKTPULL(p, D11_TXH_LEN);
1218 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME, seq, ini->tx_in_transit));
1219 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1225 /* break out if last packet of ampdu */
1226 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1230 p = GETNEXTTXP(wlc, queue);
1236 wlc_send_q(wlc, wlc->active_queue);
1238 /* update rate state */
1239 if (WLANTSEL_ENAB(wlc))
1240 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1242 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1246 ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
1249 scb_ampdu_tid_ini_t *ini;
1250 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1254 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1255 ampdu->wlc->pub->unit, tid));
1257 if (ini->tx_in_transit && !force)
1260 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1261 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1263 /* free all buffered tx packets */
1264 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
1267 /* initialize the initiator code for tid */
1268 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
1269 scb_ampdu_t *scb_ampdu,
1270 u8 tid, bool override)
1272 scb_ampdu_tid_ini_t *ini;
1275 ASSERT(scb_ampdu->scb);
1276 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1277 ASSERT(tid < AMPDU_MAX_SCB_TID);
1279 /* check for per-tid control of ampdu */
1280 if (!ampdu->ini_enable[tid]) {
1281 WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
1285 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1287 ini->scb = scb_ampdu->scb;
1288 ini->magic = INI_MAGIC;
1289 WLCNTINCR(ampdu->cnt->txaddbareq);
1294 int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
1296 wlc_info_t *wlc = ampdu->wlc;
1298 wlc->pub->_ampdu = false;
1301 if (!N_ENAB(wlc->pub)) {
1302 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1304 return BCME_UNSUPPORTED;
1306 if (!wlc_ampdu_cap(ampdu)) {
1307 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1309 return BCME_UNSUPPORTED;
1311 wlc->pub->_ampdu = on;
1317 bool wlc_ampdu_cap(ampdu_info_t *ampdu)
1319 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1325 static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur)
1329 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1330 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1332 rate = MCS_RATE(mcs, false, false);
1333 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1334 /* 40 MHz, No SGI */
1335 rate = MCS_RATE(mcs, true, false);
1336 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1338 rate = MCS_RATE(mcs, false, true);
1339 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1341 rate = MCS_RATE(mcs, true, true);
1342 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1347 wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
1348 ratespec_t rspec, int phylen)
1350 scb_ampdu_t *scb_ampdu;
1351 int bytes, cnt, tmp;
1355 ASSERT(SCB_AMPDU(scb));
1357 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1360 if (scb_ampdu->mpdu_density == 0)
1363 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1364 density x is in 2^(x-4) usec
1365 ==> # of bytes needed for req density = rate/2^(17-x)
1366 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1369 tx_density = scb_ampdu->mpdu_density;
1371 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1372 tmp = 1 << (17 - tx_density);
1373 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1375 if (bytes > phylen) {
1376 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1383 void wlc_ampdu_macaddr_upd(wlc_info_t *wlc)
1385 char template[T_RAM_ACCESS_SZ * 2];
1387 /* driver needs to write the ta in the template; ta is at offset 16 */
1388 bzero(template, sizeof(template));
1389 bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETHER_ADDR_LEN);
1390 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1394 bool wlc_aggregatable(wlc_info_t *wlc, u8 tid)
1396 return wlc->ampdu->ini_enable[tid];
1399 void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
1401 wlc_info_t *wlc = ampdu->wlc;
1403 /* Extend ucode internal watchdog timer to match larger received frames */
1404 if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1405 AMPDU_RX_FACTOR_64K) {
1406 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1407 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1409 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1410 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);