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 wlc_info_t
*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(wlc_info_t
*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(wlc_info_t
*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(wlc_info_t
*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(wlc_info_t
*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
,
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 wlc_info_t
*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
);
935 void rate_status(wlc_info_t
*wlc
, struct ieee80211_tx_info
*tx_info
,
936 tx_status_t
*txs
, u8 mcs
);
939 rate_status(wlc_info_t
*wlc
, struct ieee80211_tx_info
*tx_info
,
940 tx_status_t
*txs
, u8 mcs
)
942 struct ieee80211_tx_rate
*txrate
= tx_info
->status
.rates
;
945 /* clear the rest of the rates */
946 for (i
= 2; i
< IEEE80211_TX_MAX_RATES
; i
++) {
952 #define SHORTNAME "AMPDU status"
954 static void BCMFASTPATH
955 wlc_ampdu_dotxstatus_complete(ampdu_info_t
*ampdu
, struct scb
*scb
,
956 struct sk_buff
*p
, tx_status_t
*txs
,
959 scb_ampdu_t
*scb_ampdu
;
960 wlc_info_t
*wlc
= ampdu
->wlc
;
961 scb_ampdu_tid_ini_t
*ini
;
962 u8 bitmap
[8], queue
, tid
;
965 struct dot11_header
*h
;
966 u16 seq
, start_seq
= 0, bindex
, index
, mcl
;
968 bool ba_recd
= false, ack_recd
= false;
969 u8 suc_mpdu
= 0, tot_mpdu
= 0;
971 bool update_rate
= true, retry
= true, tx_error
= false;
974 u8 retry_limit
, rr_retry_limit
;
975 struct ieee80211_tx_info
*tx_info
= IEEE80211_SKB_CB(p
);
978 u8 hole
[AMPDU_MAX_MPDU
];
979 memset(hole
, 0, sizeof(hole
));
982 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
983 ASSERT(txs
->status
& TX_STATUS_AMPDU
);
985 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
988 tid
= (u8
) (p
->priority
);
990 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
991 retry_limit
= ampdu
->retry_limit_tid
[tid
];
992 rr_retry_limit
= ampdu
->rr_retry_limit_tid
[tid
];
994 ASSERT(ini
->scb
== scb
);
996 memset(bitmap
, 0, sizeof(bitmap
));
997 queue
= txs
->frameid
& TXFID_QUEUE_MASK
;
998 ASSERT(queue
< AC_COUNT
);
1000 supr_status
= txs
->status
& TX_STATUS_SUPR_MASK
;
1002 if (txs
->status
& TX_STATUS_ACK_RCV
) {
1003 if (TX_STATUS_SUPR_UF
== supr_status
) {
1004 update_rate
= false;
1007 ASSERT(txs
->status
& TX_STATUS_INTERMEDIATE
);
1008 start_seq
= txs
->sequence
>> SEQNUM_SHIFT
;
1009 bitmap
[0] = (txs
->status
& TX_STATUS_BA_BMAP03_MASK
) >>
1010 TX_STATUS_BA_BMAP03_SHIFT
;
1012 ASSERT(!(s1
& TX_STATUS_INTERMEDIATE
));
1013 ASSERT(s1
& TX_STATUS_AMPDU
);
1016 (s1
& TX_STATUS_BA_BMAP47_MASK
) <<
1017 TX_STATUS_BA_BMAP47_SHIFT
;
1018 bitmap
[1] = (s1
>> 8) & 0xff;
1019 bitmap
[2] = (s1
>> 16) & 0xff;
1020 bitmap
[3] = (s1
>> 24) & 0xff;
1022 bitmap
[4] = s2
& 0xff;
1023 bitmap
[5] = (s2
>> 8) & 0xff;
1024 bitmap
[6] = (s2
>> 16) & 0xff;
1025 bitmap
[7] = (s2
>> 24) & 0xff;
1029 WLCNTINCR(ampdu
->cnt
->noba
);
1031 update_rate
= false;
1032 if (supr_status
== TX_STATUS_SUPR_BADCH
) {
1033 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__
, CHSPEC_CHANNEL(wlc
->default_bss
->chanspec
)));
1035 if (supr_status
== TX_STATUS_SUPR_FRAG
)
1036 WL_NONE(("%s: AMPDU frag err\n",
1039 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__
, supr_status
));
1041 /* no need to retry for badch; will fail again */
1042 if (supr_status
== TX_STATUS_SUPR_BADCH
||
1043 supr_status
== TX_STATUS_SUPR_EXPTIME
) {
1045 WLCNTINCR(wlc
->pub
->_cnt
->txchanrej
);
1046 } else if (supr_status
== TX_STATUS_SUPR_EXPTIME
) {
1048 WLCNTINCR(wlc
->pub
->_cnt
->txexptime
);
1050 /* TX underflow : try tuning pre-loading or ampdu size */
1051 } else if (supr_status
== TX_STATUS_SUPR_FRAG
) {
1052 /* if there were underflows, but pre-loading is not active,
1053 notify rate adaptation.
1055 if (wlc_ffpld_check_txfunfl(wlc
, prio2fifo
[tid
])
1060 } else if (txs
->phyerr
) {
1061 update_rate
= false;
1062 WLCNTINCR(wlc
->pub
->_cnt
->txphyerr
);
1063 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc
->pub
->unit
, txs
->phyerr
));
1066 if (WL_ERROR_ON()) {
1067 prpkt("txpkt (AMPDU)", wlc
->osh
, p
);
1068 wlc_print_txdesc((d11txh_t
*) p
->data
);
1069 wlc_print_txstatus(txs
);
1075 /* loop through all pkts and retry if not acked */
1077 tx_info
= IEEE80211_SKB_CB(p
);
1078 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
1079 txh
= (d11txh_t
*) p
->data
;
1080 mcl
= ltoh16(txh
->MacTxControlLow
);
1081 plcp
= (u8
*) (txh
+ 1);
1082 h
= (struct dot11_header
*)(plcp
+ D11_PHY_HDR_LEN
);
1083 seq
= ltoh16(h
->seq
) >> SEQNUM_SHIFT
;
1085 if (tot_mpdu
== 0) {
1086 mcs
= plcp
[0] & MIMO_PLCP_MCS_MASK
;
1087 mimoantsel
= ltoh16(txh
->ABI_MimoAntSel
);
1090 index
= TX_SEQ_TO_INDEX(seq
);
1093 bindex
= MODSUB_POW2(seq
, start_seq
, SEQNUM_MAX
);
1095 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1096 "bindex is %d set %d, index %d\n",
1097 __func__
, tid
, seq
, start_seq
, bindex
,
1098 isset(bitmap
, bindex
), index
));
1100 /* if acked then clear bit and free packet */
1101 if ((bindex
< AMPDU_TX_BA_MAX_WSIZE
)
1102 && isset(bitmap
, bindex
)) {
1103 ini
->tx_in_transit
--;
1104 ini
->txretry
[index
] = 0;
1106 /* ampdu_ack_len: number of acked aggregated frames */
1107 /* ampdu_ack_map: block ack bit map for the aggregation */
1108 /* ampdu_len: number of aggregated frames */
1109 rate_status(wlc
, tx_info
, txs
, mcs
);
1110 tx_info
->flags
|= IEEE80211_TX_STAT_ACK
;
1111 tx_info
->flags
|= IEEE80211_TX_STAT_AMPDU
;
1113 /* XXX TODO: Make these accurate. */
1114 tx_info
->status
.ampdu_ack_len
=
1116 status
& TX_STATUS_FRM_RTX_MASK
) >>
1117 TX_STATUS_FRM_RTX_SHIFT
;
1118 tx_info
->status
.ampdu_len
=
1120 status
& TX_STATUS_FRM_RTX_MASK
) >>
1121 TX_STATUS_FRM_RTX_SHIFT
;
1123 skb_pull(p
, D11_PHY_HDR_LEN
);
1124 skb_pull(p
, D11_TXH_LEN
);
1126 ieee80211_tx_status_irqsafe(wlc
->pub
->ieee_hw
,
1132 /* either retransmit or send bar if ack not recd */
1134 struct ieee80211_tx_rate
*txrate
=
1135 tx_info
->status
.rates
;
1136 if (retry
&& (txrate
[0].count
< (int)retry_limit
)) {
1137 ini
->txretry
[index
]++;
1138 ini
->tx_in_transit
--;
1139 /* Use high prededence for retransmit to give some punch */
1140 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1141 wlc_txq_enq(wlc
, scb
, p
,
1142 WLC_PRIO_TO_HI_PREC(tid
));
1145 ini
->tx_in_transit
--;
1146 ieee80211_tx_info_clear_status(tx_info
);
1148 IEEE80211_TX_STAT_AMPDU_NO_BACK
;
1149 skb_pull(p
, D11_PHY_HDR_LEN
);
1150 skb_pull(p
, D11_TXH_LEN
);
1151 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME
, seq
, ini
->tx_in_transit
));
1152 ieee80211_tx_status_irqsafe(wlc
->pub
->ieee_hw
,
1158 /* break out if last packet of ampdu */
1159 if (((mcl
& TXC_AMPDU_MASK
) >> TXC_AMPDU_SHIFT
) ==
1163 p
= GETNEXTTXP(wlc
, queue
);
1169 wlc_send_q(wlc
, wlc
->active_queue
);
1171 /* update rate state */
1172 if (WLANTSEL_ENAB(wlc
))
1173 antselid
= wlc_antsel_antsel2id(wlc
->asi
, mimoantsel
);
1175 wlc_txfifo_complete(wlc
, queue
, ampdu
->txpkt_weight
);
1179 ampdu_cleanup_tid_ini(ampdu_info_t
*ampdu
, scb_ampdu_t
*scb_ampdu
, u8 tid
,
1182 scb_ampdu_tid_ini_t
*ini
;
1183 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1187 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1188 ampdu
->wlc
->pub
->unit
, tid
));
1190 if (ini
->tx_in_transit
&& !force
)
1193 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, ini
->scb
);
1194 ASSERT(ini
== &scb_ampdu
->ini
[ini
->tid
]);
1196 /* free all buffered tx packets */
1197 pktq_pflush(ampdu
->wlc
->osh
, &scb_ampdu
->txq
, ini
->tid
, true, NULL
, 0);
1200 /* initialize the initiator code for tid */
1201 static scb_ampdu_tid_ini_t
*wlc_ampdu_init_tid_ini(ampdu_info_t
*ampdu
,
1202 scb_ampdu_t
*scb_ampdu
,
1203 u8 tid
, bool override
)
1205 scb_ampdu_tid_ini_t
*ini
;
1208 ASSERT(scb_ampdu
->scb
);
1209 ASSERT(SCB_AMPDU(scb_ampdu
->scb
));
1210 ASSERT(tid
< AMPDU_MAX_SCB_TID
);
1212 /* check for per-tid control of ampdu */
1213 if (!ampdu
->ini_enable
[tid
]) {
1214 WL_ERROR(("%s: Rejecting tid %d\n", __func__
, tid
));
1218 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1220 ini
->scb
= scb_ampdu
->scb
;
1221 ini
->magic
= INI_MAGIC
;
1222 WLCNTINCR(ampdu
->cnt
->txaddbareq
);
1227 int wlc_ampdu_set(ampdu_info_t
*ampdu
, bool on
)
1229 wlc_info_t
*wlc
= ampdu
->wlc
;
1231 wlc
->pub
->_ampdu
= false;
1234 if (!N_ENAB(wlc
->pub
)) {
1235 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1237 return BCME_UNSUPPORTED
;
1239 if (!wlc_ampdu_cap(ampdu
)) {
1240 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1242 return BCME_UNSUPPORTED
;
1244 wlc
->pub
->_ampdu
= on
;
1250 bool wlc_ampdu_cap(ampdu_info_t
*ampdu
)
1252 if (WLC_PHY_11N_CAP(ampdu
->wlc
->band
))
1258 static void ampdu_update_max_txlen(ampdu_info_t
*ampdu
, u8 dur
)
1262 for (mcs
= 0; mcs
< MCS_TABLE_SIZE
; mcs
++) {
1263 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1265 rate
= MCS_RATE(mcs
, false, false);
1266 ampdu
->max_txlen
[mcs
][0][0] = (rate
* dur
) >> 3;
1267 /* 40 MHz, No SGI */
1268 rate
= MCS_RATE(mcs
, true, false);
1269 ampdu
->max_txlen
[mcs
][1][0] = (rate
* dur
) >> 3;
1271 rate
= MCS_RATE(mcs
, false, true);
1272 ampdu
->max_txlen
[mcs
][0][1] = (rate
* dur
) >> 3;
1274 rate
= MCS_RATE(mcs
, true, true);
1275 ampdu
->max_txlen
[mcs
][1][1] = (rate
* dur
) >> 3;
1280 wlc_ampdu_null_delim_cnt(ampdu_info_t
*ampdu
, struct scb
*scb
,
1281 ratespec_t rspec
, int phylen
)
1283 scb_ampdu_t
*scb_ampdu
;
1284 int bytes
, cnt
, tmp
;
1288 ASSERT(SCB_AMPDU(scb
));
1290 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
1293 if (scb_ampdu
->mpdu_density
== 0)
1296 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1297 density x is in 2^(x-4) usec
1298 ==> # of bytes needed for req density = rate/2^(17-x)
1299 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1302 tx_density
= scb_ampdu
->mpdu_density
;
1304 ASSERT(tx_density
<= AMPDU_MAX_MPDU_DENSITY
);
1305 tmp
= 1 << (17 - tx_density
);
1306 bytes
= CEIL(RSPEC2RATE(rspec
), tmp
);
1308 if (bytes
> phylen
) {
1309 cnt
= CEIL(bytes
- phylen
, AMPDU_DELIMITER_LEN
);
1316 void wlc_ampdu_macaddr_upd(wlc_info_t
*wlc
)
1318 char template[T_RAM_ACCESS_SZ
* 2];
1320 /* driver needs to write the ta in the template; ta is at offset 16 */
1321 memset(template, 0, sizeof(template));
1322 bcopy((char *)wlc
->pub
->cur_etheraddr
.octet
, template, ETHER_ADDR_LEN
);
1323 wlc_write_template_ram(wlc
, (T_BA_TPL_BASE
+ 16), (T_RAM_ACCESS_SZ
* 2),
1327 bool wlc_aggregatable(wlc_info_t
*wlc
, u8 tid
)
1329 return wlc
->ampdu
->ini_enable
[tid
];
1332 void wlc_ampdu_shm_upd(ampdu_info_t
*ampdu
)
1334 wlc_info_t
*wlc
= ampdu
->wlc
;
1336 /* Extend ucode internal watchdog timer to match larger received frames */
1337 if ((ampdu
->rx_factor
& HT_PARAMS_RX_FACTOR_MASK
) ==
1338 AMPDU_RX_FACTOR_64K
) {
1339 wlc_write_shm(wlc
, M_MIMO_MAXSYM
, MIMO_MAXSYM_MAX
);
1340 wlc_write_shm(wlc
, M_WATCHDOG_8TU
, WATCHDOG_8TU_MAX
);
1342 wlc_write_shm(wlc
, M_MIMO_MAXSYM
, MIMO_MAXSYM_DEF
);
1343 wlc_write_shm(wlc
, M_WATCHDOG_8TU
, WATCHDOG_8TU_DEF
);