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 #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
43 #define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
44 #define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
45 #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
46 #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
47 #define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
48 #define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
49 #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
50 #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
51 #define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
52 #define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
53 #define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
54 #define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
56 #define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
57 #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
60 #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
61 #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
62 #define FFPLD_PLD_INCR 1000 /* increments in bytes */
63 #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
64 * accumulate between resets.
67 #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
69 /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
70 #define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
71 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
80 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
83 /* structure to hold tx fifo information and pre-loading state
84 * counters specific to tx underflows of ampdus
85 * some counters might be redundant with the ones in wlc or ampdu structures.
86 * This allows to maintain a specific state independantly of
87 * how often and/or when the wlc counters are updated.
89 typedef struct wlc_fifo_info {
90 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
91 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
92 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
93 u32 accum_txfunfl; /* num of underflows since we modified pld params */
94 u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
95 u32 prev_txampdu; /* previous reading of tx ampdu */
96 u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
99 /* AMPDU module specific state */
101 struct wlc_info *wlc; /* pointer to main wlc structure */
102 int scb_handle; /* scb cubby handle to retrieve data from scb */
103 u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
104 u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
105 u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
106 u8 retry_limit; /* mpdu transmit retry limit */
107 u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
108 u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
109 /* per-tid mpdu transmit retry limit at regular rate */
110 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
111 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
112 s8 max_pdu; /* max pdus allowed in ampdu */
113 u8 dur; /* max duration of an ampdu (in msec) */
114 u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
115 u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
116 u32 ffpld_rsvd; /* number of bytes to reserve for preload */
117 u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
118 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
119 bool mfbr; /* enable multiple fallback rate */
120 u32 tx_max_funl; /* underflows should be kept such that
121 * (tx_max_funfl*underflows) < tx frames
123 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
127 #define AMPDU_CLEANUPFLAG_RX (0x1)
128 #define AMPDU_CLEANUPFLAG_TX (0x2)
130 #define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
131 #define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
133 static void wlc_ffpld_init(ampdu_info_t *ampdu);
134 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
135 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
137 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
138 scb_ampdu_t *scb_ampdu,
139 u8 tid, bool override);
140 static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
142 static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur);
143 static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
144 static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
146 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
148 static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
149 struct sk_buff *p, tx_status_t *txs,
150 u32 frmtxstatus, u32 frmtxstatus2);
152 static inline u16 pkt_txh_seqnum(struct wlc_info *wlc, struct sk_buff *p)
155 struct dot11_header *h;
156 txh = (d11txh_t *) p->data;
157 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
158 return ltoh16(h->seq) >> SEQNUM_SHIFT;
161 ampdu_info_t *wlc_ampdu_attach(struct wlc_info *wlc)
166 /* some code depends on packed structures */
167 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
168 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
169 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
170 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
171 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
173 ampdu = kzalloc(sizeof(ampdu_info_t), GFP_ATOMIC);
175 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem\n", wlc->pub->unit));
180 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
181 ampdu->ini_enable[i] = true;
182 /* Disable ampdu for VO by default */
183 ampdu->ini_enable[PRIO_8021D_VO] = false;
184 ampdu->ini_enable[PRIO_8021D_NC] = false;
186 /* Disable ampdu for BK by default since not enough fifo space */
187 ampdu->ini_enable[PRIO_8021D_NONE] = false;
188 ampdu->ini_enable[PRIO_8021D_BK] = false;
190 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
191 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
192 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
193 ampdu->max_pdu = AUTO;
194 ampdu->dur = AMPDU_MAX_DUR;
195 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
197 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
198 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
199 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
200 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
202 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
203 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
204 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
206 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
207 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
208 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
211 ampdu_update_max_txlen(ampdu, ampdu->dur);
213 /* try to set ampdu to the default value */
214 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
216 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
217 wlc_ffpld_init(ampdu);
222 void wlc_ampdu_detach(ampdu_info_t *ampdu)
229 /* free all ini's which were to be freed on callbacks which were never called */
230 for (i = 0; i < AMPDU_INI_FREE; i++) {
231 if (ampdu->ini_free[i]) {
232 kfree(ampdu->ini_free[i]);
236 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
240 void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
242 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
245 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
248 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
249 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
253 /* reset the ampdu state machine so that it can gracefully handle packets that were
254 * freed from the dma and tx queues during reinit
256 void wlc_ampdu_reset(ampdu_info_t *ampdu)
258 WL_NONE(("%s: Entering\n", __func__));
261 static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
263 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
266 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
268 /* go back to legacy size if some preloading is occuring */
269 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
270 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
271 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
274 /* apply user override */
275 if (ampdu->max_pdu != AUTO)
276 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
278 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
280 if (scb_ampdu->max_rxlen)
282 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
284 scb_ampdu->release = min(scb_ampdu->release,
285 ampdu->fifo_tb[TX_AC_BE_FIFO].
286 mcs2ampdu_table[FFPLD_MAX_MCS]);
288 ASSERT(scb_ampdu->release);
291 void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
293 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
296 static void wlc_ffpld_init(ampdu_info_t *ampdu)
299 wlc_fifo_info_t *fifo;
301 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
302 fifo = (ampdu->fifo_tb + j);
303 fifo->ampdu_pld_size = 0;
304 for (i = 0; i <= FFPLD_MAX_MCS; i++)
305 fifo->mcs2ampdu_table[i] = 255;
306 fifo->dmaxferrate = 0;
307 fifo->accum_txampdu = 0;
308 fifo->prev_txfunfl = 0;
309 fifo->accum_txfunfl = 0;
314 /* evaluate the dma transfer rate using the tx underflows as feedback.
315 * If necessary, increase tx fifo preloading. If not enough,
316 * decrease maximum ampdu size for each mcs till underflows stop
317 * Return 1 if pre-loading not active, -1 if not an underflow event,
318 * 0 if pre-loading module took care of the event.
320 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
322 ampdu_info_t *ampdu = wlc->ampdu;
323 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
326 u32 current_ampdu_cnt = 0;
329 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
333 /* return if we got here for a different reason than underflows */
336 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
337 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
338 if (new_txunfl == 0) {
339 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
342 fifo->prev_txfunfl = cur_txunfl;
344 if (!ampdu->tx_max_funl)
347 /* check if fifo is big enough */
348 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
349 WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
353 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
356 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
357 fifo->accum_txfunfl += new_txunfl;
359 /* we need to wait for at least 10 underflows */
360 if (fifo->accum_txfunfl < 10)
363 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
364 current_ampdu_cnt, fifo->accum_txfunfl));
367 compute the current ratio of tx unfl per ampdu.
368 When the current ampdu count becomes too
369 big while the ratio remains small, we reset
370 the current count in order to not
371 introduce too big of a latency in detecting a
372 large amount of tx underflows later.
375 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
377 if (txunfl_ratio > ampdu->tx_max_funl) {
378 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
379 fifo->accum_txfunfl = 0;
384 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
386 /* In case max value max_pdu is already lower than
387 the fifo depth, there is nothing more we can do.
390 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
391 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
392 fifo->accum_txfunfl = 0;
396 if (fifo->ampdu_pld_size < max_pld_size) {
398 /* increment by TX_FIFO_PLD_INC bytes */
399 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
400 if (fifo->ampdu_pld_size > max_pld_size)
401 fifo->ampdu_pld_size = max_pld_size;
403 /* update scb release size */
404 scb_ampdu_update_config_all(ampdu);
407 compute a new dma xfer rate for max_mpdu @ max mcs.
408 This is the minimum dma rate that
409 can acheive no unferflow condition for the current mpdu size.
411 /* note : we divide/multiply by 100 to avoid integer overflows */
414 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
415 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
417 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
418 fifo->dmaxferrate, fifo->ampdu_pld_size));
421 /* decrease ampdu size */
422 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
423 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
424 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
425 AMPDU_NUM_MPDU_LEGACY - 1;
427 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
429 /* recompute the table */
430 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
432 /* update scb release size */
433 scb_ampdu_update_config_all(ampdu);
436 fifo->accum_txfunfl = 0;
440 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
443 u32 phy_rate, dma_rate, tmp;
445 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
447 /* recompute the dma rate */
448 /* note : we divide/multiply by 100 to avoid integer overflows */
450 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
451 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
454 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
455 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
456 fifo->dmaxferrate = dma_rate;
458 /* fill up the mcs2ampdu table; do not recalc the last mcs */
459 dma_rate = dma_rate >> 7;
460 for (i = 0; i < FFPLD_MAX_MCS; i++) {
461 /* shifting to keep it within integer range */
462 phy_rate = MCS_RATE(i, true, false) >> 7;
463 if (phy_rate > dma_rate) {
464 tmp = ((fifo->ampdu_pld_size * phy_rate) /
465 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
466 tmp = min_t(u32, tmp, 255);
467 fifo->mcs2ampdu_table[i] = (u8) tmp;
472 static void BCMFASTPATH
473 wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, struct sk_buff *p,
476 scb_ampdu_t *scb_ampdu;
477 scb_ampdu_tid_ini_t *ini;
478 u8 tid = (u8) (p->priority);
480 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
482 /* initialize initiator on first packet; sends addba req */
483 ini = SCB_AMPDU_INI(scb_ampdu, tid);
484 if (ini->magic != INI_MAGIC) {
485 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
491 wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, struct sk_buff **pdu,
494 struct wlc_info *wlc;
495 struct osl_info *osh;
496 struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
499 u8 preamble_type = WLC_GF_PREAMBLE;
500 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
501 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
502 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
504 bool rr = true, fbr = false;
505 uint i, count = 0, fifo, seg_cnt = 0;
506 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
507 u32 ampdu_len, maxlen = 0;
508 d11txh_t *txh = NULL;
510 struct dot11_header *h;
512 scb_ampdu_t *scb_ampdu;
513 scb_ampdu_tid_ini_t *ini;
515 bool use_rts = false, use_cts = false;
516 ratespec_t rspec = 0, rspec_fallback = 0;
517 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
518 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
519 struct dot11_rts_frame *rts;
523 struct ieee80211_tx_info *tx_info;
532 tid = (u8) (p->priority);
533 ASSERT(tid < AMPDU_MAX_SCB_TID);
535 f = ampdu->fifo_tb + prio2fifo[tid];
537 scb = wlc->pub->global_scb;
538 ASSERT(scb->magic == SCB_MAGIC);
540 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
542 ini = &scb_ampdu->ini[tid];
544 /* Let pressure continue to build ... */
545 qlen = pktq_plen(&qi->q, prec);
546 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
550 wlc_ampdu_agg(ampdu, scb, p, tid);
552 if (wlc->block_datafifo) {
553 WL_ERROR(("%s: Fifo blocked\n", __func__));
556 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
560 struct ieee80211_tx_rate *txrate;
562 tx_info = IEEE80211_SKB_CB(p);
563 txrate = tx_info->status.rates;
565 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
566 err = wlc_prep_pdu(wlc, p, &fifo);
568 WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
575 if (err == BCME_BUSY) {
576 WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq));
577 WLCNTINCR(ampdu->cnt->sduretry);
582 /* error in the packet; reject it */
583 WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc->pub->unit, seq));
584 WLCNTINCR(ampdu->cnt->sdurejected);
590 /* pkt is good to be aggregated */
591 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
592 txh = (d11txh_t *) p->data;
593 plcp = (u8 *) (txh + 1);
594 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
595 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
596 index = TX_SEQ_TO_INDEX(seq);
598 /* check mcl fields and test whether it can be agg'd */
599 mcl = ltoh16(txh->MacTxControlLow);
600 mcl &= ~TXC_AMPDU_MASK;
601 fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
603 txh->PreloadSize = 0; /* always default to 0 */
605 /* Handle retry limits */
606 if (txrate[0].count <= rr_retry_limit) {
617 /* extract the length info */
618 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
619 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
621 /* retrieve null delimiter count */
622 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
625 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
626 wlc->pub->unit, count, len));
629 * aggregateable mpdu. For ucode/hw agg,
630 * test whether need to break or change the epoch
634 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
635 /* refill the bits since might be a retx mpdu */
636 mcl |= TXC_STARTMSDU;
637 rts = (struct dot11_rts_frame *)&txh->rts_frame;
638 fc = ltoh16(rts->fc);
639 if ((fc & FC_KIND_MASK) == FC_RTS) {
643 if ((fc & FC_KIND_MASK) == FC_CTS) {
648 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
649 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
652 len = roundup(len, 4);
653 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
655 dma_len += (u16) pkttotlen(osh, p);
657 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));
659 txh->MacTxControlLow = htol16(mcl);
661 /* this packet is added */
664 /* patch the first MPDU */
666 u8 plcp0, plcp3, is40, sgi;
667 struct ieee80211_sta *sta;
669 sta = tx_info->control.sta;
675 plcp0 = txh->FragPLCPFallback[0];
676 plcp3 = txh->FragPLCPFallback[3];
679 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
680 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
681 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
682 ASSERT(mcs < MCS_TABLE_SIZE);
684 min(scb_ampdu->max_rxlen,
685 ampdu->max_txlen[mcs][is40][sgi]);
687 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
690 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
694 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
695 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
697 /* rebuild the rspec and rspec_fallback */
698 rspec = RSPEC_MIMORATE;
699 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
700 if (plcp[0] & MIMO_PLCP_40MHZ)
701 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
703 if (fbr_iscck) /* CCK */
705 CCK_RSPEC(CCK_PHY2MAC_RATE
706 (txh->FragPLCPFallback[0]));
708 rspec_fallback = RSPEC_MIMORATE;
710 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
711 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
713 (PHY_TXC1_BW_40MHZ <<
717 if (use_rts || use_cts) {
719 wlc_rspec_to_rts_rspec(wlc, rspec, false,
722 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
723 false, mimo_ctlchbw);
727 /* if (first mpdu for host agg) */
728 /* test whether to add more */
729 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
730 (count == f->mcs2ampdu_table[mcs])) {
731 WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc->pub->unit, count, mcs));
735 if (count == scb_ampdu->max_pdu) {
736 WL_NONE(("Stop taking from q, reached %d deep\n",
737 scb_ampdu->max_pdu));
741 /* check to see if the next pkt is a candidate for aggregation */
742 p = pktq_ppeek(&qi->q, prec);
743 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
746 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
747 ((u8) (p->priority) == tid)) {
750 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
751 plen = max(scb_ampdu->min_len, plen);
753 if ((plen + ampdu_len) > maxlen) {
755 WL_ERROR(("%s: Bogus plen #1\n",
761 /* check if there are enough descriptors available */
762 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
763 WL_ERROR(("%s: No fifo space !!!!!!\n", __func__));
767 p = pktq_pdeq(&qi->q, prec);
775 ini->tx_in_transit += count;
778 WLCNTADD(ampdu->cnt->txmpdu, count);
780 /* patch up the last txh */
781 txh = (d11txh_t *) pkt[count - 1]->data;
782 mcl = ltoh16(txh->MacTxControlLow);
783 mcl &= ~TXC_AMPDU_MASK;
784 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
785 txh->MacTxControlLow = htol16(mcl);
787 /* remove the null delimiter after last mpdu */
788 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
789 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
790 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
792 /* remove the pad len from last mpdu */
793 fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
794 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
795 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
796 ampdu_len -= roundup(len, 4) - len;
798 /* patch up the first txh & plcp */
799 txh = (d11txh_t *) pkt[0]->data;
800 plcp = (u8 *) (txh + 1);
802 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
803 /* mark plcp to indicate ampdu */
804 WLC_SET_MIMO_PLCP_AMPDU(plcp);
806 /* reset the mixed mode header durations */
809 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
810 txh->MModeLen = htol16(mmodelen);
811 preamble_type = WLC_MM_PREAMBLE;
813 if (txh->MModeFbrLen) {
815 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
816 txh->MModeFbrLen = htol16(mmfbrlen);
817 fbr_preamble_type = WLC_MM_PREAMBLE;
820 /* set the preload length */
821 if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
822 dma_len = min(dma_len, f->ampdu_pld_size);
823 txh->PreloadSize = htol16(dma_len);
825 txh->PreloadSize = 0;
827 mch = ltoh16(txh->MacTxControlHigh);
829 /* update RTS dur fields */
830 if (use_rts || use_cts) {
832 rts = (struct dot11_rts_frame *)&txh->rts_frame;
833 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
834 TXC_PREAMBLE_RTS_MAIN_SHORT)
835 rts_preamble_type = WLC_SHORT_PREAMBLE;
837 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
838 TXC_PREAMBLE_RTS_FB_SHORT)
839 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
842 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
843 rspec, rts_preamble_type,
844 preamble_type, ampdu_len,
846 rts->durid = htol16(durid);
847 durid = wlc_compute_rtscts_dur(wlc, use_cts,
850 rts_fbr_preamble_type,
853 txh->RTSDurFallback = htol16(durid);
854 /* set TxFesTimeNormal */
855 txh->TxFesTimeNormal = rts->durid;
856 /* set fallback rate version of TxFesTimeNormal */
857 txh->TxFesTimeFallback = txh->RTSDurFallback;
860 /* set flag and plcp for fallback rate */
862 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
863 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
864 mch |= TXC_AMPDU_FBR;
865 txh->MacTxControlHigh = htol16(mch);
866 WLC_SET_MIMO_PLCP_AMPDU(plcp);
867 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
870 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
871 wlc->pub->unit, count, ampdu_len));
873 /* inform rate_sel if it this is a rate probe pkt */
874 frameid = ltoh16(txh->TxFrameID);
875 if (frameid & TXFID_RATE_PROBE_MASK) {
876 WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__));
878 for (i = 0; i < count; i++)
879 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
880 ampdu->txpkt_weight);
888 wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, struct sk_buff *p,
891 scb_ampdu_t *scb_ampdu;
892 struct wlc_info *wlc = ampdu->wlc;
893 scb_ampdu_tid_ini_t *ini;
895 struct ieee80211_tx_info *tx_info;
897 tx_info = IEEE80211_SKB_CB(p);
898 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
900 ASSERT(scb->magic == SCB_MAGIC);
901 ASSERT(txs->status & TX_STATUS_AMPDU);
902 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
904 ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
905 ASSERT(ini->scb == scb);
907 /* BMAC_NOTE: For the split driver, second level txstatus comes later
908 * So if the ACK was received then wait for the second level else just
911 if (txs->status & TX_STATUS_ACK_RCV) {
914 /* wait till the next 8 bytes of txstatus is available */
917 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
920 if (status_delay > 10) {
921 ASSERT(status_delay <= 10);
926 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
927 ASSERT(s1 & TX_STATUS_AMPDU);
928 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
931 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
932 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
936 rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
937 tx_status_t *txs, u8 mcs)
939 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
942 /* clear the rest of the rates */
943 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
949 #define SHORTNAME "AMPDU status"
951 static void BCMFASTPATH
952 wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
953 struct sk_buff *p, tx_status_t *txs,
956 scb_ampdu_t *scb_ampdu;
957 struct wlc_info *wlc = ampdu->wlc;
958 scb_ampdu_tid_ini_t *ini;
959 u8 bitmap[8], queue, tid;
962 struct dot11_header *h;
963 u16 seq, start_seq = 0, bindex, index, mcl;
965 bool ba_recd = false, ack_recd = false;
966 u8 suc_mpdu = 0, tot_mpdu = 0;
968 bool update_rate = true, retry = true, tx_error = false;
971 u8 retry_limit, rr_retry_limit;
972 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
975 u8 hole[AMPDU_MAX_MPDU];
976 memset(hole, 0, sizeof(hole));
979 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
980 ASSERT(txs->status & TX_STATUS_AMPDU);
982 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
985 tid = (u8) (p->priority);
987 ini = SCB_AMPDU_INI(scb_ampdu, tid);
988 retry_limit = ampdu->retry_limit_tid[tid];
989 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
991 ASSERT(ini->scb == scb);
993 memset(bitmap, 0, sizeof(bitmap));
994 queue = txs->frameid & TXFID_QUEUE_MASK;
995 ASSERT(queue < AC_COUNT);
997 supr_status = txs->status & TX_STATUS_SUPR_MASK;
999 if (txs->status & TX_STATUS_ACK_RCV) {
1000 if (TX_STATUS_SUPR_UF == supr_status) {
1001 update_rate = false;
1004 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1005 start_seq = txs->sequence >> SEQNUM_SHIFT;
1006 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1007 TX_STATUS_BA_BMAP03_SHIFT;
1009 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1010 ASSERT(s1 & TX_STATUS_AMPDU);
1013 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1014 TX_STATUS_BA_BMAP47_SHIFT;
1015 bitmap[1] = (s1 >> 8) & 0xff;
1016 bitmap[2] = (s1 >> 16) & 0xff;
1017 bitmap[3] = (s1 >> 24) & 0xff;
1019 bitmap[4] = s2 & 0xff;
1020 bitmap[5] = (s2 >> 8) & 0xff;
1021 bitmap[6] = (s2 >> 16) & 0xff;
1022 bitmap[7] = (s2 >> 24) & 0xff;
1026 WLCNTINCR(ampdu->cnt->noba);
1028 update_rate = false;
1029 if (supr_status == TX_STATUS_SUPR_BADCH) {
1030 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
1032 if (supr_status == TX_STATUS_SUPR_FRAG)
1033 WL_NONE(("%s: AMPDU frag err\n",
1036 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
1038 /* no need to retry for badch; will fail again */
1039 if (supr_status == TX_STATUS_SUPR_BADCH ||
1040 supr_status == TX_STATUS_SUPR_EXPTIME) {
1042 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1043 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1045 WLCNTINCR(wlc->pub->_cnt->txexptime);
1047 /* TX underflow : try tuning pre-loading or ampdu size */
1048 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1049 /* if there were underflows, but pre-loading is not active,
1050 notify rate adaptation.
1052 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1057 } else if (txs->phyerr) {
1058 update_rate = false;
1059 WLCNTINCR(wlc->pub->_cnt->txphyerr);
1060 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc->pub->unit, txs->phyerr));
1063 if (WL_ERROR_ON()) {
1064 prpkt("txpkt (AMPDU)", wlc->osh, p);
1065 wlc_print_txdesc((d11txh_t *) p->data);
1066 wlc_print_txstatus(txs);
1072 /* loop through all pkts and retry if not acked */
1074 tx_info = IEEE80211_SKB_CB(p);
1075 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1076 txh = (d11txh_t *) p->data;
1077 mcl = ltoh16(txh->MacTxControlLow);
1078 plcp = (u8 *) (txh + 1);
1079 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1080 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1082 if (tot_mpdu == 0) {
1083 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1084 mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1087 index = TX_SEQ_TO_INDEX(seq);
1090 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1092 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1093 "bindex is %d set %d, index %d\n",
1094 __func__, tid, seq, start_seq, bindex,
1095 isset(bitmap, bindex), index));
1097 /* if acked then clear bit and free packet */
1098 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1099 && isset(bitmap, bindex)) {
1100 ini->tx_in_transit--;
1101 ini->txretry[index] = 0;
1103 /* ampdu_ack_len: number of acked aggregated frames */
1104 /* ampdu_ack_map: block ack bit map for the aggregation */
1105 /* ampdu_len: number of aggregated frames */
1106 rate_status(wlc, tx_info, txs, mcs);
1107 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1108 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1110 /* XXX TODO: Make these accurate. */
1111 tx_info->status.ampdu_ack_len =
1113 status & TX_STATUS_FRM_RTX_MASK) >>
1114 TX_STATUS_FRM_RTX_SHIFT;
1115 tx_info->status.ampdu_len =
1117 status & TX_STATUS_FRM_RTX_MASK) >>
1118 TX_STATUS_FRM_RTX_SHIFT;
1120 skb_pull(p, D11_PHY_HDR_LEN);
1121 skb_pull(p, D11_TXH_LEN);
1123 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1129 /* either retransmit or send bar if ack not recd */
1131 struct ieee80211_tx_rate *txrate =
1132 tx_info->status.rates;
1133 if (retry && (txrate[0].count < (int)retry_limit)) {
1134 ini->txretry[index]++;
1135 ini->tx_in_transit--;
1136 /* Use high prededence for retransmit to give some punch */
1137 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1138 wlc_txq_enq(wlc, scb, p,
1139 WLC_PRIO_TO_HI_PREC(tid));
1142 ini->tx_in_transit--;
1143 ieee80211_tx_info_clear_status(tx_info);
1145 IEEE80211_TX_STAT_AMPDU_NO_BACK;
1146 skb_pull(p, D11_PHY_HDR_LEN);
1147 skb_pull(p, D11_TXH_LEN);
1148 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME, seq, ini->tx_in_transit));
1149 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1155 /* break out if last packet of ampdu */
1156 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1160 p = GETNEXTTXP(wlc, queue);
1166 wlc_send_q(wlc, wlc->active_queue);
1168 /* update rate state */
1169 if (WLANTSEL_ENAB(wlc))
1170 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1172 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1176 ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
1179 scb_ampdu_tid_ini_t *ini;
1180 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1184 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1185 ampdu->wlc->pub->unit, tid));
1187 if (ini->tx_in_transit && !force)
1190 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1191 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1193 /* free all buffered tx packets */
1194 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
1197 /* initialize the initiator code for tid */
1198 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
1199 scb_ampdu_t *scb_ampdu,
1200 u8 tid, bool override)
1202 scb_ampdu_tid_ini_t *ini;
1205 ASSERT(scb_ampdu->scb);
1206 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1207 ASSERT(tid < AMPDU_MAX_SCB_TID);
1209 /* check for per-tid control of ampdu */
1210 if (!ampdu->ini_enable[tid]) {
1211 WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
1215 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1217 ini->scb = scb_ampdu->scb;
1218 ini->magic = INI_MAGIC;
1219 WLCNTINCR(ampdu->cnt->txaddbareq);
1224 int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
1226 struct wlc_info *wlc = ampdu->wlc;
1228 wlc->pub->_ampdu = false;
1231 if (!N_ENAB(wlc->pub)) {
1232 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1234 return BCME_UNSUPPORTED;
1236 if (!wlc_ampdu_cap(ampdu)) {
1237 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1239 return BCME_UNSUPPORTED;
1241 wlc->pub->_ampdu = on;
1247 bool wlc_ampdu_cap(ampdu_info_t *ampdu)
1249 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1255 static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur)
1259 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1260 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1262 rate = MCS_RATE(mcs, false, false);
1263 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1264 /* 40 MHz, No SGI */
1265 rate = MCS_RATE(mcs, true, false);
1266 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1268 rate = MCS_RATE(mcs, false, true);
1269 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1271 rate = MCS_RATE(mcs, true, true);
1272 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1277 wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
1278 ratespec_t rspec, int phylen)
1280 scb_ampdu_t *scb_ampdu;
1281 int bytes, cnt, tmp;
1285 ASSERT(SCB_AMPDU(scb));
1287 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1290 if (scb_ampdu->mpdu_density == 0)
1293 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1294 density x is in 2^(x-4) usec
1295 ==> # of bytes needed for req density = rate/2^(17-x)
1296 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1299 tx_density = scb_ampdu->mpdu_density;
1301 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1302 tmp = 1 << (17 - tx_density);
1303 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1305 if (bytes > phylen) {
1306 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1313 void wlc_ampdu_macaddr_upd(struct wlc_info *wlc)
1315 char template[T_RAM_ACCESS_SZ * 2];
1317 /* driver needs to write the ta in the template; ta is at offset 16 */
1318 memset(template, 0, sizeof(template));
1319 bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETHER_ADDR_LEN);
1320 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1324 bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
1326 return wlc->ampdu->ini_enable[tid];
1329 void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
1331 struct wlc_info *wlc = ampdu->wlc;
1333 /* Extend ucode internal watchdog timer to match larger received frames */
1334 if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1335 AMPDU_RX_FACTOR_64K) {
1336 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1337 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1339 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1340 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);