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 (FCS_LEN + DOT11_ICV_AES_LEN +\
71 AMPDU_DELIMITER_LEN + 3\
72 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
81 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
84 /* structure to hold tx fifo information and pre-loading state
85 * counters specific to tx underflows of ampdus
86 * some counters might be redundant with the ones in wlc or ampdu structures.
87 * This allows to maintain a specific state independantly of
88 * how often and/or when the wlc counters are updated.
90 typedef struct wlc_fifo_info {
91 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
92 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
93 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
94 u32 accum_txfunfl; /* num of underflows since we modified pld params */
95 u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
96 u32 prev_txampdu; /* previous reading of tx ampdu */
97 u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
100 /* AMPDU module specific state */
102 struct wlc_info *wlc; /* pointer to main wlc structure */
103 int scb_handle; /* scb cubby handle to retrieve data from scb */
104 u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
105 u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
106 u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
107 u8 retry_limit; /* mpdu transmit retry limit */
108 u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
109 u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
110 /* per-tid mpdu transmit retry limit at regular rate */
111 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
112 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
113 s8 max_pdu; /* max pdus allowed in ampdu */
114 u8 dur; /* max duration of an ampdu (in msec) */
115 u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
116 u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
117 u32 ffpld_rsvd; /* number of bytes to reserve for preload */
118 u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
119 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
120 bool mfbr; /* enable multiple fallback rate */
121 u32 tx_max_funl; /* underflows should be kept such that
122 * (tx_max_funfl*underflows) < tx frames
124 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
128 #define AMPDU_CLEANUPFLAG_RX (0x1)
129 #define AMPDU_CLEANUPFLAG_TX (0x2)
131 #define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
132 #define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
134 static void wlc_ffpld_init(struct ampdu_info *ampdu);
135 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
136 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
138 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
139 scb_ampdu_t *scb_ampdu,
140 u8 tid, bool override);
141 static void ampdu_cleanup_tid_ini(struct ampdu_info *ampdu,
142 scb_ampdu_t *scb_ampdu,
144 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
145 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
146 static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
148 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
150 static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
152 struct sk_buff *p, tx_status_t *txs,
153 u32 frmtxstatus, u32 frmtxstatus2);
155 static inline u16 pkt_txh_seqnum(struct wlc_info *wlc, struct sk_buff *p)
158 struct ieee80211_hdr *h;
159 txh = (d11txh_t *) p->data;
160 h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
161 return ltoh16(h->seq_ctrl) >> SEQNUM_SHIFT;
164 struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
166 struct ampdu_info *ampdu;
169 /* some code depends on packed structures */
170 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
171 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
172 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
173 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
174 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
176 ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
178 WL_ERROR("wl%d: wlc_ampdu_attach: out of mem\n",
184 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
185 ampdu->ini_enable[i] = true;
186 /* Disable ampdu for VO by default */
187 ampdu->ini_enable[PRIO_8021D_VO] = false;
188 ampdu->ini_enable[PRIO_8021D_NC] = false;
190 /* Disable ampdu for BK by default since not enough fifo space */
191 ampdu->ini_enable[PRIO_8021D_NONE] = false;
192 ampdu->ini_enable[PRIO_8021D_BK] = false;
194 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
195 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
196 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
197 ampdu->max_pdu = AUTO;
198 ampdu->dur = AMPDU_MAX_DUR;
199 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
201 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
202 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
203 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
204 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
206 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
207 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
208 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
210 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
211 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
212 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
215 ampdu_update_max_txlen(ampdu, ampdu->dur);
217 /* try to set ampdu to the default value */
218 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
220 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
221 wlc_ffpld_init(ampdu);
226 void wlc_ampdu_detach(struct ampdu_info *ampdu)
233 /* free all ini's which were to be freed on callbacks which were never called */
234 for (i = 0; i < AMPDU_INI_FREE; i++) {
235 if (ampdu->ini_free[i]) {
236 kfree(ampdu->ini_free[i]);
240 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
244 void scb_ampdu_cleanup(struct ampdu_info *ampdu, struct scb *scb)
246 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
249 WL_AMPDU_UPDN("scb_ampdu_cleanup: enter\n");
252 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
253 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
257 /* reset the ampdu state machine so that it can gracefully handle packets that were
258 * freed from the dma and tx queues during reinit
260 void wlc_ampdu_reset(struct ampdu_info *ampdu)
262 WL_NONE("%s: Entering\n", __func__);
265 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb)
267 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
270 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
272 /* go back to legacy size if some preloading is occuring */
273 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
274 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
275 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
278 /* apply user override */
279 if (ampdu->max_pdu != AUTO)
280 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
282 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
284 if (scb_ampdu->max_rxlen)
286 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
288 scb_ampdu->release = min(scb_ampdu->release,
289 ampdu->fifo_tb[TX_AC_BE_FIFO].
290 mcs2ampdu_table[FFPLD_MAX_MCS]);
292 ASSERT(scb_ampdu->release);
295 void scb_ampdu_update_config_all(struct ampdu_info *ampdu)
297 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
300 static void wlc_ffpld_init(struct ampdu_info *ampdu)
303 wlc_fifo_info_t *fifo;
305 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
306 fifo = (ampdu->fifo_tb + j);
307 fifo->ampdu_pld_size = 0;
308 for (i = 0; i <= FFPLD_MAX_MCS; i++)
309 fifo->mcs2ampdu_table[i] = 255;
310 fifo->dmaxferrate = 0;
311 fifo->accum_txampdu = 0;
312 fifo->prev_txfunfl = 0;
313 fifo->accum_txfunfl = 0;
318 /* evaluate the dma transfer rate using the tx underflows as feedback.
319 * If necessary, increase tx fifo preloading. If not enough,
320 * decrease maximum ampdu size for each mcs till underflows stop
321 * Return 1 if pre-loading not active, -1 if not an underflow event,
322 * 0 if pre-loading module took care of the event.
324 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
326 struct ampdu_info *ampdu = wlc->ampdu;
327 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
330 u32 current_ampdu_cnt = 0;
333 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
337 /* return if we got here for a different reason than underflows */
340 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
341 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
342 if (new_txunfl == 0) {
343 WL_FFPLD("check_txunfl : TX status FRAG set but no tx underflows\n");
346 fifo->prev_txfunfl = cur_txunfl;
348 if (!ampdu->tx_max_funl)
351 /* check if fifo is big enough */
352 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
353 WL_FFPLD("check_txunfl : get xmtfifo_sz failed\n");
357 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
360 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
361 fifo->accum_txfunfl += new_txunfl;
363 /* we need to wait for at least 10 underflows */
364 if (fifo->accum_txfunfl < 10)
367 WL_FFPLD("ampdu_count %d tx_underflows %d\n",
368 current_ampdu_cnt, fifo->accum_txfunfl);
371 compute the current ratio of tx unfl per ampdu.
372 When the current ampdu count becomes too
373 big while the ratio remains small, we reset
374 the current count in order to not
375 introduce too big of a latency in detecting a
376 large amount of tx underflows later.
379 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
381 if (txunfl_ratio > ampdu->tx_max_funl) {
382 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
383 fifo->accum_txfunfl = 0;
388 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
390 /* In case max value max_pdu is already lower than
391 the fifo depth, there is nothing more we can do.
394 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
395 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
396 fifo->accum_txfunfl = 0;
400 if (fifo->ampdu_pld_size < max_pld_size) {
402 /* increment by TX_FIFO_PLD_INC bytes */
403 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
404 if (fifo->ampdu_pld_size > max_pld_size)
405 fifo->ampdu_pld_size = max_pld_size;
407 /* update scb release size */
408 scb_ampdu_update_config_all(ampdu);
411 compute a new dma xfer rate for max_mpdu @ max mcs.
412 This is the minimum dma rate that
413 can acheive no unferflow condition for the current mpdu size.
415 /* note : we divide/multiply by 100 to avoid integer overflows */
418 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
419 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
421 WL_FFPLD("DMA estimated transfer rate %d; pre-load size %d\n",
422 fifo->dmaxferrate, fifo->ampdu_pld_size);
425 /* decrease ampdu size */
426 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
427 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
428 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
429 AMPDU_NUM_MPDU_LEGACY - 1;
431 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
433 /* recompute the table */
434 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
436 /* update scb release size */
437 scb_ampdu_update_config_all(ampdu);
440 fifo->accum_txfunfl = 0;
444 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
447 u32 phy_rate, dma_rate, tmp;
449 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
451 /* recompute the dma rate */
452 /* note : we divide/multiply by 100 to avoid integer overflows */
454 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
455 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
458 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
459 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
460 fifo->dmaxferrate = dma_rate;
462 /* fill up the mcs2ampdu table; do not recalc the last mcs */
463 dma_rate = dma_rate >> 7;
464 for (i = 0; i < FFPLD_MAX_MCS; i++) {
465 /* shifting to keep it within integer range */
466 phy_rate = MCS_RATE(i, true, false) >> 7;
467 if (phy_rate > dma_rate) {
468 tmp = ((fifo->ampdu_pld_size * phy_rate) /
469 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
470 tmp = min_t(u32, tmp, 255);
471 fifo->mcs2ampdu_table[i] = (u8) tmp;
476 static void BCMFASTPATH
477 wlc_ampdu_agg(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
480 scb_ampdu_t *scb_ampdu;
481 scb_ampdu_tid_ini_t *ini;
482 u8 tid = (u8) (p->priority);
484 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
486 /* initialize initiator on first packet; sends addba req */
487 ini = SCB_AMPDU_INI(scb_ampdu, tid);
488 if (ini->magic != INI_MAGIC) {
489 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
495 wlc_sendampdu(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
496 struct sk_buff **pdu, int prec)
498 struct wlc_info *wlc;
499 struct osl_info *osh;
500 struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
503 u8 preamble_type = WLC_GF_PREAMBLE;
504 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
505 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
506 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
508 bool rr = true, fbr = false;
509 uint i, count = 0, fifo, seg_cnt = 0;
510 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
511 u32 ampdu_len, maxlen = 0;
512 d11txh_t *txh = NULL;
514 struct ieee80211_hdr *h;
516 scb_ampdu_t *scb_ampdu;
517 scb_ampdu_tid_ini_t *ini;
519 bool use_rts = false, use_cts = false;
520 ratespec_t rspec = 0, rspec_fallback = 0;
521 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
522 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
523 struct ieee80211_rts *rts;
527 struct ieee80211_tx_info *tx_info;
536 tid = (u8) (p->priority);
537 ASSERT(tid < AMPDU_MAX_SCB_TID);
539 f = ampdu->fifo_tb + prio2fifo[tid];
541 scb = wlc->pub->global_scb;
542 ASSERT(scb->magic == SCB_MAGIC);
544 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
546 ini = &scb_ampdu->ini[tid];
548 /* Let pressure continue to build ... */
549 qlen = pktq_plen(&qi->q, prec);
550 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
554 wlc_ampdu_agg(ampdu, scb, p, tid);
556 if (wlc->block_datafifo) {
557 WL_ERROR("%s: Fifo blocked\n", __func__);
560 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
564 struct ieee80211_tx_rate *txrate;
566 tx_info = IEEE80211_SKB_CB(p);
567 txrate = tx_info->status.rates;
569 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
570 err = wlc_prep_pdu(wlc, p, &fifo);
572 WL_ERROR("%s: AMPDU flag is off!\n", __func__);
579 if (err == BCME_BUSY) {
580 WL_ERROR("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n",
581 wlc->pub->unit, seq);
582 WLCNTINCR(ampdu->cnt->sduretry);
587 /* error in the packet; reject it */
588 WL_AMPDU_ERR("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n",
589 wlc->pub->unit, seq);
590 WLCNTINCR(ampdu->cnt->sdurejected);
596 /* pkt is good to be aggregated */
597 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
598 txh = (d11txh_t *) p->data;
599 plcp = (u8 *) (txh + 1);
600 h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
601 seq = ltoh16(h->seq_ctrl) >> SEQNUM_SHIFT;
602 index = TX_SEQ_TO_INDEX(seq);
604 /* check mcl fields and test whether it can be agg'd */
605 mcl = ltoh16(txh->MacTxControlLow);
606 mcl &= ~TXC_AMPDU_MASK;
607 fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
609 txh->PreloadSize = 0; /* always default to 0 */
611 /* Handle retry limits */
612 if (txrate[0].count <= rr_retry_limit) {
623 /* extract the length info */
624 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
625 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
627 /* retrieve null delimiter count */
628 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
631 WL_AMPDU_TX("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
632 wlc->pub->unit, count, len);
635 * aggregateable mpdu. For ucode/hw agg,
636 * test whether need to break or change the epoch
640 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
641 /* refill the bits since might be a retx mpdu */
642 mcl |= TXC_STARTMSDU;
643 rts = (struct ieee80211_rts *)&txh->rts_frame;
644 fc = ltoh16(rts->frame_control);
645 if ((fc & FC_KIND_MASK) == FC_RTS) {
649 if ((fc & FC_KIND_MASK) == FC_CTS) {
654 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
655 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
658 len = roundup(len, 4);
659 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
661 dma_len += (u16) pkttotlen(osh, p);
663 WL_AMPDU_TX("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n",
664 wlc->pub->unit, ampdu_len, seg_cnt, ndelim);
666 txh->MacTxControlLow = htol16(mcl);
668 /* this packet is added */
671 /* patch the first MPDU */
673 u8 plcp0, plcp3, is40, sgi;
674 struct ieee80211_sta *sta;
676 sta = tx_info->control.sta;
682 plcp0 = txh->FragPLCPFallback[0];
683 plcp3 = txh->FragPLCPFallback[3];
686 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
687 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
688 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
689 ASSERT(mcs < MCS_TABLE_SIZE);
691 min(scb_ampdu->max_rxlen,
692 ampdu->max_txlen[mcs][is40][sgi]);
694 WL_NONE("sendampdu: sgi %d, is40 %d, mcs %d\n",
697 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
701 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
702 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
704 /* rebuild the rspec and rspec_fallback */
705 rspec = RSPEC_MIMORATE;
706 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
707 if (plcp[0] & MIMO_PLCP_40MHZ)
708 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
710 if (fbr_iscck) /* CCK */
712 CCK_RSPEC(CCK_PHY2MAC_RATE
713 (txh->FragPLCPFallback[0]));
715 rspec_fallback = RSPEC_MIMORATE;
717 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
718 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
720 (PHY_TXC1_BW_40MHZ <<
724 if (use_rts || use_cts) {
726 wlc_rspec_to_rts_rspec(wlc, rspec, false,
729 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
730 false, mimo_ctlchbw);
734 /* if (first mpdu for host agg) */
735 /* test whether to add more */
736 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
737 (count == f->mcs2ampdu_table[mcs])) {
738 WL_AMPDU_ERR("wl%d: PR 37644: stopping ampdu at %d for mcs %d\n",
739 wlc->pub->unit, count, mcs);
743 if (count == scb_ampdu->max_pdu) {
744 WL_NONE("Stop taking from q, reached %d deep\n",
749 /* check to see if the next pkt is a candidate for aggregation */
750 p = pktq_ppeek(&qi->q, prec);
751 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
754 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
755 ((u8) (p->priority) == tid)) {
758 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
759 plen = max(scb_ampdu->min_len, plen);
761 if ((plen + ampdu_len) > maxlen) {
763 WL_ERROR("%s: Bogus plen #1\n",
769 /* check if there are enough descriptors available */
770 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
771 WL_ERROR("%s: No fifo space !!!!!!\n",
776 p = pktq_pdeq(&qi->q, prec);
784 ini->tx_in_transit += count;
787 WLCNTADD(ampdu->cnt->txmpdu, count);
789 /* patch up the last txh */
790 txh = (d11txh_t *) pkt[count - 1]->data;
791 mcl = ltoh16(txh->MacTxControlLow);
792 mcl &= ~TXC_AMPDU_MASK;
793 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
794 txh->MacTxControlLow = htol16(mcl);
796 /* remove the null delimiter after last mpdu */
797 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
798 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
799 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
801 /* remove the pad len from last mpdu */
802 fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
803 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
804 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
805 ampdu_len -= roundup(len, 4) - len;
807 /* patch up the first txh & plcp */
808 txh = (d11txh_t *) pkt[0]->data;
809 plcp = (u8 *) (txh + 1);
811 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
812 /* mark plcp to indicate ampdu */
813 WLC_SET_MIMO_PLCP_AMPDU(plcp);
815 /* reset the mixed mode header durations */
818 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
819 txh->MModeLen = htol16(mmodelen);
820 preamble_type = WLC_MM_PREAMBLE;
822 if (txh->MModeFbrLen) {
824 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
825 txh->MModeFbrLen = htol16(mmfbrlen);
826 fbr_preamble_type = WLC_MM_PREAMBLE;
829 /* set the preload length */
830 if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
831 dma_len = min(dma_len, f->ampdu_pld_size);
832 txh->PreloadSize = htol16(dma_len);
834 txh->PreloadSize = 0;
836 mch = ltoh16(txh->MacTxControlHigh);
838 /* update RTS dur fields */
839 if (use_rts || use_cts) {
841 rts = (struct ieee80211_rts *)&txh->rts_frame;
842 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
843 TXC_PREAMBLE_RTS_MAIN_SHORT)
844 rts_preamble_type = WLC_SHORT_PREAMBLE;
846 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
847 TXC_PREAMBLE_RTS_FB_SHORT)
848 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
851 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
852 rspec, rts_preamble_type,
853 preamble_type, ampdu_len,
855 rts->duration = htol16(durid);
856 durid = wlc_compute_rtscts_dur(wlc, use_cts,
859 rts_fbr_preamble_type,
862 txh->RTSDurFallback = htol16(durid);
863 /* set TxFesTimeNormal */
864 txh->TxFesTimeNormal = rts->duration;
865 /* set fallback rate version of TxFesTimeNormal */
866 txh->TxFesTimeFallback = txh->RTSDurFallback;
869 /* set flag and plcp for fallback rate */
871 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
872 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
873 mch |= TXC_AMPDU_FBR;
874 txh->MacTxControlHigh = htol16(mch);
875 WLC_SET_MIMO_PLCP_AMPDU(plcp);
876 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
879 WL_AMPDU_TX("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
880 wlc->pub->unit, count, ampdu_len);
882 /* inform rate_sel if it this is a rate probe pkt */
883 frameid = ltoh16(txh->TxFrameID);
884 if (frameid & TXFID_RATE_PROBE_MASK) {
885 WL_ERROR("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n",
888 for (i = 0; i < count; i++)
889 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
890 ampdu->txpkt_weight);
898 wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
899 struct sk_buff *p, tx_status_t *txs)
901 scb_ampdu_t *scb_ampdu;
902 struct wlc_info *wlc = ampdu->wlc;
903 scb_ampdu_tid_ini_t *ini;
905 struct ieee80211_tx_info *tx_info;
907 tx_info = IEEE80211_SKB_CB(p);
908 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
910 ASSERT(scb->magic == SCB_MAGIC);
911 ASSERT(txs->status & TX_STATUS_AMPDU);
912 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
914 ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
915 ASSERT(ini->scb == scb);
917 /* BMAC_NOTE: For the split driver, second level txstatus comes later
918 * So if the ACK was received then wait for the second level else just
921 if (txs->status & TX_STATUS_ACK_RCV) {
924 /* wait till the next 8 bytes of txstatus is available */
927 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
930 if (status_delay > 10) {
931 ASSERT(status_delay <= 10);
936 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
937 ASSERT(s1 & TX_STATUS_AMPDU);
938 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
941 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
942 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
946 rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
947 tx_status_t *txs, u8 mcs)
949 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
952 /* clear the rest of the rates */
953 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
959 #define SHORTNAME "AMPDU status"
961 static void BCMFASTPATH
962 wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
963 struct sk_buff *p, tx_status_t *txs,
966 scb_ampdu_t *scb_ampdu;
967 struct wlc_info *wlc = ampdu->wlc;
968 scb_ampdu_tid_ini_t *ini;
969 u8 bitmap[8], queue, tid;
972 struct ieee80211_hdr *h;
973 u16 seq, start_seq = 0, bindex, index, mcl;
975 bool ba_recd = false, ack_recd = false;
976 u8 suc_mpdu = 0, tot_mpdu = 0;
978 bool update_rate = true, retry = true, tx_error = false;
981 u8 retry_limit, rr_retry_limit;
982 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
985 u8 hole[AMPDU_MAX_MPDU];
986 memset(hole, 0, sizeof(hole));
989 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
990 ASSERT(txs->status & TX_STATUS_AMPDU);
992 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
995 tid = (u8) (p->priority);
997 ini = SCB_AMPDU_INI(scb_ampdu, tid);
998 retry_limit = ampdu->retry_limit_tid[tid];
999 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
1001 ASSERT(ini->scb == scb);
1003 memset(bitmap, 0, sizeof(bitmap));
1004 queue = txs->frameid & TXFID_QUEUE_MASK;
1005 ASSERT(queue < AC_COUNT);
1007 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1009 if (txs->status & TX_STATUS_ACK_RCV) {
1010 if (TX_STATUS_SUPR_UF == supr_status) {
1011 update_rate = false;
1014 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1015 start_seq = txs->sequence >> SEQNUM_SHIFT;
1016 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1017 TX_STATUS_BA_BMAP03_SHIFT;
1019 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1020 ASSERT(s1 & TX_STATUS_AMPDU);
1023 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1024 TX_STATUS_BA_BMAP47_SHIFT;
1025 bitmap[1] = (s1 >> 8) & 0xff;
1026 bitmap[2] = (s1 >> 16) & 0xff;
1027 bitmap[3] = (s1 >> 24) & 0xff;
1029 bitmap[4] = s2 & 0xff;
1030 bitmap[5] = (s2 >> 8) & 0xff;
1031 bitmap[6] = (s2 >> 16) & 0xff;
1032 bitmap[7] = (s2 >> 24) & 0xff;
1036 WLCNTINCR(ampdu->cnt->noba);
1038 update_rate = false;
1039 if (supr_status == TX_STATUS_SUPR_BADCH) {
1040 WL_ERROR("%s: Pkt tx suppressed, illegal channel possibly %d\n",
1042 CHSPEC_CHANNEL(wlc->default_bss->chanspec));
1044 if (supr_status == TX_STATUS_SUPR_FRAG)
1045 WL_NONE("%s: AMPDU frag err\n",
1048 WL_ERROR("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n",
1049 __func__, supr_status);
1051 /* no need to retry for badch; will fail again */
1052 if (supr_status == TX_STATUS_SUPR_BADCH ||
1053 supr_status == TX_STATUS_SUPR_EXPTIME) {
1055 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1056 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1058 WLCNTINCR(wlc->pub->_cnt->txexptime);
1060 /* TX underflow : try tuning pre-loading or ampdu size */
1061 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1062 /* if there were underflows, but pre-loading is not active,
1063 notify rate adaptation.
1065 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1070 } else if (txs->phyerr) {
1071 update_rate = false;
1072 WLCNTINCR(wlc->pub->_cnt->txphyerr);
1073 WL_ERROR("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n",
1074 wlc->pub->unit, txs->phyerr);
1077 if (WL_ERROR_ON()) {
1078 prpkt("txpkt (AMPDU)", wlc->osh, p);
1079 wlc_print_txdesc((d11txh_t *) p->data);
1080 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 = ltoh16(txh->MacTxControlLow);
1092 plcp = (u8 *) (txh + 1);
1093 h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
1094 seq = ltoh16(h->seq_ctrl) >> SEQNUM_SHIFT;
1096 if (tot_mpdu == 0) {
1097 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1098 mimoantsel = ltoh16(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 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 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 bcopy((char *)wlc->pub->cur_etheraddr, template, 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);