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(struct ampdu_info *ampdu);
134 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
135 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
137 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
138 scb_ampdu_t *scb_ampdu,
139 u8 tid, bool override);
140 static void ampdu_cleanup_tid_ini(struct ampdu_info *ampdu,
141 scb_ampdu_t *scb_ampdu,
143 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
144 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
145 static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
147 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
149 static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
151 struct sk_buff *p, tx_status_t *txs,
152 u32 frmtxstatus, u32 frmtxstatus2);
154 static inline u16 pkt_txh_seqnum(struct wlc_info *wlc, struct sk_buff *p)
157 struct dot11_header *h;
158 txh = (d11txh_t *) p->data;
159 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
160 return ltoh16(h->seq) >> SEQNUM_SHIFT;
163 struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
165 struct ampdu_info *ampdu;
168 /* some code depends on packed structures */
169 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
170 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
171 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
172 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
173 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
175 ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
177 WL_ERROR("wl%d: wlc_ampdu_attach: out of mem\n",
183 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
184 ampdu->ini_enable[i] = true;
185 /* Disable ampdu for VO by default */
186 ampdu->ini_enable[PRIO_8021D_VO] = false;
187 ampdu->ini_enable[PRIO_8021D_NC] = false;
189 /* Disable ampdu for BK by default since not enough fifo space */
190 ampdu->ini_enable[PRIO_8021D_NONE] = false;
191 ampdu->ini_enable[PRIO_8021D_BK] = false;
193 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
194 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
195 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
196 ampdu->max_pdu = AUTO;
197 ampdu->dur = AMPDU_MAX_DUR;
198 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
200 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
201 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
202 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
203 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
205 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
206 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
207 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
209 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
210 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
211 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
214 ampdu_update_max_txlen(ampdu, ampdu->dur);
216 /* try to set ampdu to the default value */
217 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
219 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
220 wlc_ffpld_init(ampdu);
225 void wlc_ampdu_detach(struct ampdu_info *ampdu)
232 /* free all ini's which were to be freed on callbacks which were never called */
233 for (i = 0; i < AMPDU_INI_FREE; i++) {
234 if (ampdu->ini_free[i]) {
235 kfree(ampdu->ini_free[i]);
239 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
243 void scb_ampdu_cleanup(struct ampdu_info *ampdu, struct scb *scb)
245 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
248 WL_AMPDU_UPDN("scb_ampdu_cleanup: enter\n");
251 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
252 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
256 /* reset the ampdu state machine so that it can gracefully handle packets that were
257 * freed from the dma and tx queues during reinit
259 void wlc_ampdu_reset(struct ampdu_info *ampdu)
261 WL_NONE("%s: Entering\n", __func__);
264 static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb)
266 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
269 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
271 /* go back to legacy size if some preloading is occuring */
272 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
273 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
274 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
277 /* apply user override */
278 if (ampdu->max_pdu != AUTO)
279 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
281 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
283 if (scb_ampdu->max_rxlen)
285 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
287 scb_ampdu->release = min(scb_ampdu->release,
288 ampdu->fifo_tb[TX_AC_BE_FIFO].
289 mcs2ampdu_table[FFPLD_MAX_MCS]);
291 ASSERT(scb_ampdu->release);
294 void scb_ampdu_update_config_all(struct ampdu_info *ampdu)
296 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
299 static void wlc_ffpld_init(struct ampdu_info *ampdu)
302 wlc_fifo_info_t *fifo;
304 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
305 fifo = (ampdu->fifo_tb + j);
306 fifo->ampdu_pld_size = 0;
307 for (i = 0; i <= FFPLD_MAX_MCS; i++)
308 fifo->mcs2ampdu_table[i] = 255;
309 fifo->dmaxferrate = 0;
310 fifo->accum_txampdu = 0;
311 fifo->prev_txfunfl = 0;
312 fifo->accum_txfunfl = 0;
317 /* evaluate the dma transfer rate using the tx underflows as feedback.
318 * If necessary, increase tx fifo preloading. If not enough,
319 * decrease maximum ampdu size for each mcs till underflows stop
320 * Return 1 if pre-loading not active, -1 if not an underflow event,
321 * 0 if pre-loading module took care of the event.
323 static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int fid)
325 struct ampdu_info *ampdu = wlc->ampdu;
326 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
329 u32 current_ampdu_cnt = 0;
332 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
336 /* return if we got here for a different reason than underflows */
339 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
340 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
341 if (new_txunfl == 0) {
342 WL_FFPLD("check_txunfl : TX status FRAG set but no tx underflows\n");
345 fifo->prev_txfunfl = cur_txunfl;
347 if (!ampdu->tx_max_funl)
350 /* check if fifo is big enough */
351 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
352 WL_FFPLD("check_txunfl : get xmtfifo_sz failed\n");
356 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
359 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
360 fifo->accum_txfunfl += new_txunfl;
362 /* we need to wait for at least 10 underflows */
363 if (fifo->accum_txfunfl < 10)
366 WL_FFPLD("ampdu_count %d tx_underflows %d\n",
367 current_ampdu_cnt, fifo->accum_txfunfl);
370 compute the current ratio of tx unfl per ampdu.
371 When the current ampdu count becomes too
372 big while the ratio remains small, we reset
373 the current count in order to not
374 introduce too big of a latency in detecting a
375 large amount of tx underflows later.
378 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
380 if (txunfl_ratio > ampdu->tx_max_funl) {
381 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
382 fifo->accum_txfunfl = 0;
387 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
389 /* In case max value max_pdu is already lower than
390 the fifo depth, there is nothing more we can do.
393 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
394 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
395 fifo->accum_txfunfl = 0;
399 if (fifo->ampdu_pld_size < max_pld_size) {
401 /* increment by TX_FIFO_PLD_INC bytes */
402 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
403 if (fifo->ampdu_pld_size > max_pld_size)
404 fifo->ampdu_pld_size = max_pld_size;
406 /* update scb release size */
407 scb_ampdu_update_config_all(ampdu);
410 compute a new dma xfer rate for max_mpdu @ max mcs.
411 This is the minimum dma rate that
412 can acheive no unferflow condition for the current mpdu size.
414 /* note : we divide/multiply by 100 to avoid integer overflows */
417 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
418 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
420 WL_FFPLD("DMA estimated transfer rate %d; pre-load size %d\n",
421 fifo->dmaxferrate, fifo->ampdu_pld_size);
424 /* decrease ampdu size */
425 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
426 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
427 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
428 AMPDU_NUM_MPDU_LEGACY - 1;
430 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
432 /* recompute the table */
433 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
435 /* update scb release size */
436 scb_ampdu_update_config_all(ampdu);
439 fifo->accum_txfunfl = 0;
443 static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
446 u32 phy_rate, dma_rate, tmp;
448 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
450 /* recompute the dma rate */
451 /* note : we divide/multiply by 100 to avoid integer overflows */
453 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
454 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
457 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
458 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
459 fifo->dmaxferrate = dma_rate;
461 /* fill up the mcs2ampdu table; do not recalc the last mcs */
462 dma_rate = dma_rate >> 7;
463 for (i = 0; i < FFPLD_MAX_MCS; i++) {
464 /* shifting to keep it within integer range */
465 phy_rate = MCS_RATE(i, true, false) >> 7;
466 if (phy_rate > dma_rate) {
467 tmp = ((fifo->ampdu_pld_size * phy_rate) /
468 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
469 tmp = min_t(u32, tmp, 255);
470 fifo->mcs2ampdu_table[i] = (u8) tmp;
475 static void BCMFASTPATH
476 wlc_ampdu_agg(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
479 scb_ampdu_t *scb_ampdu;
480 scb_ampdu_tid_ini_t *ini;
481 u8 tid = (u8) (p->priority);
483 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
485 /* initialize initiator on first packet; sends addba req */
486 ini = SCB_AMPDU_INI(scb_ampdu, tid);
487 if (ini->magic != INI_MAGIC) {
488 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
494 wlc_sendampdu(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
495 struct sk_buff **pdu, int prec)
497 struct wlc_info *wlc;
498 struct osl_info *osh;
499 struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
502 u8 preamble_type = WLC_GF_PREAMBLE;
503 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
504 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
505 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
507 bool rr = true, fbr = false;
508 uint i, count = 0, fifo, seg_cnt = 0;
509 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
510 u32 ampdu_len, maxlen = 0;
511 d11txh_t *txh = NULL;
513 struct dot11_header *h;
515 scb_ampdu_t *scb_ampdu;
516 scb_ampdu_tid_ini_t *ini;
518 bool use_rts = false, use_cts = false;
519 ratespec_t rspec = 0, rspec_fallback = 0;
520 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
521 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
522 struct dot11_rts_frame *rts;
526 struct ieee80211_tx_info *tx_info;
535 tid = (u8) (p->priority);
536 ASSERT(tid < AMPDU_MAX_SCB_TID);
538 f = ampdu->fifo_tb + prio2fifo[tid];
540 scb = wlc->pub->global_scb;
541 ASSERT(scb->magic == SCB_MAGIC);
543 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
545 ini = &scb_ampdu->ini[tid];
547 /* Let pressure continue to build ... */
548 qlen = pktq_plen(&qi->q, prec);
549 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
553 wlc_ampdu_agg(ampdu, scb, p, tid);
555 if (wlc->block_datafifo) {
556 WL_ERROR("%s: Fifo blocked\n", __func__);
559 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
563 struct ieee80211_tx_rate *txrate;
565 tx_info = IEEE80211_SKB_CB(p);
566 txrate = tx_info->status.rates;
568 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
569 err = wlc_prep_pdu(wlc, p, &fifo);
571 WL_ERROR("%s: AMPDU flag is off!\n", __func__);
578 if (err == BCME_BUSY) {
579 WL_ERROR("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n",
580 wlc->pub->unit, seq);
581 WLCNTINCR(ampdu->cnt->sduretry);
586 /* error in the packet; reject it */
587 WL_AMPDU_ERR("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n",
588 wlc->pub->unit, seq);
589 WLCNTINCR(ampdu->cnt->sdurejected);
595 /* pkt is good to be aggregated */
596 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
597 txh = (d11txh_t *) p->data;
598 plcp = (u8 *) (txh + 1);
599 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
600 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
601 index = TX_SEQ_TO_INDEX(seq);
603 /* check mcl fields and test whether it can be agg'd */
604 mcl = ltoh16(txh->MacTxControlLow);
605 mcl &= ~TXC_AMPDU_MASK;
606 fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
608 txh->PreloadSize = 0; /* always default to 0 */
610 /* Handle retry limits */
611 if (txrate[0].count <= rr_retry_limit) {
622 /* extract the length info */
623 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
624 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
626 /* retrieve null delimiter count */
627 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
630 WL_AMPDU_TX("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
631 wlc->pub->unit, count, len);
634 * aggregateable mpdu. For ucode/hw agg,
635 * test whether need to break or change the epoch
639 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
640 /* refill the bits since might be a retx mpdu */
641 mcl |= TXC_STARTMSDU;
642 rts = (struct dot11_rts_frame *)&txh->rts_frame;
643 fc = ltoh16(rts->fc);
644 if ((fc & FC_KIND_MASK) == FC_RTS) {
648 if ((fc & FC_KIND_MASK) == FC_CTS) {
653 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
654 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
657 len = roundup(len, 4);
658 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
660 dma_len += (u16) pkttotlen(osh, p);
662 WL_AMPDU_TX("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n",
663 wlc->pub->unit, ampdu_len, seg_cnt, ndelim);
665 txh->MacTxControlLow = htol16(mcl);
667 /* this packet is added */
670 /* patch the first MPDU */
672 u8 plcp0, plcp3, is40, sgi;
673 struct ieee80211_sta *sta;
675 sta = tx_info->control.sta;
681 plcp0 = txh->FragPLCPFallback[0];
682 plcp3 = txh->FragPLCPFallback[3];
685 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
686 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
687 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
688 ASSERT(mcs < MCS_TABLE_SIZE);
690 min(scb_ampdu->max_rxlen,
691 ampdu->max_txlen[mcs][is40][sgi]);
693 WL_NONE("sendampdu: sgi %d, is40 %d, mcs %d\n",
696 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
700 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
701 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
703 /* rebuild the rspec and rspec_fallback */
704 rspec = RSPEC_MIMORATE;
705 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
706 if (plcp[0] & MIMO_PLCP_40MHZ)
707 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
709 if (fbr_iscck) /* CCK */
711 CCK_RSPEC(CCK_PHY2MAC_RATE
712 (txh->FragPLCPFallback[0]));
714 rspec_fallback = RSPEC_MIMORATE;
716 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
717 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
719 (PHY_TXC1_BW_40MHZ <<
723 if (use_rts || use_cts) {
725 wlc_rspec_to_rts_rspec(wlc, rspec, false,
728 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
729 false, mimo_ctlchbw);
733 /* if (first mpdu for host agg) */
734 /* test whether to add more */
735 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
736 (count == f->mcs2ampdu_table[mcs])) {
737 WL_AMPDU_ERR("wl%d: PR 37644: stopping ampdu at %d for mcs %d\n",
738 wlc->pub->unit, count, mcs);
742 if (count == scb_ampdu->max_pdu) {
743 WL_NONE("Stop taking from q, reached %d deep\n",
748 /* check to see if the next pkt is a candidate for aggregation */
749 p = pktq_ppeek(&qi->q, prec);
750 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
753 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
754 ((u8) (p->priority) == tid)) {
757 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
758 plen = max(scb_ampdu->min_len, plen);
760 if ((plen + ampdu_len) > maxlen) {
762 WL_ERROR("%s: Bogus plen #1\n",
768 /* check if there are enough descriptors available */
769 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
770 WL_ERROR("%s: No fifo space !!!!!!\n",
775 p = pktq_pdeq(&qi->q, prec);
783 ini->tx_in_transit += count;
786 WLCNTADD(ampdu->cnt->txmpdu, count);
788 /* patch up the last txh */
789 txh = (d11txh_t *) pkt[count - 1]->data;
790 mcl = ltoh16(txh->MacTxControlLow);
791 mcl &= ~TXC_AMPDU_MASK;
792 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
793 txh->MacTxControlLow = htol16(mcl);
795 /* remove the null delimiter after last mpdu */
796 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
797 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
798 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
800 /* remove the pad len from last mpdu */
801 fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
802 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
803 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
804 ampdu_len -= roundup(len, 4) - len;
806 /* patch up the first txh & plcp */
807 txh = (d11txh_t *) pkt[0]->data;
808 plcp = (u8 *) (txh + 1);
810 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
811 /* mark plcp to indicate ampdu */
812 WLC_SET_MIMO_PLCP_AMPDU(plcp);
814 /* reset the mixed mode header durations */
817 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
818 txh->MModeLen = htol16(mmodelen);
819 preamble_type = WLC_MM_PREAMBLE;
821 if (txh->MModeFbrLen) {
823 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
824 txh->MModeFbrLen = htol16(mmfbrlen);
825 fbr_preamble_type = WLC_MM_PREAMBLE;
828 /* set the preload length */
829 if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
830 dma_len = min(dma_len, f->ampdu_pld_size);
831 txh->PreloadSize = htol16(dma_len);
833 txh->PreloadSize = 0;
835 mch = ltoh16(txh->MacTxControlHigh);
837 /* update RTS dur fields */
838 if (use_rts || use_cts) {
840 rts = (struct dot11_rts_frame *)&txh->rts_frame;
841 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
842 TXC_PREAMBLE_RTS_MAIN_SHORT)
843 rts_preamble_type = WLC_SHORT_PREAMBLE;
845 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
846 TXC_PREAMBLE_RTS_FB_SHORT)
847 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
850 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
851 rspec, rts_preamble_type,
852 preamble_type, ampdu_len,
854 rts->durid = htol16(durid);
855 durid = wlc_compute_rtscts_dur(wlc, use_cts,
858 rts_fbr_preamble_type,
861 txh->RTSDurFallback = htol16(durid);
862 /* set TxFesTimeNormal */
863 txh->TxFesTimeNormal = rts->durid;
864 /* set fallback rate version of TxFesTimeNormal */
865 txh->TxFesTimeFallback = txh->RTSDurFallback;
868 /* set flag and plcp for fallback rate */
870 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
871 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
872 mch |= TXC_AMPDU_FBR;
873 txh->MacTxControlHigh = htol16(mch);
874 WLC_SET_MIMO_PLCP_AMPDU(plcp);
875 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
878 WL_AMPDU_TX("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
879 wlc->pub->unit, count, ampdu_len);
881 /* inform rate_sel if it this is a rate probe pkt */
882 frameid = ltoh16(txh->TxFrameID);
883 if (frameid & TXFID_RATE_PROBE_MASK) {
884 WL_ERROR("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n",
887 for (i = 0; i < count; i++)
888 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
889 ampdu->txpkt_weight);
897 wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
898 struct sk_buff *p, tx_status_t *txs)
900 scb_ampdu_t *scb_ampdu;
901 struct wlc_info *wlc = ampdu->wlc;
902 scb_ampdu_tid_ini_t *ini;
904 struct ieee80211_tx_info *tx_info;
906 tx_info = IEEE80211_SKB_CB(p);
907 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
909 ASSERT(scb->magic == SCB_MAGIC);
910 ASSERT(txs->status & TX_STATUS_AMPDU);
911 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
913 ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
914 ASSERT(ini->scb == scb);
916 /* BMAC_NOTE: For the split driver, second level txstatus comes later
917 * So if the ACK was received then wait for the second level else just
920 if (txs->status & TX_STATUS_ACK_RCV) {
923 /* wait till the next 8 bytes of txstatus is available */
926 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
929 if (status_delay > 10) {
930 ASSERT(status_delay <= 10);
935 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
936 ASSERT(s1 & TX_STATUS_AMPDU);
937 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
940 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
941 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
945 rate_status(struct wlc_info *wlc, struct ieee80211_tx_info *tx_info,
946 tx_status_t *txs, u8 mcs)
948 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
951 /* clear the rest of the rates */
952 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
958 #define SHORTNAME "AMPDU status"
960 static void BCMFASTPATH
961 wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
962 struct sk_buff *p, tx_status_t *txs,
965 scb_ampdu_t *scb_ampdu;
966 struct wlc_info *wlc = ampdu->wlc;
967 scb_ampdu_tid_ini_t *ini;
968 u8 bitmap[8], queue, tid;
971 struct dot11_header *h;
972 u16 seq, start_seq = 0, bindex, index, mcl;
974 bool ba_recd = false, ack_recd = false;
975 u8 suc_mpdu = 0, tot_mpdu = 0;
977 bool update_rate = true, retry = true, tx_error = false;
980 u8 retry_limit, rr_retry_limit;
981 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
984 u8 hole[AMPDU_MAX_MPDU];
985 memset(hole, 0, sizeof(hole));
988 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
989 ASSERT(txs->status & TX_STATUS_AMPDU);
991 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
994 tid = (u8) (p->priority);
996 ini = SCB_AMPDU_INI(scb_ampdu, tid);
997 retry_limit = ampdu->retry_limit_tid[tid];
998 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
1000 ASSERT(ini->scb == scb);
1002 memset(bitmap, 0, sizeof(bitmap));
1003 queue = txs->frameid & TXFID_QUEUE_MASK;
1004 ASSERT(queue < AC_COUNT);
1006 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1008 if (txs->status & TX_STATUS_ACK_RCV) {
1009 if (TX_STATUS_SUPR_UF == supr_status) {
1010 update_rate = false;
1013 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1014 start_seq = txs->sequence >> SEQNUM_SHIFT;
1015 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1016 TX_STATUS_BA_BMAP03_SHIFT;
1018 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1019 ASSERT(s1 & TX_STATUS_AMPDU);
1022 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1023 TX_STATUS_BA_BMAP47_SHIFT;
1024 bitmap[1] = (s1 >> 8) & 0xff;
1025 bitmap[2] = (s1 >> 16) & 0xff;
1026 bitmap[3] = (s1 >> 24) & 0xff;
1028 bitmap[4] = s2 & 0xff;
1029 bitmap[5] = (s2 >> 8) & 0xff;
1030 bitmap[6] = (s2 >> 16) & 0xff;
1031 bitmap[7] = (s2 >> 24) & 0xff;
1035 WLCNTINCR(ampdu->cnt->noba);
1037 update_rate = false;
1038 if (supr_status == TX_STATUS_SUPR_BADCH) {
1039 WL_ERROR("%s: Pkt tx suppressed, illegal channel possibly %d\n",
1041 CHSPEC_CHANNEL(wlc->default_bss->chanspec));
1043 if (supr_status == TX_STATUS_SUPR_FRAG)
1044 WL_NONE("%s: AMPDU frag err\n",
1047 WL_ERROR("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n",
1048 __func__, supr_status);
1050 /* no need to retry for badch; will fail again */
1051 if (supr_status == TX_STATUS_SUPR_BADCH ||
1052 supr_status == TX_STATUS_SUPR_EXPTIME) {
1054 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1055 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1057 WLCNTINCR(wlc->pub->_cnt->txexptime);
1059 /* TX underflow : try tuning pre-loading or ampdu size */
1060 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1061 /* if there were underflows, but pre-loading is not active,
1062 notify rate adaptation.
1064 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1069 } else if (txs->phyerr) {
1070 update_rate = false;
1071 WLCNTINCR(wlc->pub->_cnt->txphyerr);
1072 WL_ERROR("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n",
1073 wlc->pub->unit, txs->phyerr);
1076 if (WL_ERROR_ON()) {
1077 prpkt("txpkt (AMPDU)", wlc->osh, p);
1078 wlc_print_txdesc((d11txh_t *) p->data);
1079 wlc_print_txstatus(txs);
1085 /* loop through all pkts and retry if not acked */
1087 tx_info = IEEE80211_SKB_CB(p);
1088 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1089 txh = (d11txh_t *) p->data;
1090 mcl = ltoh16(txh->MacTxControlLow);
1091 plcp = (u8 *) (txh + 1);
1092 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1093 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1095 if (tot_mpdu == 0) {
1096 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1097 mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1100 index = TX_SEQ_TO_INDEX(seq);
1103 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1105 WL_AMPDU_TX("%s: tid %d seq is %d, start_seq is %d, bindex is %d set %d, index %d\n",
1106 __func__, tid, seq, start_seq, bindex,
1107 isset(bitmap, bindex), index);
1109 /* if acked then clear bit and free packet */
1110 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1111 && isset(bitmap, bindex)) {
1112 ini->tx_in_transit--;
1113 ini->txretry[index] = 0;
1115 /* ampdu_ack_len: number of acked aggregated frames */
1116 /* ampdu_ack_map: block ack bit map for the aggregation */
1117 /* ampdu_len: number of aggregated frames */
1118 rate_status(wlc, tx_info, txs, mcs);
1119 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1120 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1122 /* XXX TODO: Make these accurate. */
1123 tx_info->status.ampdu_ack_len =
1125 status & TX_STATUS_FRM_RTX_MASK) >>
1126 TX_STATUS_FRM_RTX_SHIFT;
1127 tx_info->status.ampdu_len =
1129 status & TX_STATUS_FRM_RTX_MASK) >>
1130 TX_STATUS_FRM_RTX_SHIFT;
1132 skb_pull(p, D11_PHY_HDR_LEN);
1133 skb_pull(p, D11_TXH_LEN);
1135 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1141 /* either retransmit or send bar if ack not recd */
1143 struct ieee80211_tx_rate *txrate =
1144 tx_info->status.rates;
1145 if (retry && (txrate[0].count < (int)retry_limit)) {
1146 ini->txretry[index]++;
1147 ini->tx_in_transit--;
1148 /* Use high prededence for retransmit to give some punch */
1149 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1150 wlc_txq_enq(wlc, scb, p,
1151 WLC_PRIO_TO_HI_PREC(tid));
1154 ini->tx_in_transit--;
1155 ieee80211_tx_info_clear_status(tx_info);
1157 IEEE80211_TX_STAT_AMPDU_NO_BACK;
1158 skb_pull(p, D11_PHY_HDR_LEN);
1159 skb_pull(p, D11_TXH_LEN);
1160 WL_ERROR("%s: BA Timeout, seq %d, in_transit %d\n",
1161 SHORTNAME, seq, ini->tx_in_transit);
1162 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1168 /* break out if last packet of ampdu */
1169 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1173 p = GETNEXTTXP(wlc, queue);
1179 wlc_send_q(wlc, wlc->active_queue);
1181 /* update rate state */
1182 if (WLANTSEL_ENAB(wlc))
1183 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1185 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1189 ampdu_cleanup_tid_ini(struct ampdu_info *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
1192 scb_ampdu_tid_ini_t *ini;
1193 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1197 WL_AMPDU_CTL("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1198 ampdu->wlc->pub->unit, tid);
1200 if (ini->tx_in_transit && !force)
1203 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1204 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1206 /* free all buffered tx packets */
1207 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
1210 /* initialize the initiator code for tid */
1211 static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
1212 scb_ampdu_t *scb_ampdu,
1213 u8 tid, bool override)
1215 scb_ampdu_tid_ini_t *ini;
1218 ASSERT(scb_ampdu->scb);
1219 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1220 ASSERT(tid < AMPDU_MAX_SCB_TID);
1222 /* check for per-tid control of ampdu */
1223 if (!ampdu->ini_enable[tid]) {
1224 WL_ERROR("%s: Rejecting tid %d\n", __func__, tid);
1228 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1230 ini->scb = scb_ampdu->scb;
1231 ini->magic = INI_MAGIC;
1232 WLCNTINCR(ampdu->cnt->txaddbareq);
1237 int wlc_ampdu_set(struct ampdu_info *ampdu, bool on)
1239 struct wlc_info *wlc = ampdu->wlc;
1241 wlc->pub->_ampdu = false;
1244 if (!N_ENAB(wlc->pub)) {
1245 WL_AMPDU_ERR("wl%d: driver not nmode enabled\n",
1247 return BCME_UNSUPPORTED;
1249 if (!wlc_ampdu_cap(ampdu)) {
1250 WL_AMPDU_ERR("wl%d: device not ampdu capable\n",
1252 return BCME_UNSUPPORTED;
1254 wlc->pub->_ampdu = on;
1260 bool wlc_ampdu_cap(struct ampdu_info *ampdu)
1262 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1268 static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
1272 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1273 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1275 rate = MCS_RATE(mcs, false, false);
1276 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1277 /* 40 MHz, No SGI */
1278 rate = MCS_RATE(mcs, true, false);
1279 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1281 rate = MCS_RATE(mcs, false, true);
1282 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1284 rate = MCS_RATE(mcs, true, true);
1285 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1290 wlc_ampdu_null_delim_cnt(struct ampdu_info *ampdu, struct scb *scb,
1291 ratespec_t rspec, int phylen)
1293 scb_ampdu_t *scb_ampdu;
1294 int bytes, cnt, tmp;
1298 ASSERT(SCB_AMPDU(scb));
1300 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1303 if (scb_ampdu->mpdu_density == 0)
1306 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1307 density x is in 2^(x-4) usec
1308 ==> # of bytes needed for req density = rate/2^(17-x)
1309 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1312 tx_density = scb_ampdu->mpdu_density;
1314 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1315 tmp = 1 << (17 - tx_density);
1316 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1318 if (bytes > phylen) {
1319 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1326 void wlc_ampdu_macaddr_upd(struct wlc_info *wlc)
1328 char template[T_RAM_ACCESS_SZ * 2];
1330 /* driver needs to write the ta in the template; ta is at offset 16 */
1331 memset(template, 0, sizeof(template));
1332 bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETH_ALEN);
1333 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1337 bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
1339 return wlc->ampdu->ini_enable[tid];
1342 void wlc_ampdu_shm_upd(struct ampdu_info *ampdu)
1344 struct wlc_info *wlc = ampdu->wlc;
1346 /* Extend ucode internal watchdog timer to match larger received frames */
1347 if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1348 AMPDU_RX_FACTOR_64K) {
1349 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1350 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1352 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1353 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);