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.
24 #include <bcmendian.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>
41 #include <bcm_rpc_tp.h>
42 #include <wlc_rpctx.h>
45 #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
46 #define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
47 #define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
48 #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
49 #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
50 #define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
51 #define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
52 #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
53 #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
54 #define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
55 #define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
56 #define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
57 #define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
59 #define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
60 #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
63 #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
64 #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
65 #define FFPLD_PLD_INCR 1000 /* increments in bytes */
66 #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
67 * accumulate between resets.
70 #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
72 /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
73 #define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
74 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
83 WL_AMPDU_HW_VAL
| WL_AMPDU_HWTXS_VAL
| WL_AMPDU_HWDBG_VAL
;
86 /* structure to hold tx fifo information and pre-loading state
87 * counters specific to tx underflows of ampdus
88 * some counters might be redundant with the ones in wlc or ampdu structures.
89 * This allows to maintain a specific state independantly of
90 * how often and/or when the wlc counters are updated.
92 typedef struct wlc_fifo_info
{
93 u16 ampdu_pld_size
; /* number of bytes to be pre-loaded */
94 u8 mcs2ampdu_table
[FFPLD_MAX_MCS
+ 1]; /* per-mcs max # of mpdus in an ampdu */
95 u16 prev_txfunfl
; /* num of underflows last read from the HW macstats counter */
96 u32 accum_txfunfl
; /* num of underflows since we modified pld params */
97 u32 accum_txampdu
; /* num of tx ampdu since we modified pld params */
98 u32 prev_txampdu
; /* previous reading of tx ampdu */
99 u32 dmaxferrate
; /* estimated dma avg xfer rate in kbits/sec */
102 /* AMPDU module specific state */
104 wlc_info_t
*wlc
; /* pointer to main wlc structure */
105 int scb_handle
; /* scb cubby handle to retrieve data from scb */
106 u8 ini_enable
[AMPDU_MAX_SCB_TID
]; /* per-tid initiator enable/disable of ampdu */
107 u8 ba_tx_wsize
; /* Tx ba window size (in pdu) */
108 u8 ba_rx_wsize
; /* Rx ba window size (in pdu) */
109 u8 retry_limit
; /* mpdu transmit retry limit */
110 u8 rr_retry_limit
; /* mpdu transmit retry limit at regular rate */
111 u8 retry_limit_tid
[AMPDU_MAX_SCB_TID
]; /* per-tid mpdu transmit retry limit */
112 /* per-tid mpdu transmit retry limit at regular rate */
113 u8 rr_retry_limit_tid
[AMPDU_MAX_SCB_TID
];
114 u8 mpdu_density
; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
115 s8 max_pdu
; /* max pdus allowed in ampdu */
116 u8 dur
; /* max duration of an ampdu (in msec) */
117 u8 txpkt_weight
; /* weight of ampdu in txfifo; reduces rate lag */
118 u8 rx_factor
; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
119 u32 ffpld_rsvd
; /* number of bytes to reserve for preload */
120 u32 max_txlen
[MCS_TABLE_SIZE
][2][2]; /* max size of ampdu per mcs, bw and sgi */
121 void *ini_free
[AMPDU_INI_FREE
]; /* array of ini's to be freed on detach */
122 bool mfbr
; /* enable multiple fallback rate */
123 u32 tx_max_funl
; /* underflows should be kept such that
124 * (tx_max_funfl*underflows) < tx frames
126 wlc_fifo_info_t fifo_tb
[NUM_FFPLD_FIFO
]; /* table of fifo infos */
131 bool waiting_status
; /* To help sanity checks */
135 #define AMPDU_CLEANUPFLAG_RX (0x1)
136 #define AMPDU_CLEANUPFLAG_TX (0x2)
138 #define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
139 #define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
141 static void wlc_ffpld_init(ampdu_info_t
*ampdu
);
142 static int wlc_ffpld_check_txfunfl(wlc_info_t
*wlc
, int f
);
143 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t
*ampdu
, int f
);
145 static scb_ampdu_tid_ini_t
*wlc_ampdu_init_tid_ini(ampdu_info_t
*ampdu
,
146 scb_ampdu_t
*scb_ampdu
,
147 u8 tid
, bool override
);
148 static void ampdu_cleanup_tid_ini(ampdu_info_t
*ampdu
, scb_ampdu_t
*scb_ampdu
,
150 static void ampdu_update_max_txlen(ampdu_info_t
*ampdu
, u8 dur
);
151 static void scb_ampdu_update_config(ampdu_info_t
*ampdu
, struct scb
*scb
);
152 static void scb_ampdu_update_config_all(ampdu_info_t
*ampdu
);
154 #define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
156 static void wlc_ampdu_dotxstatus_complete(ampdu_info_t
*ampdu
, struct scb
*scb
,
157 void *p
, tx_status_t
*txs
,
161 static inline u16
pkt_txh_seqnum(wlc_info_t
*wlc
, void *p
)
164 struct dot11_header
*h
;
165 txh
= (d11txh_t
*) PKTDATA(p
);
166 h
= (struct dot11_header
*)((u8
*) (txh
+ 1) + D11_PHY_HDR_LEN
);
167 return ltoh16(h
->seq
) >> SEQNUM_SHIFT
;
170 ampdu_info_t
*wlc_ampdu_attach(wlc_info_t
*wlc
)
175 /* some code depends on packed structures */
176 ASSERT(DOT11_MAXNUMFRAGS
== NBITS(u16
));
177 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE
));
178 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE
));
179 ASSERT(wlc
->pub
->tunables
->ampdunummpdu
<= AMPDU_MAX_MPDU
);
180 ASSERT(wlc
->pub
->tunables
->ampdunummpdu
> 0);
182 ampdu
= kzalloc(sizeof(ampdu_info_t
), GFP_ATOMIC
);
184 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem\n", wlc
->pub
->unit
));
189 for (i
= 0; i
< AMPDU_MAX_SCB_TID
; i
++)
190 ampdu
->ini_enable
[i
] = true;
191 /* Disable ampdu for VO by default */
192 ampdu
->ini_enable
[PRIO_8021D_VO
] = false;
193 ampdu
->ini_enable
[PRIO_8021D_NC
] = false;
195 /* Disable ampdu for BK by default since not enough fifo space */
196 ampdu
->ini_enable
[PRIO_8021D_NONE
] = false;
197 ampdu
->ini_enable
[PRIO_8021D_BK
] = false;
199 ampdu
->ba_tx_wsize
= AMPDU_TX_BA_DEF_WSIZE
;
200 ampdu
->ba_rx_wsize
= AMPDU_RX_BA_DEF_WSIZE
;
201 ampdu
->mpdu_density
= AMPDU_DEF_MPDU_DENSITY
;
202 ampdu
->max_pdu
= AUTO
;
203 ampdu
->dur
= AMPDU_MAX_DUR
;
204 ampdu
->txpkt_weight
= AMPDU_DEF_TXPKT_WEIGHT
;
206 ampdu
->ffpld_rsvd
= AMPDU_DEF_FFPLD_RSVD
;
207 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
208 if (WLCISNPHY(wlc
->band
) && NREV_LT(wlc
->band
->phyrev
, 2))
209 ampdu
->rx_factor
= AMPDU_RX_FACTOR_32K
;
211 ampdu
->rx_factor
= AMPDU_RX_FACTOR_64K
;
213 /* Restrict to smaller rcv size for BMAC dongle */
214 ampdu
->rx_factor
= AMPDU_RX_FACTOR_32K
;
216 ampdu
->retry_limit
= AMPDU_DEF_RETRY_LIMIT
;
217 ampdu
->rr_retry_limit
= AMPDU_DEF_RR_RETRY_LIMIT
;
219 for (i
= 0; i
< AMPDU_MAX_SCB_TID
; i
++) {
220 ampdu
->retry_limit_tid
[i
] = ampdu
->retry_limit
;
221 ampdu
->rr_retry_limit_tid
[i
] = ampdu
->rr_retry_limit
;
224 ampdu_update_max_txlen(ampdu
, ampdu
->dur
);
226 /* try to set ampdu to the default value */
227 wlc_ampdu_set(ampdu
, wlc
->pub
->_ampdu
);
229 ampdu
->tx_max_funl
= FFPLD_TX_MAX_UNFL
;
230 wlc_ffpld_init(ampdu
);
235 void wlc_ampdu_detach(ampdu_info_t
*ampdu
)
242 /* free all ini's which were to be freed on callbacks which were never called */
243 for (i
= 0; i
< AMPDU_INI_FREE
; i
++) {
244 if (ampdu
->ini_free
[i
]) {
245 kfree(ampdu
->ini_free
[i
]);
249 wlc_module_unregister(ampdu
->wlc
->pub
, "ampdu", ampdu
);
253 void scb_ampdu_cleanup(ampdu_info_t
*ampdu
, struct scb
*scb
)
255 scb_ampdu_t
*scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
258 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
261 for (tid
= 0; tid
< AMPDU_MAX_SCB_TID
; tid
++) {
262 ampdu_cleanup_tid_ini(ampdu
, scb_ampdu
, tid
, false);
266 /* reset the ampdu state machine so that it can gracefully handle packets that were
267 * freed from the dma and tx queues during reinit
269 void wlc_ampdu_reset(ampdu_info_t
*ampdu
)
271 WL_NONE(("%s: Entering\n", __func__
));
274 static void scb_ampdu_update_config(ampdu_info_t
*ampdu
, struct scb
*scb
)
276 scb_ampdu_t
*scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
279 scb_ampdu
->max_pdu
= (u8
) ampdu
->wlc
->pub
->tunables
->ampdunummpdu
;
281 /* go back to legacy size if some preloading is occuring */
282 for (i
= 0; i
< NUM_FFPLD_FIFO
; i
++) {
283 if (ampdu
->fifo_tb
[i
].ampdu_pld_size
> FFPLD_PLD_INCR
)
284 scb_ampdu
->max_pdu
= AMPDU_NUM_MPDU_LEGACY
;
287 /* apply user override */
288 if (ampdu
->max_pdu
!= AUTO
)
289 scb_ampdu
->max_pdu
= (u8
) ampdu
->max_pdu
;
291 scb_ampdu
->release
= min_t(u8
, scb_ampdu
->max_pdu
, AMPDU_SCB_MAX_RELEASE
);
293 if (scb_ampdu
->max_rxlen
)
295 min_t(u8
, scb_ampdu
->release
, scb_ampdu
->max_rxlen
/ 1600);
297 scb_ampdu
->release
= min(scb_ampdu
->release
,
298 ampdu
->fifo_tb
[TX_AC_BE_FIFO
].
299 mcs2ampdu_table
[FFPLD_MAX_MCS
]);
301 ASSERT(scb_ampdu
->release
);
304 void scb_ampdu_update_config_all(ampdu_info_t
*ampdu
)
306 scb_ampdu_update_config(ampdu
, ampdu
->wlc
->pub
->global_scb
);
309 static void wlc_ffpld_init(ampdu_info_t
*ampdu
)
312 wlc_fifo_info_t
*fifo
;
314 for (j
= 0; j
< NUM_FFPLD_FIFO
; j
++) {
315 fifo
= (ampdu
->fifo_tb
+ j
);
316 fifo
->ampdu_pld_size
= 0;
317 for (i
= 0; i
<= FFPLD_MAX_MCS
; i
++)
318 fifo
->mcs2ampdu_table
[i
] = 255;
319 fifo
->dmaxferrate
= 0;
320 fifo
->accum_txampdu
= 0;
321 fifo
->prev_txfunfl
= 0;
322 fifo
->accum_txfunfl
= 0;
327 /* evaluate the dma transfer rate using the tx underflows as feedback.
328 * If necessary, increase tx fifo preloading. If not enough,
329 * decrease maximum ampdu size for each mcs till underflows stop
330 * Return 1 if pre-loading not active, -1 if not an underflow event,
331 * 0 if pre-loading module took care of the event.
333 static int wlc_ffpld_check_txfunfl(wlc_info_t
*wlc
, int fid
)
335 ampdu_info_t
*ampdu
= wlc
->ampdu
;
336 u32 phy_rate
= MCS_RATE(FFPLD_MAX_MCS
, true, false);
339 u32 current_ampdu_cnt
= 0;
342 wlc_fifo_info_t
*fifo
= (ampdu
->fifo_tb
+ fid
);
346 /* return if we got here for a different reason than underflows */
349 M_UCODE_MACSTAT
+ offsetof(macstat_t
, txfunfl
[fid
]));
350 new_txunfl
= (u16
) (cur_txunfl
- fifo
->prev_txfunfl
);
351 if (new_txunfl
== 0) {
352 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
355 fifo
->prev_txfunfl
= cur_txunfl
;
357 if (!ampdu
->tx_max_funl
)
360 /* check if fifo is big enough */
361 if (wlc_xmtfifo_sz_get(wlc
, fid
, &xmtfifo_sz
)) {
362 WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
366 if ((TXFIFO_SIZE_UNIT
* (u32
) xmtfifo_sz
) <= ampdu
->ffpld_rsvd
)
369 max_pld_size
= TXFIFO_SIZE_UNIT
* xmtfifo_sz
- ampdu
->ffpld_rsvd
;
370 fifo
->accum_txfunfl
+= new_txunfl
;
372 /* we need to wait for at least 10 underflows */
373 if (fifo
->accum_txfunfl
< 10)
376 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
377 current_ampdu_cnt
, fifo
->accum_txfunfl
));
380 compute the current ratio of tx unfl per ampdu.
381 When the current ampdu count becomes too
382 big while the ratio remains small, we reset
383 the current count in order to not
384 introduce too big of a latency in detecting a
385 large amount of tx underflows later.
388 txunfl_ratio
= current_ampdu_cnt
/ fifo
->accum_txfunfl
;
390 if (txunfl_ratio
> ampdu
->tx_max_funl
) {
391 if (current_ampdu_cnt
>= FFPLD_MAX_AMPDU_CNT
) {
392 fifo
->accum_txfunfl
= 0;
397 min_t(u8
, fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
], AMPDU_NUM_MPDU_LEGACY
);
399 /* In case max value max_pdu is already lower than
400 the fifo depth, there is nothing more we can do.
403 if (fifo
->ampdu_pld_size
>= max_mpdu
* FFPLD_MPDU_SIZE
) {
404 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
405 fifo
->accum_txfunfl
= 0;
409 if (fifo
->ampdu_pld_size
< max_pld_size
) {
411 /* increment by TX_FIFO_PLD_INC bytes */
412 fifo
->ampdu_pld_size
+= FFPLD_PLD_INCR
;
413 if (fifo
->ampdu_pld_size
> max_pld_size
)
414 fifo
->ampdu_pld_size
= max_pld_size
;
416 /* update scb release size */
417 scb_ampdu_update_config_all(ampdu
);
420 compute a new dma xfer rate for max_mpdu @ max mcs.
421 This is the minimum dma rate that
422 can acheive no unferflow condition for the current mpdu size.
424 /* note : we divide/multiply by 100 to avoid integer overflows */
427 (max_mpdu
* FFPLD_MPDU_SIZE
- fifo
->ampdu_pld_size
))
428 / (max_mpdu
* FFPLD_MPDU_SIZE
)) * 100;
430 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
431 fifo
->dmaxferrate
, fifo
->ampdu_pld_size
));
434 /* decrease ampdu size */
435 if (fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] > 1) {
436 if (fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] == 255)
437 fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] =
438 AMPDU_NUM_MPDU_LEGACY
- 1;
440 fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] -= 1;
442 /* recompute the table */
443 wlc_ffpld_calc_mcs2ampdu_table(ampdu
, fid
);
445 /* update scb release size */
446 scb_ampdu_update_config_all(ampdu
);
449 fifo
->accum_txfunfl
= 0;
453 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t
*ampdu
, int f
)
456 u32 phy_rate
, dma_rate
, tmp
;
458 wlc_fifo_info_t
*fifo
= (ampdu
->fifo_tb
+ f
);
460 /* recompute the dma rate */
461 /* note : we divide/multiply by 100 to avoid integer overflows */
463 min_t(u8
, fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
], AMPDU_NUM_MPDU_LEGACY
);
464 phy_rate
= MCS_RATE(FFPLD_MAX_MCS
, true, false);
467 (max_mpdu
* FFPLD_MPDU_SIZE
- fifo
->ampdu_pld_size
))
468 / (max_mpdu
* FFPLD_MPDU_SIZE
)) * 100;
469 fifo
->dmaxferrate
= dma_rate
;
471 /* fill up the mcs2ampdu table; do not recalc the last mcs */
472 dma_rate
= dma_rate
>> 7;
473 for (i
= 0; i
< FFPLD_MAX_MCS
; i
++) {
474 /* shifting to keep it within integer range */
475 phy_rate
= MCS_RATE(i
, true, false) >> 7;
476 if (phy_rate
> dma_rate
) {
477 tmp
= ((fifo
->ampdu_pld_size
* phy_rate
) /
478 ((phy_rate
- dma_rate
) * FFPLD_MPDU_SIZE
)) + 1;
479 tmp
= min_t(u32
, tmp
, 255);
480 fifo
->mcs2ampdu_table
[i
] = (u8
) tmp
;
485 static void BCMFASTPATH
486 wlc_ampdu_agg(ampdu_info_t
*ampdu
, struct scb
*scb
, void *p
, uint prec
)
488 scb_ampdu_t
*scb_ampdu
;
489 scb_ampdu_tid_ini_t
*ini
;
490 u8 tid
= (u8
) PKTPRIO(p
);
492 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
494 /* initialize initiator on first packet; sends addba req */
495 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
496 if (ini
->magic
!= INI_MAGIC
) {
497 ini
= wlc_ampdu_init_tid_ini(ampdu
, scb_ampdu
, tid
, false);
503 wlc_sendampdu(ampdu_info_t
*ampdu
, wlc_txq_info_t
*qi
, void **pdu
, int prec
)
507 void *p
, *pkt
[AMPDU_MAX_MPDU
];
510 u8 preamble_type
= WLC_GF_PREAMBLE
;
511 u8 fbr_preamble_type
= WLC_GF_PREAMBLE
;
512 u8 rts_preamble_type
= WLC_LONG_PREAMBLE
;
513 u8 rts_fbr_preamble_type
= WLC_LONG_PREAMBLE
;
515 bool rr
= true, fbr
= false;
516 uint i
, count
= 0, fifo
, seg_cnt
= 0;
517 u16 plen
, len
, seq
= 0, mcl
, mch
, index
, frameid
, dma_len
= 0;
518 u32 ampdu_len
, maxlen
= 0;
519 d11txh_t
*txh
= NULL
;
521 struct dot11_header
*h
;
523 scb_ampdu_t
*scb_ampdu
;
524 scb_ampdu_tid_ini_t
*ini
;
526 bool use_rts
= false, use_cts
= false;
527 ratespec_t rspec
= 0, rspec_fallback
= 0;
528 ratespec_t rts_rspec
= 0, rts_rspec_fallback
= 0;
529 u16 mimo_ctlchbw
= PHY_TXC1_BW_20MHZ
;
530 struct dot11_rts_frame
*rts
;
534 struct ieee80211_tx_info
*tx_info
;
543 tid
= (u8
) PKTPRIO(p
);
544 ASSERT(tid
< AMPDU_MAX_SCB_TID
);
546 f
= ampdu
->fifo_tb
+ prio2fifo
[tid
];
548 scb
= wlc
->pub
->global_scb
;
549 ASSERT(scb
->magic
== SCB_MAGIC
);
551 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
553 ini
= &scb_ampdu
->ini
[tid
];
555 /* Let pressure continue to build ... */
556 qlen
= pktq_plen(&qi
->q
, prec
);
557 if (ini
->tx_in_transit
> 0 && qlen
< scb_ampdu
->max_pdu
) {
561 wlc_ampdu_agg(ampdu
, scb
, p
, tid
);
563 if (wlc
->block_datafifo
) {
564 WL_ERROR(("%s: Fifo blocked\n", __func__
));
567 rr_retry_limit
= ampdu
->rr_retry_limit_tid
[tid
];
571 struct ieee80211_tx_rate
*txrate
;
573 tx_info
= IEEE80211_SKB_CB(p
);
574 txrate
= tx_info
->status
.rates
;
576 if (tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
) {
577 err
= wlc_prep_pdu(wlc
, p
, &fifo
);
579 WL_ERROR(("%s: AMPDU flag is off!\n", __func__
));
586 if (err
== BCME_BUSY
) {
587 WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc
->pub
->unit
, seq
));
588 WLCNTINCR(ampdu
->cnt
->sduretry
);
593 /* error in the packet; reject it */
594 WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc
->pub
->unit
, seq
));
595 WLCNTINCR(ampdu
->cnt
->sdurejected
);
601 /* pkt is good to be aggregated */
602 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
603 txh
= (d11txh_t
*) PKTDATA(p
);
604 plcp
= (u8
*) (txh
+ 1);
605 h
= (struct dot11_header
*)(plcp
+ D11_PHY_HDR_LEN
);
606 seq
= ltoh16(h
->seq
) >> SEQNUM_SHIFT
;
607 index
= TX_SEQ_TO_INDEX(seq
);
609 /* check mcl fields and test whether it can be agg'd */
610 mcl
= ltoh16(txh
->MacTxControlLow
);
611 mcl
&= ~TXC_AMPDU_MASK
;
612 fbr_iscck
= !(ltoh16(txh
->XtraFrameTypes
) & 0x3);
614 txh
->PreloadSize
= 0; /* always default to 0 */
616 /* Handle retry limits */
617 if (txrate
[0].count
<= rr_retry_limit
) {
628 /* extract the length info */
629 len
= fbr_iscck
? WLC_GET_CCK_PLCP_LEN(txh
->FragPLCPFallback
)
630 : WLC_GET_MIMO_PLCP_LEN(txh
->FragPLCPFallback
);
632 /* retrieve null delimiter count */
633 ndelim
= txh
->RTSPLCPFallback
[AMPDU_FBR_NULL_DELIM
];
636 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
637 wlc
->pub
->unit
, count
, len
));
640 * aggregateable mpdu. For ucode/hw agg,
641 * test whether need to break or change the epoch
645 mcl
|= (TXC_AMPDU_FIRST
<< TXC_AMPDU_SHIFT
);
646 /* refill the bits since might be a retx mpdu */
647 mcl
|= TXC_STARTMSDU
;
648 rts
= (struct dot11_rts_frame
*)&txh
->rts_frame
;
649 fc
= ltoh16(rts
->fc
);
650 if ((fc
& FC_KIND_MASK
) == FC_RTS
) {
654 if ((fc
& FC_KIND_MASK
) == FC_CTS
) {
659 mcl
|= (TXC_AMPDU_MIDDLE
<< TXC_AMPDU_SHIFT
);
660 mcl
&= ~(TXC_STARTMSDU
| TXC_SENDRTS
| TXC_SENDCTS
);
663 len
= roundup(len
, 4);
664 ampdu_len
+= (len
+ (ndelim
+ 1) * AMPDU_DELIMITER_LEN
);
666 dma_len
+= (u16
) pkttotlen(osh
, p
);
668 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
));
670 txh
->MacTxControlLow
= htol16(mcl
);
672 /* this packet is added */
675 /* patch the first MPDU */
677 u8 plcp0
, plcp3
, is40
, sgi
;
678 struct ieee80211_sta
*sta
;
680 sta
= tx_info
->control
.sta
;
686 plcp0
= txh
->FragPLCPFallback
[0];
687 plcp3
= txh
->FragPLCPFallback
[3];
690 is40
= (plcp0
& MIMO_PLCP_40MHZ
) ? 1 : 0;
691 sgi
= PLCP3_ISSGI(plcp3
) ? 1 : 0;
692 mcs
= plcp0
& ~MIMO_PLCP_40MHZ
;
693 ASSERT(mcs
< MCS_TABLE_SIZE
);
695 min(scb_ampdu
->max_rxlen
,
696 ampdu
->max_txlen
[mcs
][is40
][sgi
]);
698 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi
,
701 maxlen
= 64 * 1024; /* XXX Fix me to honor real max_rxlen */
705 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC
)
706 ? PHY_TXC1_BW_20MHZ_UP
: PHY_TXC1_BW_20MHZ
;
708 /* rebuild the rspec and rspec_fallback */
709 rspec
= RSPEC_MIMORATE
;
710 rspec
|= plcp
[0] & ~MIMO_PLCP_40MHZ
;
711 if (plcp
[0] & MIMO_PLCP_40MHZ
)
712 rspec
|= (PHY_TXC1_BW_40MHZ
<< RSPEC_BW_SHIFT
);
714 if (fbr_iscck
) /* CCK */
716 CCK_RSPEC(CCK_PHY2MAC_RATE
717 (txh
->FragPLCPFallback
[0]));
719 rspec_fallback
= RSPEC_MIMORATE
;
721 txh
->FragPLCPFallback
[0] & ~MIMO_PLCP_40MHZ
;
722 if (txh
->FragPLCPFallback
[0] & MIMO_PLCP_40MHZ
)
724 (PHY_TXC1_BW_40MHZ
<<
728 if (use_rts
|| use_cts
) {
730 wlc_rspec_to_rts_rspec(wlc
, rspec
, false,
733 wlc_rspec_to_rts_rspec(wlc
, rspec_fallback
,
734 false, mimo_ctlchbw
);
738 /* if (first mpdu for host agg) */
739 /* test whether to add more */
740 if ((MCS_RATE(mcs
, true, false) >= f
->dmaxferrate
) &&
741 (count
== f
->mcs2ampdu_table
[mcs
])) {
742 WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc
->pub
->unit
, count
, mcs
));
746 if (count
== scb_ampdu
->max_pdu
) {
747 WL_NONE(("Stop taking from q, reached %d deep\n",
748 scb_ampdu
->max_pdu
));
752 /* check to see if the next pkt is a candidate for aggregation */
753 p
= pktq_ppeek(&qi
->q
, prec
);
754 tx_info
= IEEE80211_SKB_CB(p
); /* tx_info must be checked with current p */
757 if ((tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
) &&
758 ((u8
) PKTPRIO(p
) == tid
)) {
761 pkttotlen(osh
, p
) + AMPDU_MAX_MPDU_OVERHEAD
;
762 plen
= max(scb_ampdu
->min_len
, plen
);
764 if ((plen
+ ampdu_len
) > maxlen
) {
766 WL_ERROR(("%s: Bogus plen #1\n",
772 /* check if there are enough descriptors available */
773 if (TXAVAIL(wlc
, fifo
) <= (seg_cnt
+ 1)) {
774 WL_ERROR(("%s: No fifo space !!!!!!\n", __func__
));
778 p
= pktq_pdeq(&qi
->q
, prec
);
786 ini
->tx_in_transit
+= count
;
789 WLCNTADD(ampdu
->cnt
->txmpdu
, count
);
791 /* patch up the last txh */
792 txh
= (d11txh_t
*) PKTDATA(pkt
[count
- 1]);
793 mcl
= ltoh16(txh
->MacTxControlLow
);
794 mcl
&= ~TXC_AMPDU_MASK
;
795 mcl
|= (TXC_AMPDU_LAST
<< TXC_AMPDU_SHIFT
);
796 txh
->MacTxControlLow
= htol16(mcl
);
798 /* remove the null delimiter after last mpdu */
799 ndelim
= txh
->RTSPLCPFallback
[AMPDU_FBR_NULL_DELIM
];
800 txh
->RTSPLCPFallback
[AMPDU_FBR_NULL_DELIM
] = 0;
801 ampdu_len
-= ndelim
* AMPDU_DELIMITER_LEN
;
803 /* remove the pad len from last mpdu */
804 fbr_iscck
= ((ltoh16(txh
->XtraFrameTypes
) & 0x3) == 0);
805 len
= fbr_iscck
? WLC_GET_CCK_PLCP_LEN(txh
->FragPLCPFallback
)
806 : WLC_GET_MIMO_PLCP_LEN(txh
->FragPLCPFallback
);
807 ampdu_len
-= roundup(len
, 4) - len
;
809 /* patch up the first txh & plcp */
810 txh
= (d11txh_t
*) PKTDATA(pkt
[0]);
811 plcp
= (u8
*) (txh
+ 1);
813 WLC_SET_MIMO_PLCP_LEN(plcp
, ampdu_len
);
814 /* mark plcp to indicate ampdu */
815 WLC_SET_MIMO_PLCP_AMPDU(plcp
);
817 /* reset the mixed mode header durations */
820 wlc_calc_lsig_len(wlc
, rspec
, ampdu_len
);
821 txh
->MModeLen
= htol16(mmodelen
);
822 preamble_type
= WLC_MM_PREAMBLE
;
824 if (txh
->MModeFbrLen
) {
826 wlc_calc_lsig_len(wlc
, rspec_fallback
, ampdu_len
);
827 txh
->MModeFbrLen
= htol16(mmfbrlen
);
828 fbr_preamble_type
= WLC_MM_PREAMBLE
;
831 /* set the preload length */
832 if (MCS_RATE(mcs
, true, false) >= f
->dmaxferrate
) {
833 dma_len
= min(dma_len
, f
->ampdu_pld_size
);
834 txh
->PreloadSize
= htol16(dma_len
);
836 txh
->PreloadSize
= 0;
838 mch
= ltoh16(txh
->MacTxControlHigh
);
840 /* update RTS dur fields */
841 if (use_rts
|| use_cts
) {
843 rts
= (struct dot11_rts_frame
*)&txh
->rts_frame
;
844 if ((mch
& TXC_PREAMBLE_RTS_MAIN_SHORT
) ==
845 TXC_PREAMBLE_RTS_MAIN_SHORT
)
846 rts_preamble_type
= WLC_SHORT_PREAMBLE
;
848 if ((mch
& TXC_PREAMBLE_RTS_FB_SHORT
) ==
849 TXC_PREAMBLE_RTS_FB_SHORT
)
850 rts_fbr_preamble_type
= WLC_SHORT_PREAMBLE
;
853 wlc_compute_rtscts_dur(wlc
, use_cts
, rts_rspec
,
854 rspec
, rts_preamble_type
,
855 preamble_type
, ampdu_len
,
857 rts
->durid
= htol16(durid
);
858 durid
= wlc_compute_rtscts_dur(wlc
, use_cts
,
861 rts_fbr_preamble_type
,
864 txh
->RTSDurFallback
= htol16(durid
);
865 /* set TxFesTimeNormal */
866 txh
->TxFesTimeNormal
= rts
->durid
;
867 /* set fallback rate version of TxFesTimeNormal */
868 txh
->TxFesTimeFallback
= txh
->RTSDurFallback
;
871 /* set flag and plcp for fallback rate */
873 WLCNTADD(ampdu
->cnt
->txfbr_mpdu
, count
);
874 WLCNTINCR(ampdu
->cnt
->txfbr_ampdu
);
875 mch
|= TXC_AMPDU_FBR
;
876 txh
->MacTxControlHigh
= htol16(mch
);
877 WLC_SET_MIMO_PLCP_AMPDU(plcp
);
878 WLC_SET_MIMO_PLCP_AMPDU(txh
->FragPLCPFallback
);
881 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
882 wlc
->pub
->unit
, count
, ampdu_len
));
884 /* inform rate_sel if it this is a rate probe pkt */
885 frameid
= ltoh16(txh
->TxFrameID
);
886 if (frameid
& TXFID_RATE_PROBE_MASK
) {
887 WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__
));
890 if (wlc
->rpc_agg
& BCM_RPC_TP_HOST_AGG_AMPDU
)
891 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc
->rpc
),
892 BCM_RPC_TP_HOST_AGG_AMPDU
, true);
894 for (i
= 0; i
< count
; i
++)
895 wlc_txfifo(wlc
, fifo
, pkt
[i
], i
== (count
- 1),
896 ampdu
->txpkt_weight
);
898 if (wlc
->rpc_agg
& BCM_RPC_TP_HOST_AGG_AMPDU
)
899 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc
->rpc
),
900 BCM_RPC_TP_HOST_AGG_AMPDU
, false);
909 wlc_ampdu_dotxstatus(ampdu_info_t
*ampdu
, struct scb
*scb
, void *p
,
912 scb_ampdu_t
*scb_ampdu
;
913 wlc_info_t
*wlc
= ampdu
->wlc
;
914 scb_ampdu_tid_ini_t
*ini
;
916 struct ieee80211_tx_info
*tx_info
;
918 tx_info
= IEEE80211_SKB_CB(p
);
919 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
921 ASSERT(scb
->magic
== SCB_MAGIC
);
922 ASSERT(txs
->status
& TX_STATUS_AMPDU
);
923 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
925 ini
= SCB_AMPDU_INI(scb_ampdu
, PKTPRIO(p
));
926 ASSERT(ini
->scb
== scb
);
928 /* BMAC_NOTE: For the split driver, second level txstatus comes later
929 * So if the ACK was received then wait for the second level else just
932 if (txs
->status
& TX_STATUS_ACK_RCV
) {
936 /* wait till the next 8 bytes of txstatus is available */
939 &wlc
->regs
->frmtxstatus
)) & TXS_V
) == 0) {
942 if (status_delay
> 10) {
943 ASSERT(status_delay
<= 10);
948 ASSERT(!(s1
& TX_STATUS_INTERMEDIATE
));
949 ASSERT(s1
& TX_STATUS_AMPDU
);
950 s2
= R_REG(wlc
->osh
, &wlc
->regs
->frmtxstatus2
);
953 /* Store the relevant information in ampdu structure */
954 WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
959 bcopy(txs
, &du
->txs
, sizeof(tx_status_t
));
960 ampdu
->waiting_status
= true;
965 wlc_ampdu_dotxstatus_complete(ampdu
, scb
, p
, txs
, s1
, s2
);
966 wlc_ampdu_txflowcontrol(wlc
, scb_ampdu
, ini
);
970 void wlc_ampdu_txstatus_complete(ampdu_info_t
*ampdu
, u32 s1
, u32 s2
)
972 WL_AMPDU_TX(("wl%d: wlc_ampdu_txstatus_complete: High Recvd 0x%x 0x%x p:%p\n", ampdu
->wlc
->pub
->unit
, s1
, s2
, ampdu
->p
));
974 ASSERT(ampdu
->waiting_status
);
976 /* The packet may have been freed if the SCB went away, if so, then still free the
980 struct ieee80211_tx_info
*tx_info
;
983 tx_info
= IEEE80211_SKB_CB(ampdu
->p
);
984 scb
= (struct scb
*)tx_info
->control
.sta
->drv_priv
;
986 wlc_ampdu_dotxstatus_complete(ampdu
, scb
, ampdu
->p
, &du
->txs
,
991 ampdu
->waiting_status
= false;
993 #endif /* WLC_HIGH_ONLY */
994 void rate_status(wlc_info_t
*wlc
, struct ieee80211_tx_info
*tx_info
,
995 tx_status_t
*txs
, u8 mcs
);
998 rate_status(wlc_info_t
*wlc
, struct ieee80211_tx_info
*tx_info
,
999 tx_status_t
*txs
, u8 mcs
)
1001 struct ieee80211_tx_rate
*txrate
= tx_info
->status
.rates
;
1004 /* clear the rest of the rates */
1005 for (i
= 2; i
< IEEE80211_TX_MAX_RATES
; i
++) {
1007 txrate
[i
].count
= 0;
1011 extern void wlc_txq_enq(wlc_info_t
*wlc
, struct scb
*scb
, void *sdu
,
1014 #define SHORTNAME "AMPDU status"
1016 static void BCMFASTPATH
1017 wlc_ampdu_dotxstatus_complete(ampdu_info_t
*ampdu
, struct scb
*scb
, void *p
,
1018 tx_status_t
*txs
, u32 s1
, u32 s2
)
1020 scb_ampdu_t
*scb_ampdu
;
1021 wlc_info_t
*wlc
= ampdu
->wlc
;
1022 scb_ampdu_tid_ini_t
*ini
;
1023 u8 bitmap
[8], queue
, tid
;
1026 struct dot11_header
*h
;
1027 u16 seq
, start_seq
= 0, bindex
, index
, mcl
;
1029 bool ba_recd
= false, ack_recd
= false;
1030 u8 suc_mpdu
= 0, tot_mpdu
= 0;
1032 bool update_rate
= true, retry
= true, tx_error
= false;
1035 u8 retry_limit
, rr_retry_limit
;
1036 struct ieee80211_tx_info
*tx_info
= IEEE80211_SKB_CB(p
);
1039 u8 hole
[AMPDU_MAX_MPDU
];
1040 bzero(hole
, sizeof(hole
));
1043 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
1044 ASSERT(txs
->status
& TX_STATUS_AMPDU
);
1046 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
1049 tid
= (u8
) PKTPRIO(p
);
1051 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1052 retry_limit
= ampdu
->retry_limit_tid
[tid
];
1053 rr_retry_limit
= ampdu
->rr_retry_limit_tid
[tid
];
1055 ASSERT(ini
->scb
== scb
);
1057 bzero(bitmap
, sizeof(bitmap
));
1058 queue
= txs
->frameid
& TXFID_QUEUE_MASK
;
1059 ASSERT(queue
< AC_COUNT
);
1061 supr_status
= txs
->status
& TX_STATUS_SUPR_MASK
;
1063 if (txs
->status
& TX_STATUS_ACK_RCV
) {
1064 if (TX_STATUS_SUPR_UF
== supr_status
) {
1065 update_rate
= false;
1068 ASSERT(txs
->status
& TX_STATUS_INTERMEDIATE
);
1069 start_seq
= txs
->sequence
>> SEQNUM_SHIFT
;
1070 bitmap
[0] = (txs
->status
& TX_STATUS_BA_BMAP03_MASK
) >>
1071 TX_STATUS_BA_BMAP03_SHIFT
;
1073 ASSERT(!(s1
& TX_STATUS_INTERMEDIATE
));
1074 ASSERT(s1
& TX_STATUS_AMPDU
);
1077 (s1
& TX_STATUS_BA_BMAP47_MASK
) <<
1078 TX_STATUS_BA_BMAP47_SHIFT
;
1079 bitmap
[1] = (s1
>> 8) & 0xff;
1080 bitmap
[2] = (s1
>> 16) & 0xff;
1081 bitmap
[3] = (s1
>> 24) & 0xff;
1083 bitmap
[4] = s2
& 0xff;
1084 bitmap
[5] = (s2
>> 8) & 0xff;
1085 bitmap
[6] = (s2
>> 16) & 0xff;
1086 bitmap
[7] = (s2
>> 24) & 0xff;
1090 WLCNTINCR(ampdu
->cnt
->noba
);
1092 update_rate
= false;
1093 if (supr_status
== TX_STATUS_SUPR_BADCH
) {
1094 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__
, CHSPEC_CHANNEL(wlc
->default_bss
->chanspec
)));
1096 if (supr_status
== TX_STATUS_SUPR_FRAG
)
1097 WL_NONE(("%s: AMPDU frag err\n",
1100 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__
, supr_status
));
1102 /* no need to retry for badch; will fail again */
1103 if (supr_status
== TX_STATUS_SUPR_BADCH
||
1104 supr_status
== TX_STATUS_SUPR_EXPTIME
) {
1106 WLCNTINCR(wlc
->pub
->_cnt
->txchanrej
);
1107 } else if (supr_status
== TX_STATUS_SUPR_EXPTIME
) {
1109 WLCNTINCR(wlc
->pub
->_cnt
->txexptime
);
1111 /* TX underflow : try tuning pre-loading or ampdu size */
1112 } else if (supr_status
== TX_STATUS_SUPR_FRAG
) {
1113 /* if there were underflows, but pre-loading is not active,
1114 notify rate adaptation.
1116 if (wlc_ffpld_check_txfunfl(wlc
, prio2fifo
[tid
])
1119 #ifdef WLC_HIGH_ONLY
1120 /* With BMAC, TX Underflows should not happen */
1121 WL_ERROR(("wl%d: BMAC TX Underflow?",
1126 } else if (txs
->phyerr
) {
1127 update_rate
= false;
1128 WLCNTINCR(wlc
->pub
->_cnt
->txphyerr
);
1129 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc
->pub
->unit
, txs
->phyerr
));
1132 if (WL_ERROR_ON()) {
1133 prpkt("txpkt (AMPDU)", wlc
->osh
, p
);
1134 wlc_print_txdesc((d11txh_t
*) PKTDATA(p
));
1135 wlc_print_txstatus(txs
);
1141 /* loop through all pkts and retry if not acked */
1143 tx_info
= IEEE80211_SKB_CB(p
);
1144 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
1145 txh
= (d11txh_t
*) PKTDATA(p
);
1146 mcl
= ltoh16(txh
->MacTxControlLow
);
1147 plcp
= (u8
*) (txh
+ 1);
1148 h
= (struct dot11_header
*)(plcp
+ D11_PHY_HDR_LEN
);
1149 seq
= ltoh16(h
->seq
) >> SEQNUM_SHIFT
;
1151 if (tot_mpdu
== 0) {
1152 mcs
= plcp
[0] & MIMO_PLCP_MCS_MASK
;
1153 mimoantsel
= ltoh16(txh
->ABI_MimoAntSel
);
1156 index
= TX_SEQ_TO_INDEX(seq
);
1159 bindex
= MODSUB_POW2(seq
, start_seq
, SEQNUM_MAX
);
1161 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1162 "bindex is %d set %d, index %d\n",
1163 __func__
, tid
, seq
, start_seq
, bindex
,
1164 isset(bitmap
, bindex
), index
));
1166 /* if acked then clear bit and free packet */
1167 if ((bindex
< AMPDU_TX_BA_MAX_WSIZE
)
1168 && isset(bitmap
, bindex
)) {
1169 ini
->tx_in_transit
--;
1170 ini
->txretry
[index
] = 0;
1172 /* ampdu_ack_len: number of acked aggregated frames */
1173 /* ampdu_ack_map: block ack bit map for the aggregation */
1174 /* ampdu_len: number of aggregated frames */
1175 rate_status(wlc
, tx_info
, txs
, mcs
);
1176 tx_info
->flags
|= IEEE80211_TX_STAT_ACK
;
1177 tx_info
->flags
|= IEEE80211_TX_STAT_AMPDU
;
1179 /* XXX TODO: Make these accurate. */
1180 tx_info
->status
.ampdu_ack_len
=
1182 status
& TX_STATUS_FRM_RTX_MASK
) >>
1183 TX_STATUS_FRM_RTX_SHIFT
;
1184 tx_info
->status
.ampdu_len
=
1186 status
& TX_STATUS_FRM_RTX_MASK
) >>
1187 TX_STATUS_FRM_RTX_SHIFT
;
1189 PKTPULL(p
, D11_PHY_HDR_LEN
);
1190 PKTPULL(p
, D11_TXH_LEN
);
1192 ieee80211_tx_status_irqsafe(wlc
->pub
->ieee_hw
,
1198 /* either retransmit or send bar if ack not recd */
1200 struct ieee80211_tx_rate
*txrate
=
1201 tx_info
->status
.rates
;
1202 if (retry
&& (txrate
[0].count
< (int)retry_limit
)) {
1203 ini
->txretry
[index
]++;
1204 ini
->tx_in_transit
--;
1205 /* Use high prededence for retransmit to give some punch */
1206 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1207 wlc_txq_enq(wlc
, scb
, p
,
1208 WLC_PRIO_TO_HI_PREC(tid
));
1211 ini
->tx_in_transit
--;
1212 ieee80211_tx_info_clear_status(tx_info
);
1214 IEEE80211_TX_STAT_AMPDU_NO_BACK
;
1215 PKTPULL(p
, D11_PHY_HDR_LEN
);
1216 PKTPULL(p
, D11_TXH_LEN
);
1217 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME
, seq
, ini
->tx_in_transit
));
1218 ieee80211_tx_status_irqsafe(wlc
->pub
->ieee_hw
,
1224 /* break out if last packet of ampdu */
1225 if (((mcl
& TXC_AMPDU_MASK
) >> TXC_AMPDU_SHIFT
) ==
1229 p
= GETNEXTTXP(wlc
, queue
);
1235 wlc_send_q(wlc
, wlc
->active_queue
);
1237 /* update rate state */
1238 if (WLANTSEL_ENAB(wlc
))
1239 antselid
= wlc_antsel_antsel2id(wlc
->asi
, mimoantsel
);
1241 wlc_txfifo_complete(wlc
, queue
, ampdu
->txpkt_weight
);
1245 ampdu_cleanup_tid_ini(ampdu_info_t
*ampdu
, scb_ampdu_t
*scb_ampdu
, u8 tid
,
1248 scb_ampdu_tid_ini_t
*ini
;
1249 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1253 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1254 ampdu
->wlc
->pub
->unit
, tid
));
1256 if (ini
->tx_in_transit
&& !force
)
1259 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, ini
->scb
);
1260 ASSERT(ini
== &scb_ampdu
->ini
[ini
->tid
]);
1262 /* free all buffered tx packets */
1263 pktq_pflush(ampdu
->wlc
->osh
, &scb_ampdu
->txq
, ini
->tid
, true, NULL
, 0);
1266 /* initialize the initiator code for tid */
1267 static scb_ampdu_tid_ini_t
*wlc_ampdu_init_tid_ini(ampdu_info_t
*ampdu
,
1268 scb_ampdu_t
*scb_ampdu
,
1269 u8 tid
, bool override
)
1271 scb_ampdu_tid_ini_t
*ini
;
1274 ASSERT(scb_ampdu
->scb
);
1275 ASSERT(SCB_AMPDU(scb_ampdu
->scb
));
1276 ASSERT(tid
< AMPDU_MAX_SCB_TID
);
1278 /* check for per-tid control of ampdu */
1279 if (!ampdu
->ini_enable
[tid
]) {
1280 WL_ERROR(("%s: Rejecting tid %d\n", __func__
, tid
));
1284 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1286 ini
->scb
= scb_ampdu
->scb
;
1287 ini
->magic
= INI_MAGIC
;
1288 WLCNTINCR(ampdu
->cnt
->txaddbareq
);
1293 int wlc_ampdu_set(ampdu_info_t
*ampdu
, bool on
)
1295 wlc_info_t
*wlc
= ampdu
->wlc
;
1297 wlc
->pub
->_ampdu
= false;
1300 if (!N_ENAB(wlc
->pub
)) {
1301 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1303 return BCME_UNSUPPORTED
;
1305 if (!wlc_ampdu_cap(ampdu
)) {
1306 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1308 return BCME_UNSUPPORTED
;
1310 wlc
->pub
->_ampdu
= on
;
1316 bool wlc_ampdu_cap(ampdu_info_t
*ampdu
)
1318 if (WLC_PHY_11N_CAP(ampdu
->wlc
->band
))
1324 static void ampdu_update_max_txlen(ampdu_info_t
*ampdu
, u8 dur
)
1328 for (mcs
= 0; mcs
< MCS_TABLE_SIZE
; mcs
++) {
1329 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1331 rate
= MCS_RATE(mcs
, false, false);
1332 ampdu
->max_txlen
[mcs
][0][0] = (rate
* dur
) >> 3;
1333 /* 40 MHz, No SGI */
1334 rate
= MCS_RATE(mcs
, true, false);
1335 ampdu
->max_txlen
[mcs
][1][0] = (rate
* dur
) >> 3;
1337 rate
= MCS_RATE(mcs
, false, true);
1338 ampdu
->max_txlen
[mcs
][0][1] = (rate
* dur
) >> 3;
1340 rate
= MCS_RATE(mcs
, true, true);
1341 ampdu
->max_txlen
[mcs
][1][1] = (rate
* dur
) >> 3;
1346 wlc_ampdu_null_delim_cnt(ampdu_info_t
*ampdu
, struct scb
*scb
,
1347 ratespec_t rspec
, int phylen
)
1349 scb_ampdu_t
*scb_ampdu
;
1350 int bytes
, cnt
, tmp
;
1354 ASSERT(SCB_AMPDU(scb
));
1356 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
1359 if (scb_ampdu
->mpdu_density
== 0)
1362 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1363 density x is in 2^(x-4) usec
1364 ==> # of bytes needed for req density = rate/2^(17-x)
1365 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1368 tx_density
= scb_ampdu
->mpdu_density
;
1370 ASSERT(tx_density
<= AMPDU_MAX_MPDU_DENSITY
);
1371 tmp
= 1 << (17 - tx_density
);
1372 bytes
= CEIL(RSPEC2RATE(rspec
), tmp
);
1374 if (bytes
> phylen
) {
1375 cnt
= CEIL(bytes
- phylen
, AMPDU_DELIMITER_LEN
);
1382 void wlc_ampdu_macaddr_upd(wlc_info_t
*wlc
)
1384 char template[T_RAM_ACCESS_SZ
* 2];
1386 /* driver needs to write the ta in the template; ta is at offset 16 */
1387 bzero(template, sizeof(template));
1388 bcopy((char *)wlc
->pub
->cur_etheraddr
.octet
, template, ETHER_ADDR_LEN
);
1389 wlc_write_template_ram(wlc
, (T_BA_TPL_BASE
+ 16), (T_RAM_ACCESS_SZ
* 2),
1393 bool wlc_aggregatable(wlc_info_t
*wlc
, u8 tid
)
1395 return wlc
->ampdu
->ini_enable
[tid
];
1398 void wlc_ampdu_shm_upd(ampdu_info_t
*ampdu
)
1400 wlc_info_t
*wlc
= ampdu
->wlc
;
1402 /* Extend ucode internal watchdog timer to match larger received frames */
1403 if ((ampdu
->rx_factor
& HT_PARAMS_RX_FACTOR_MASK
) ==
1404 AMPDU_RX_FACTOR_64K
) {
1405 wlc_write_shm(wlc
, M_MIMO_MAXSYM
, MIMO_MAXSYM_MAX
);
1406 wlc_write_shm(wlc
, M_WATCHDOG_8TU
, WATCHDOG_8TU_MAX
);
1408 wlc_write_shm(wlc
, M_MIMO_MAXSYM
, MIMO_MAXSYM_DEF
);
1409 wlc_write_shm(wlc
, M_WATCHDOG_8TU
, WATCHDOG_8TU_DEF
);