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
*BCMATTACHFN(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
= (ampdu_info_t
*) MALLOC(wlc
->osh
, sizeof(ampdu_info_t
));
184 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem, malloced %d bytes\n", wlc
->pub
->unit
, MALLOCED(wlc
->osh
)));
187 bzero((char *)ampdu
, sizeof(ampdu_info_t
));
190 for (i
= 0; i
< AMPDU_MAX_SCB_TID
; i
++)
191 ampdu
->ini_enable
[i
] = TRUE
;
192 /* Disable ampdu for VO by default */
193 ampdu
->ini_enable
[PRIO_8021D_VO
] = FALSE
;
194 ampdu
->ini_enable
[PRIO_8021D_NC
] = FALSE
;
196 /* Disable ampdu for BK by default since not enough fifo space */
197 ampdu
->ini_enable
[PRIO_8021D_NONE
] = FALSE
;
198 ampdu
->ini_enable
[PRIO_8021D_BK
] = FALSE
;
200 ampdu
->ba_tx_wsize
= AMPDU_TX_BA_DEF_WSIZE
;
201 ampdu
->ba_rx_wsize
= AMPDU_RX_BA_DEF_WSIZE
;
202 ampdu
->mpdu_density
= AMPDU_DEF_MPDU_DENSITY
;
203 ampdu
->max_pdu
= AUTO
;
204 ampdu
->dur
= AMPDU_MAX_DUR
;
205 ampdu
->txpkt_weight
= AMPDU_DEF_TXPKT_WEIGHT
;
207 ampdu
->ffpld_rsvd
= AMPDU_DEF_FFPLD_RSVD
;
208 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
209 if (WLCISNPHY(wlc
->band
) && NREV_LT(wlc
->band
->phyrev
, 2))
210 ampdu
->rx_factor
= AMPDU_RX_FACTOR_32K
;
212 ampdu
->rx_factor
= AMPDU_RX_FACTOR_64K
;
214 /* Restrict to smaller rcv size for BMAC dongle */
215 ampdu
->rx_factor
= AMPDU_RX_FACTOR_32K
;
217 ampdu
->retry_limit
= AMPDU_DEF_RETRY_LIMIT
;
218 ampdu
->rr_retry_limit
= AMPDU_DEF_RR_RETRY_LIMIT
;
220 for (i
= 0; i
< AMPDU_MAX_SCB_TID
; i
++) {
221 ampdu
->retry_limit_tid
[i
] = ampdu
->retry_limit
;
222 ampdu
->rr_retry_limit_tid
[i
] = ampdu
->rr_retry_limit
;
225 ampdu_update_max_txlen(ampdu
, ampdu
->dur
);
227 /* try to set ampdu to the default value */
228 wlc_ampdu_set(ampdu
, wlc
->pub
->_ampdu
);
230 ampdu
->tx_max_funl
= FFPLD_TX_MAX_UNFL
;
231 wlc_ffpld_init(ampdu
);
236 void BCMATTACHFN(wlc_ampdu_detach
) (ampdu_info_t
*ampdu
)
243 /* free all ini's which were to be freed on callbacks which were never called */
244 for (i
= 0; i
< AMPDU_INI_FREE
; i
++) {
245 if (ampdu
->ini_free
[i
]) {
246 MFREE(ampdu
->wlc
->osh
, ampdu
->ini_free
[i
],
247 sizeof(scb_ampdu_tid_ini_t
));
251 wlc_module_unregister(ampdu
->wlc
->pub
, "ampdu", ampdu
);
252 MFREE(ampdu
->wlc
->osh
, ampdu
, sizeof(ampdu_info_t
));
255 void scb_ampdu_cleanup(ampdu_info_t
*ampdu
, struct scb
*scb
)
257 scb_ampdu_t
*scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
260 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
263 for (tid
= 0; tid
< AMPDU_MAX_SCB_TID
; tid
++) {
264 ampdu_cleanup_tid_ini(ampdu
, scb_ampdu
, tid
, FALSE
);
268 /* reset the ampdu state machine so that it can gracefully handle packets that were
269 * freed from the dma and tx queues during reinit
271 void wlc_ampdu_reset(ampdu_info_t
*ampdu
)
273 WL_NONE(("%s: Entering\n", __func__
));
276 static void scb_ampdu_update_config(ampdu_info_t
*ampdu
, struct scb
*scb
)
278 scb_ampdu_t
*scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
281 scb_ampdu
->max_pdu
= (u8
) ampdu
->wlc
->pub
->tunables
->ampdunummpdu
;
283 /* go back to legacy size if some preloading is occuring */
284 for (i
= 0; i
< NUM_FFPLD_FIFO
; i
++) {
285 if (ampdu
->fifo_tb
[i
].ampdu_pld_size
> FFPLD_PLD_INCR
)
286 scb_ampdu
->max_pdu
= AMPDU_NUM_MPDU_LEGACY
;
289 /* apply user override */
290 if (ampdu
->max_pdu
!= AUTO
)
291 scb_ampdu
->max_pdu
= (u8
) ampdu
->max_pdu
;
293 scb_ampdu
->release
= min(scb_ampdu
->max_pdu
, AMPDU_SCB_MAX_RELEASE
);
295 if (scb_ampdu
->max_rxlen
)
297 min(scb_ampdu
->release
, scb_ampdu
->max_rxlen
/ 1600);
299 scb_ampdu
->release
= min(scb_ampdu
->release
,
300 ampdu
->fifo_tb
[TX_AC_BE_FIFO
].
301 mcs2ampdu_table
[FFPLD_MAX_MCS
]);
303 ASSERT(scb_ampdu
->release
);
306 void scb_ampdu_update_config_all(ampdu_info_t
*ampdu
)
308 scb_ampdu_update_config(ampdu
, ampdu
->wlc
->pub
->global_scb
);
311 static void wlc_ffpld_init(ampdu_info_t
*ampdu
)
314 wlc_fifo_info_t
*fifo
;
316 for (j
= 0; j
< NUM_FFPLD_FIFO
; j
++) {
317 fifo
= (ampdu
->fifo_tb
+ j
);
318 fifo
->ampdu_pld_size
= 0;
319 for (i
= 0; i
<= FFPLD_MAX_MCS
; i
++)
320 fifo
->mcs2ampdu_table
[i
] = 255;
321 fifo
->dmaxferrate
= 0;
322 fifo
->accum_txampdu
= 0;
323 fifo
->prev_txfunfl
= 0;
324 fifo
->accum_txfunfl
= 0;
329 /* evaluate the dma transfer rate using the tx underflows as feedback.
330 * If necessary, increase tx fifo preloading. If not enough,
331 * decrease maximum ampdu size for each mcs till underflows stop
332 * Return 1 if pre-loading not active, -1 if not an underflow event,
333 * 0 if pre-loading module took care of the event.
335 static int wlc_ffpld_check_txfunfl(wlc_info_t
*wlc
, int fid
)
337 ampdu_info_t
*ampdu
= wlc
->ampdu
;
338 u32 phy_rate
= MCS_RATE(FFPLD_MAX_MCS
, TRUE
, FALSE
);
341 u32 current_ampdu_cnt
= 0;
344 wlc_fifo_info_t
*fifo
= (ampdu
->fifo_tb
+ fid
);
348 /* return if we got here for a different reason than underflows */
351 M_UCODE_MACSTAT
+ offsetof(macstat_t
, txfunfl
[fid
]));
352 new_txunfl
= (u16
) (cur_txunfl
- fifo
->prev_txfunfl
);
353 if (new_txunfl
== 0) {
354 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
357 fifo
->prev_txfunfl
= cur_txunfl
;
359 if (!ampdu
->tx_max_funl
)
362 /* check if fifo is big enough */
363 if (wlc_xmtfifo_sz_get(wlc
, fid
, &xmtfifo_sz
)) {
364 WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
368 if ((TXFIFO_SIZE_UNIT
* (u32
) xmtfifo_sz
) <= ampdu
->ffpld_rsvd
)
371 max_pld_size
= TXFIFO_SIZE_UNIT
* xmtfifo_sz
- ampdu
->ffpld_rsvd
;
372 fifo
->accum_txfunfl
+= new_txunfl
;
374 /* we need to wait for at least 10 underflows */
375 if (fifo
->accum_txfunfl
< 10)
378 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
379 current_ampdu_cnt
, fifo
->accum_txfunfl
));
382 compute the current ratio of tx unfl per ampdu.
383 When the current ampdu count becomes too
384 big while the ratio remains small, we reset
385 the current count in order to not
386 introduce too big of a latency in detecting a
387 large amount of tx underflows later.
390 txunfl_ratio
= current_ampdu_cnt
/ fifo
->accum_txfunfl
;
392 if (txunfl_ratio
> ampdu
->tx_max_funl
) {
393 if (current_ampdu_cnt
>= FFPLD_MAX_AMPDU_CNT
) {
394 fifo
->accum_txfunfl
= 0;
399 min(fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
], AMPDU_NUM_MPDU_LEGACY
);
401 /* In case max value max_pdu is already lower than
402 the fifo depth, there is nothing more we can do.
405 if (fifo
->ampdu_pld_size
>= max_mpdu
* FFPLD_MPDU_SIZE
) {
406 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
407 fifo
->accum_txfunfl
= 0;
411 if (fifo
->ampdu_pld_size
< max_pld_size
) {
413 /* increment by TX_FIFO_PLD_INC bytes */
414 fifo
->ampdu_pld_size
+= FFPLD_PLD_INCR
;
415 if (fifo
->ampdu_pld_size
> max_pld_size
)
416 fifo
->ampdu_pld_size
= max_pld_size
;
418 /* update scb release size */
419 scb_ampdu_update_config_all(ampdu
);
422 compute a new dma xfer rate for max_mpdu @ max mcs.
423 This is the minimum dma rate that
424 can acheive no unferflow condition for the current mpdu size.
426 /* note : we divide/multiply by 100 to avoid integer overflows */
429 (max_mpdu
* FFPLD_MPDU_SIZE
- fifo
->ampdu_pld_size
))
430 / (max_mpdu
* FFPLD_MPDU_SIZE
)) * 100;
432 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
433 fifo
->dmaxferrate
, fifo
->ampdu_pld_size
));
436 /* decrease ampdu size */
437 if (fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] > 1) {
438 if (fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] == 255)
439 fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] =
440 AMPDU_NUM_MPDU_LEGACY
- 1;
442 fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
] -= 1;
444 /* recompute the table */
445 wlc_ffpld_calc_mcs2ampdu_table(ampdu
, fid
);
447 /* update scb release size */
448 scb_ampdu_update_config_all(ampdu
);
451 fifo
->accum_txfunfl
= 0;
455 static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t
*ampdu
, int f
)
458 u32 phy_rate
, dma_rate
, tmp
;
460 wlc_fifo_info_t
*fifo
= (ampdu
->fifo_tb
+ f
);
462 /* recompute the dma rate */
463 /* note : we divide/multiply by 100 to avoid integer overflows */
465 min(fifo
->mcs2ampdu_table
[FFPLD_MAX_MCS
], AMPDU_NUM_MPDU_LEGACY
);
466 phy_rate
= MCS_RATE(FFPLD_MAX_MCS
, TRUE
, FALSE
);
469 (max_mpdu
* FFPLD_MPDU_SIZE
- fifo
->ampdu_pld_size
))
470 / (max_mpdu
* FFPLD_MPDU_SIZE
)) * 100;
471 fifo
->dmaxferrate
= dma_rate
;
473 /* fill up the mcs2ampdu table; do not recalc the last mcs */
474 dma_rate
= dma_rate
>> 7;
475 for (i
= 0; i
< FFPLD_MAX_MCS
; i
++) {
476 /* shifting to keep it within integer range */
477 phy_rate
= MCS_RATE(i
, TRUE
, FALSE
) >> 7;
478 if (phy_rate
> dma_rate
) {
479 tmp
= ((fifo
->ampdu_pld_size
* phy_rate
) /
480 ((phy_rate
- dma_rate
) * FFPLD_MPDU_SIZE
)) + 1;
482 fifo
->mcs2ampdu_table
[i
] = (u8
) tmp
;
487 static void BCMFASTPATH
488 wlc_ampdu_agg(ampdu_info_t
*ampdu
, struct scb
*scb
, void *p
, uint prec
)
490 scb_ampdu_t
*scb_ampdu
;
491 scb_ampdu_tid_ini_t
*ini
;
492 u8 tid
= (u8
) PKTPRIO(p
);
494 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
496 /* initialize initiator on first packet; sends addba req */
497 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
498 if (ini
->magic
!= INI_MAGIC
) {
499 ini
= wlc_ampdu_init_tid_ini(ampdu
, scb_ampdu
, tid
, FALSE
);
505 wlc_sendampdu(ampdu_info_t
*ampdu
, wlc_txq_info_t
*qi
, void **pdu
, int prec
)
509 void *p
, *pkt
[AMPDU_MAX_MPDU
];
512 u8 preamble_type
= WLC_GF_PREAMBLE
;
513 u8 fbr_preamble_type
= WLC_GF_PREAMBLE
;
514 u8 rts_preamble_type
= WLC_LONG_PREAMBLE
;
515 u8 rts_fbr_preamble_type
= WLC_LONG_PREAMBLE
;
517 bool rr
= TRUE
, fbr
= FALSE
;
518 uint i
, count
= 0, fifo
, seg_cnt
= 0;
519 u16 plen
, len
, seq
= 0, mcl
, mch
, index
, frameid
, dma_len
= 0;
520 u32 ampdu_len
, maxlen
= 0;
521 d11txh_t
*txh
= NULL
;
523 struct dot11_header
*h
;
525 scb_ampdu_t
*scb_ampdu
;
526 scb_ampdu_tid_ini_t
*ini
;
528 bool use_rts
= FALSE
, use_cts
= FALSE
;
529 ratespec_t rspec
= 0, rspec_fallback
= 0;
530 ratespec_t rts_rspec
= 0, rts_rspec_fallback
= 0;
531 u16 mimo_ctlchbw
= PHY_TXC1_BW_20MHZ
;
532 struct dot11_rts_frame
*rts
;
536 struct ieee80211_tx_info
*tx_info
;
545 tid
= (u8
) PKTPRIO(p
);
546 ASSERT(tid
< AMPDU_MAX_SCB_TID
);
548 f
= ampdu
->fifo_tb
+ prio2fifo
[tid
];
550 scb
= wlc
->pub
->global_scb
;
551 ASSERT(scb
->magic
== SCB_MAGIC
);
553 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
555 ini
= &scb_ampdu
->ini
[tid
];
557 /* Let pressure continue to build ... */
558 qlen
= pktq_plen(&qi
->q
, prec
);
559 if (ini
->tx_in_transit
> 0 && qlen
< scb_ampdu
->max_pdu
) {
563 wlc_ampdu_agg(ampdu
, scb
, p
, tid
);
565 if (wlc
->block_datafifo
) {
566 WL_ERROR(("%s: Fifo blocked\n", __func__
));
569 rr_retry_limit
= ampdu
->rr_retry_limit_tid
[tid
];
573 struct ieee80211_tx_rate
*txrate
;
575 tx_info
= IEEE80211_SKB_CB(p
);
576 txrate
= tx_info
->status
.rates
;
578 if (tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
) {
579 err
= wlc_prep_pdu(wlc
, p
, &fifo
);
581 WL_ERROR(("%s: AMPDU flag is off!\n", __func__
));
588 if (err
== BCME_BUSY
) {
589 WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc
->pub
->unit
, seq
));
590 WLCNTINCR(ampdu
->cnt
->sduretry
);
595 /* error in the packet; reject it */
596 WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc
->pub
->unit
, seq
));
597 WLCNTINCR(ampdu
->cnt
->sdurejected
);
603 /* pkt is good to be aggregated */
604 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
605 txh
= (d11txh_t
*) PKTDATA(p
);
606 plcp
= (u8
*) (txh
+ 1);
607 h
= (struct dot11_header
*)(plcp
+ D11_PHY_HDR_LEN
);
608 seq
= ltoh16(h
->seq
) >> SEQNUM_SHIFT
;
609 index
= TX_SEQ_TO_INDEX(seq
);
611 /* check mcl fields and test whether it can be agg'd */
612 mcl
= ltoh16(txh
->MacTxControlLow
);
613 mcl
&= ~TXC_AMPDU_MASK
;
614 fbr_iscck
= !(ltoh16(txh
->XtraFrameTypes
) & 0x3);
616 txh
->PreloadSize
= 0; /* always default to 0 */
618 /* Handle retry limits */
619 if (txrate
[0].count
<= rr_retry_limit
) {
630 /* extract the length info */
631 len
= fbr_iscck
? WLC_GET_CCK_PLCP_LEN(txh
->FragPLCPFallback
)
632 : WLC_GET_MIMO_PLCP_LEN(txh
->FragPLCPFallback
);
634 /* retrieve null delimiter count */
635 ndelim
= txh
->RTSPLCPFallback
[AMPDU_FBR_NULL_DELIM
];
638 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
639 wlc
->pub
->unit
, count
, len
));
642 * aggregateable mpdu. For ucode/hw agg,
643 * test whether need to break or change the epoch
647 mcl
|= (TXC_AMPDU_FIRST
<< TXC_AMPDU_SHIFT
);
648 /* refill the bits since might be a retx mpdu */
649 mcl
|= TXC_STARTMSDU
;
650 rts
= (struct dot11_rts_frame
*)&txh
->rts_frame
;
651 fc
= ltoh16(rts
->fc
);
652 if ((fc
& FC_KIND_MASK
) == FC_RTS
) {
656 if ((fc
& FC_KIND_MASK
) == FC_CTS
) {
661 mcl
|= (TXC_AMPDU_MIDDLE
<< TXC_AMPDU_SHIFT
);
662 mcl
&= ~(TXC_STARTMSDU
| TXC_SENDRTS
| TXC_SENDCTS
);
665 len
= roundup(len
, 4);
666 ampdu_len
+= (len
+ (ndelim
+ 1) * AMPDU_DELIMITER_LEN
);
668 dma_len
+= (u16
) pkttotlen(osh
, p
);
670 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
));
672 txh
->MacTxControlLow
= htol16(mcl
);
674 /* this packet is added */
677 /* patch the first MPDU */
679 u8 plcp0
, plcp3
, is40
, sgi
;
680 struct ieee80211_sta
*sta
;
682 sta
= tx_info
->control
.sta
;
688 plcp0
= txh
->FragPLCPFallback
[0];
689 plcp3
= txh
->FragPLCPFallback
[3];
692 is40
= (plcp0
& MIMO_PLCP_40MHZ
) ? 1 : 0;
693 sgi
= PLCP3_ISSGI(plcp3
) ? 1 : 0;
694 mcs
= plcp0
& ~MIMO_PLCP_40MHZ
;
695 ASSERT(mcs
< MCS_TABLE_SIZE
);
697 min(scb_ampdu
->max_rxlen
,
698 ampdu
->max_txlen
[mcs
][is40
][sgi
]);
700 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi
,
703 maxlen
= 64 * 1024; /* XXX Fix me to honor real max_rxlen */
707 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC
)
708 ? PHY_TXC1_BW_20MHZ_UP
: PHY_TXC1_BW_20MHZ
;
710 /* rebuild the rspec and rspec_fallback */
711 rspec
= RSPEC_MIMORATE
;
712 rspec
|= plcp
[0] & ~MIMO_PLCP_40MHZ
;
713 if (plcp
[0] & MIMO_PLCP_40MHZ
)
714 rspec
|= (PHY_TXC1_BW_40MHZ
<< RSPEC_BW_SHIFT
);
716 if (fbr_iscck
) /* CCK */
718 CCK_RSPEC(CCK_PHY2MAC_RATE
719 (txh
->FragPLCPFallback
[0]));
721 rspec_fallback
= RSPEC_MIMORATE
;
723 txh
->FragPLCPFallback
[0] & ~MIMO_PLCP_40MHZ
;
724 if (txh
->FragPLCPFallback
[0] & MIMO_PLCP_40MHZ
)
726 (PHY_TXC1_BW_40MHZ
<<
730 if (use_rts
|| use_cts
) {
732 wlc_rspec_to_rts_rspec(wlc
, rspec
, FALSE
,
735 wlc_rspec_to_rts_rspec(wlc
, rspec_fallback
,
736 FALSE
, mimo_ctlchbw
);
740 /* if (first mpdu for host agg) */
741 /* test whether to add more */
742 if ((MCS_RATE(mcs
, TRUE
, FALSE
) >= f
->dmaxferrate
) &&
743 (count
== f
->mcs2ampdu_table
[mcs
])) {
744 WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc
->pub
->unit
, count
, mcs
));
748 if (count
== scb_ampdu
->max_pdu
) {
749 WL_NONE(("Stop taking from q, reached %d deep\n",
750 scb_ampdu
->max_pdu
));
754 /* check to see if the next pkt is a candidate for aggregation */
755 p
= pktq_ppeek(&qi
->q
, prec
);
756 tx_info
= IEEE80211_SKB_CB(p
); /* tx_info must be checked with current p */
759 if ((tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
) &&
760 ((u8
) PKTPRIO(p
) == tid
)) {
763 pkttotlen(osh
, p
) + AMPDU_MAX_MPDU_OVERHEAD
;
764 plen
= max(scb_ampdu
->min_len
, plen
);
766 if ((plen
+ ampdu_len
) > maxlen
) {
768 WL_ERROR(("%s: Bogus plen #1\n",
774 /* check if there are enough descriptors available */
775 if (TXAVAIL(wlc
, fifo
) <= (seg_cnt
+ 1)) {
776 WL_ERROR(("%s: No fifo space !!!!!!\n", __func__
));
780 p
= pktq_pdeq(&qi
->q
, prec
);
788 ini
->tx_in_transit
+= count
;
791 WLCNTADD(ampdu
->cnt
->txmpdu
, count
);
793 /* patch up the last txh */
794 txh
= (d11txh_t
*) PKTDATA(pkt
[count
- 1]);
795 mcl
= ltoh16(txh
->MacTxControlLow
);
796 mcl
&= ~TXC_AMPDU_MASK
;
797 mcl
|= (TXC_AMPDU_LAST
<< TXC_AMPDU_SHIFT
);
798 txh
->MacTxControlLow
= htol16(mcl
);
800 /* remove the null delimiter after last mpdu */
801 ndelim
= txh
->RTSPLCPFallback
[AMPDU_FBR_NULL_DELIM
];
802 txh
->RTSPLCPFallback
[AMPDU_FBR_NULL_DELIM
] = 0;
803 ampdu_len
-= ndelim
* AMPDU_DELIMITER_LEN
;
805 /* remove the pad len from last mpdu */
806 fbr_iscck
= ((ltoh16(txh
->XtraFrameTypes
) & 0x3) == 0);
807 len
= fbr_iscck
? WLC_GET_CCK_PLCP_LEN(txh
->FragPLCPFallback
)
808 : WLC_GET_MIMO_PLCP_LEN(txh
->FragPLCPFallback
);
809 ampdu_len
-= roundup(len
, 4) - len
;
811 /* patch up the first txh & plcp */
812 txh
= (d11txh_t
*) PKTDATA(pkt
[0]);
813 plcp
= (u8
*) (txh
+ 1);
815 WLC_SET_MIMO_PLCP_LEN(plcp
, ampdu_len
);
816 /* mark plcp to indicate ampdu */
817 WLC_SET_MIMO_PLCP_AMPDU(plcp
);
819 /* reset the mixed mode header durations */
822 wlc_calc_lsig_len(wlc
, rspec
, ampdu_len
);
823 txh
->MModeLen
= htol16(mmodelen
);
824 preamble_type
= WLC_MM_PREAMBLE
;
826 if (txh
->MModeFbrLen
) {
828 wlc_calc_lsig_len(wlc
, rspec_fallback
, ampdu_len
);
829 txh
->MModeFbrLen
= htol16(mmfbrlen
);
830 fbr_preamble_type
= WLC_MM_PREAMBLE
;
833 /* set the preload length */
834 if (MCS_RATE(mcs
, TRUE
, FALSE
) >= f
->dmaxferrate
) {
835 dma_len
= min(dma_len
, f
->ampdu_pld_size
);
836 txh
->PreloadSize
= htol16(dma_len
);
838 txh
->PreloadSize
= 0;
840 mch
= ltoh16(txh
->MacTxControlHigh
);
842 /* update RTS dur fields */
843 if (use_rts
|| use_cts
) {
845 rts
= (struct dot11_rts_frame
*)&txh
->rts_frame
;
846 if ((mch
& TXC_PREAMBLE_RTS_MAIN_SHORT
) ==
847 TXC_PREAMBLE_RTS_MAIN_SHORT
)
848 rts_preamble_type
= WLC_SHORT_PREAMBLE
;
850 if ((mch
& TXC_PREAMBLE_RTS_FB_SHORT
) ==
851 TXC_PREAMBLE_RTS_FB_SHORT
)
852 rts_fbr_preamble_type
= WLC_SHORT_PREAMBLE
;
855 wlc_compute_rtscts_dur(wlc
, use_cts
, rts_rspec
,
856 rspec
, rts_preamble_type
,
857 preamble_type
, ampdu_len
,
859 rts
->durid
= htol16(durid
);
860 durid
= wlc_compute_rtscts_dur(wlc
, use_cts
,
863 rts_fbr_preamble_type
,
866 txh
->RTSDurFallback
= htol16(durid
);
867 /* set TxFesTimeNormal */
868 txh
->TxFesTimeNormal
= rts
->durid
;
869 /* set fallback rate version of TxFesTimeNormal */
870 txh
->TxFesTimeFallback
= txh
->RTSDurFallback
;
873 /* set flag and plcp for fallback rate */
875 WLCNTADD(ampdu
->cnt
->txfbr_mpdu
, count
);
876 WLCNTINCR(ampdu
->cnt
->txfbr_ampdu
);
877 mch
|= TXC_AMPDU_FBR
;
878 txh
->MacTxControlHigh
= htol16(mch
);
879 WLC_SET_MIMO_PLCP_AMPDU(plcp
);
880 WLC_SET_MIMO_PLCP_AMPDU(txh
->FragPLCPFallback
);
883 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
884 wlc
->pub
->unit
, count
, ampdu_len
));
886 /* inform rate_sel if it this is a rate probe pkt */
887 frameid
= ltoh16(txh
->TxFrameID
);
888 if (frameid
& TXFID_RATE_PROBE_MASK
) {
889 WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__
));
892 if (wlc
->rpc_agg
& BCM_RPC_TP_HOST_AGG_AMPDU
)
893 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc
->rpc
),
894 BCM_RPC_TP_HOST_AGG_AMPDU
, TRUE
);
896 for (i
= 0; i
< count
; i
++)
897 wlc_txfifo(wlc
, fifo
, pkt
[i
], i
== (count
- 1),
898 ampdu
->txpkt_weight
);
900 if (wlc
->rpc_agg
& BCM_RPC_TP_HOST_AGG_AMPDU
)
901 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc
->rpc
),
902 BCM_RPC_TP_HOST_AGG_AMPDU
, FALSE
);
911 wlc_ampdu_dotxstatus(ampdu_info_t
*ampdu
, struct scb
*scb
, void *p
,
914 scb_ampdu_t
*scb_ampdu
;
915 wlc_info_t
*wlc
= ampdu
->wlc
;
916 scb_ampdu_tid_ini_t
*ini
;
918 struct ieee80211_tx_info
*tx_info
;
920 tx_info
= IEEE80211_SKB_CB(p
);
921 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
923 ASSERT(scb
->magic
== SCB_MAGIC
);
924 ASSERT(txs
->status
& TX_STATUS_AMPDU
);
925 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
927 ini
= SCB_AMPDU_INI(scb_ampdu
, PKTPRIO(p
));
928 ASSERT(ini
->scb
== scb
);
930 /* BMAC_NOTE: For the split driver, second level txstatus comes later
931 * So if the ACK was received then wait for the second level else just
934 if (txs
->status
& TX_STATUS_ACK_RCV
) {
938 /* wait till the next 8 bytes of txstatus is available */
941 &wlc
->regs
->frmtxstatus
)) & TXS_V
) == 0) {
944 if (status_delay
> 10) {
945 ASSERT(status_delay
<= 10);
950 ASSERT(!(s1
& TX_STATUS_INTERMEDIATE
));
951 ASSERT(s1
& TX_STATUS_AMPDU
);
952 s2
= R_REG(wlc
->osh
, &wlc
->regs
->frmtxstatus2
);
955 /* Store the relevant information in ampdu structure */
956 WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
961 bcopy(txs
, &du
->txs
, sizeof(tx_status_t
));
962 ampdu
->waiting_status
= TRUE
;
967 wlc_ampdu_dotxstatus_complete(ampdu
, scb
, p
, txs
, s1
, s2
);
968 wlc_ampdu_txflowcontrol(wlc
, scb_ampdu
, ini
);
972 void wlc_ampdu_txstatus_complete(ampdu_info_t
*ampdu
, u32 s1
, u32 s2
)
974 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
));
976 ASSERT(ampdu
->waiting_status
);
978 /* The packet may have been freed if the SCB went away, if so, then still free the
982 struct ieee80211_tx_info
*tx_info
;
985 tx_info
= IEEE80211_SKB_CB(ampdu
->p
);
986 scb
= (struct scb
*)tx_info
->control
.sta
->drv_priv
;
988 wlc_ampdu_dotxstatus_complete(ampdu
, scb
, ampdu
->p
, &du
->txs
,
993 ampdu
->waiting_status
= FALSE
;
995 #endif /* WLC_HIGH_ONLY */
996 void rate_status(wlc_info_t
*wlc
, struct ieee80211_tx_info
*tx_info
,
997 tx_status_t
*txs
, u8 mcs
);
1000 rate_status(wlc_info_t
*wlc
, struct ieee80211_tx_info
*tx_info
,
1001 tx_status_t
*txs
, u8 mcs
)
1003 struct ieee80211_tx_rate
*txrate
= tx_info
->status
.rates
;
1006 /* clear the rest of the rates */
1007 for (i
= 2; i
< IEEE80211_TX_MAX_RATES
; i
++) {
1009 txrate
[i
].count
= 0;
1013 extern void wlc_txq_enq(wlc_info_t
*wlc
, struct scb
*scb
, void *sdu
,
1016 #define SHORTNAME "AMPDU status"
1018 static void BCMFASTPATH
1019 wlc_ampdu_dotxstatus_complete(ampdu_info_t
*ampdu
, struct scb
*scb
, void *p
,
1020 tx_status_t
*txs
, u32 s1
, u32 s2
)
1022 scb_ampdu_t
*scb_ampdu
;
1023 wlc_info_t
*wlc
= ampdu
->wlc
;
1024 scb_ampdu_tid_ini_t
*ini
;
1025 u8 bitmap
[8], queue
, tid
;
1028 struct dot11_header
*h
;
1029 u16 seq
, start_seq
= 0, bindex
, index
, mcl
;
1031 bool ba_recd
= FALSE
, ack_recd
= FALSE
;
1032 u8 suc_mpdu
= 0, tot_mpdu
= 0;
1034 bool update_rate
= TRUE
, retry
= TRUE
, tx_error
= FALSE
;
1037 u8 retry_limit
, rr_retry_limit
;
1038 struct ieee80211_tx_info
*tx_info
= IEEE80211_SKB_CB(p
);
1041 u8 hole
[AMPDU_MAX_MPDU
];
1042 bzero(hole
, sizeof(hole
));
1045 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
1046 ASSERT(txs
->status
& TX_STATUS_AMPDU
);
1048 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
1051 tid
= (u8
) PKTPRIO(p
);
1053 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1054 retry_limit
= ampdu
->retry_limit_tid
[tid
];
1055 rr_retry_limit
= ampdu
->rr_retry_limit_tid
[tid
];
1057 ASSERT(ini
->scb
== scb
);
1059 bzero(bitmap
, sizeof(bitmap
));
1060 queue
= txs
->frameid
& TXFID_QUEUE_MASK
;
1061 ASSERT(queue
< AC_COUNT
);
1063 supr_status
= txs
->status
& TX_STATUS_SUPR_MASK
;
1065 if (txs
->status
& TX_STATUS_ACK_RCV
) {
1066 if (TX_STATUS_SUPR_UF
== supr_status
) {
1067 update_rate
= FALSE
;
1070 ASSERT(txs
->status
& TX_STATUS_INTERMEDIATE
);
1071 start_seq
= txs
->sequence
>> SEQNUM_SHIFT
;
1072 bitmap
[0] = (txs
->status
& TX_STATUS_BA_BMAP03_MASK
) >>
1073 TX_STATUS_BA_BMAP03_SHIFT
;
1075 ASSERT(!(s1
& TX_STATUS_INTERMEDIATE
));
1076 ASSERT(s1
& TX_STATUS_AMPDU
);
1079 (s1
& TX_STATUS_BA_BMAP47_MASK
) <<
1080 TX_STATUS_BA_BMAP47_SHIFT
;
1081 bitmap
[1] = (s1
>> 8) & 0xff;
1082 bitmap
[2] = (s1
>> 16) & 0xff;
1083 bitmap
[3] = (s1
>> 24) & 0xff;
1085 bitmap
[4] = s2
& 0xff;
1086 bitmap
[5] = (s2
>> 8) & 0xff;
1087 bitmap
[6] = (s2
>> 16) & 0xff;
1088 bitmap
[7] = (s2
>> 24) & 0xff;
1092 WLCNTINCR(ampdu
->cnt
->noba
);
1094 update_rate
= FALSE
;
1095 if (supr_status
== TX_STATUS_SUPR_BADCH
) {
1096 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__
, CHSPEC_CHANNEL(wlc
->default_bss
->chanspec
)));
1098 if (supr_status
== TX_STATUS_SUPR_FRAG
)
1099 WL_NONE(("%s: AMPDU frag err\n",
1102 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__
, supr_status
));
1104 /* no need to retry for badch; will fail again */
1105 if (supr_status
== TX_STATUS_SUPR_BADCH
||
1106 supr_status
== TX_STATUS_SUPR_EXPTIME
) {
1108 WLCNTINCR(wlc
->pub
->_cnt
->txchanrej
);
1109 } else if (supr_status
== TX_STATUS_SUPR_EXPTIME
) {
1111 WLCNTINCR(wlc
->pub
->_cnt
->txexptime
);
1113 /* TX underflow : try tuning pre-loading or ampdu size */
1114 } else if (supr_status
== TX_STATUS_SUPR_FRAG
) {
1115 /* if there were underflows, but pre-loading is not active,
1116 notify rate adaptation.
1118 if (wlc_ffpld_check_txfunfl(wlc
, prio2fifo
[tid
])
1121 #ifdef WLC_HIGH_ONLY
1122 /* With BMAC, TX Underflows should not happen */
1123 WL_ERROR(("wl%d: BMAC TX Underflow?",
1128 } else if (txs
->phyerr
) {
1129 update_rate
= FALSE
;
1130 WLCNTINCR(wlc
->pub
->_cnt
->txphyerr
);
1131 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc
->pub
->unit
, txs
->phyerr
));
1134 if (WL_ERROR_ON()) {
1135 prpkt("txpkt (AMPDU)", wlc
->osh
, p
);
1136 wlc_print_txdesc((d11txh_t
*) PKTDATA(p
));
1137 wlc_print_txstatus(txs
);
1143 /* loop through all pkts and retry if not acked */
1145 tx_info
= IEEE80211_SKB_CB(p
);
1146 ASSERT(tx_info
->flags
& IEEE80211_TX_CTL_AMPDU
);
1147 txh
= (d11txh_t
*) PKTDATA(p
);
1148 mcl
= ltoh16(txh
->MacTxControlLow
);
1149 plcp
= (u8
*) (txh
+ 1);
1150 h
= (struct dot11_header
*)(plcp
+ D11_PHY_HDR_LEN
);
1151 seq
= ltoh16(h
->seq
) >> SEQNUM_SHIFT
;
1153 if (tot_mpdu
== 0) {
1154 mcs
= plcp
[0] & MIMO_PLCP_MCS_MASK
;
1155 mimoantsel
= ltoh16(txh
->ABI_MimoAntSel
);
1158 index
= TX_SEQ_TO_INDEX(seq
);
1161 bindex
= MODSUB_POW2(seq
, start_seq
, SEQNUM_MAX
);
1163 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1164 "bindex is %d set %d, index %d\n",
1165 __func__
, tid
, seq
, start_seq
, bindex
,
1166 isset(bitmap
, bindex
), index
));
1168 /* if acked then clear bit and free packet */
1169 if ((bindex
< AMPDU_TX_BA_MAX_WSIZE
)
1170 && isset(bitmap
, bindex
)) {
1171 ini
->tx_in_transit
--;
1172 ini
->txretry
[index
] = 0;
1174 /* ampdu_ack_len: number of acked aggregated frames */
1175 /* ampdu_ack_map: block ack bit map for the aggregation */
1176 /* ampdu_len: number of aggregated frames */
1177 rate_status(wlc
, tx_info
, txs
, mcs
);
1178 tx_info
->flags
|= IEEE80211_TX_STAT_ACK
;
1179 tx_info
->flags
|= IEEE80211_TX_STAT_AMPDU
;
1181 /* XXX TODO: Make these accurate. */
1182 tx_info
->status
.ampdu_ack_len
=
1184 status
& TX_STATUS_FRM_RTX_MASK
) >>
1185 TX_STATUS_FRM_RTX_SHIFT
;
1186 tx_info
->status
.ampdu_len
=
1188 status
& TX_STATUS_FRM_RTX_MASK
) >>
1189 TX_STATUS_FRM_RTX_SHIFT
;
1191 PKTPULL(p
, D11_PHY_HDR_LEN
);
1192 PKTPULL(p
, D11_TXH_LEN
);
1194 ieee80211_tx_status_irqsafe(wlc
->pub
->ieee_hw
,
1200 /* either retransmit or send bar if ack not recd */
1202 struct ieee80211_tx_rate
*txrate
=
1203 tx_info
->status
.rates
;
1204 if (retry
&& (txrate
[0].count
< (int)retry_limit
)) {
1205 ini
->txretry
[index
]++;
1206 ini
->tx_in_transit
--;
1207 /* Use high prededence for retransmit to give some punch */
1208 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1209 wlc_txq_enq(wlc
, scb
, p
,
1210 WLC_PRIO_TO_HI_PREC(tid
));
1213 ini
->tx_in_transit
--;
1214 ieee80211_tx_info_clear_status(tx_info
);
1216 IEEE80211_TX_STAT_AMPDU_NO_BACK
;
1217 PKTPULL(p
, D11_PHY_HDR_LEN
);
1218 PKTPULL(p
, D11_TXH_LEN
);
1219 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME
, seq
, ini
->tx_in_transit
));
1220 ieee80211_tx_status_irqsafe(wlc
->pub
->ieee_hw
,
1226 /* break out if last packet of ampdu */
1227 if (((mcl
& TXC_AMPDU_MASK
) >> TXC_AMPDU_SHIFT
) ==
1231 p
= GETNEXTTXP(wlc
, queue
);
1237 wlc_send_q(wlc
, wlc
->active_queue
);
1239 /* update rate state */
1240 if (WLANTSEL_ENAB(wlc
))
1241 antselid
= wlc_antsel_antsel2id(wlc
->asi
, mimoantsel
);
1243 wlc_txfifo_complete(wlc
, queue
, ampdu
->txpkt_weight
);
1247 ampdu_cleanup_tid_ini(ampdu_info_t
*ampdu
, scb_ampdu_t
*scb_ampdu
, u8 tid
,
1250 scb_ampdu_tid_ini_t
*ini
;
1251 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1255 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1256 ampdu
->wlc
->pub
->unit
, tid
));
1258 if (ini
->tx_in_transit
&& !force
)
1261 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, ini
->scb
);
1262 ASSERT(ini
== &scb_ampdu
->ini
[ini
->tid
]);
1264 /* free all buffered tx packets */
1265 pktq_pflush(ampdu
->wlc
->osh
, &scb_ampdu
->txq
, ini
->tid
, TRUE
, NULL
, 0);
1268 /* initialize the initiator code for tid */
1269 static scb_ampdu_tid_ini_t
*wlc_ampdu_init_tid_ini(ampdu_info_t
*ampdu
,
1270 scb_ampdu_t
*scb_ampdu
,
1271 u8 tid
, bool override
)
1273 scb_ampdu_tid_ini_t
*ini
;
1276 ASSERT(scb_ampdu
->scb
);
1277 ASSERT(SCB_AMPDU(scb_ampdu
->scb
));
1278 ASSERT(tid
< AMPDU_MAX_SCB_TID
);
1280 /* check for per-tid control of ampdu */
1281 if (!ampdu
->ini_enable
[tid
]) {
1282 WL_ERROR(("%s: Rejecting tid %d\n", __func__
, tid
));
1286 ini
= SCB_AMPDU_INI(scb_ampdu
, tid
);
1288 ini
->scb
= scb_ampdu
->scb
;
1289 ini
->magic
= INI_MAGIC
;
1290 WLCNTINCR(ampdu
->cnt
->txaddbareq
);
1295 int wlc_ampdu_set(ampdu_info_t
*ampdu
, bool on
)
1297 wlc_info_t
*wlc
= ampdu
->wlc
;
1299 wlc
->pub
->_ampdu
= FALSE
;
1302 if (!N_ENAB(wlc
->pub
)) {
1303 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1305 return BCME_UNSUPPORTED
;
1307 if (!wlc_ampdu_cap(ampdu
)) {
1308 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1310 return BCME_UNSUPPORTED
;
1312 wlc
->pub
->_ampdu
= on
;
1318 bool wlc_ampdu_cap(ampdu_info_t
*ampdu
)
1320 if (WLC_PHY_11N_CAP(ampdu
->wlc
->band
))
1326 static void ampdu_update_max_txlen(ampdu_info_t
*ampdu
, u8 dur
)
1330 for (mcs
= 0; mcs
< MCS_TABLE_SIZE
; mcs
++) {
1331 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1333 rate
= MCS_RATE(mcs
, FALSE
, FALSE
);
1334 ampdu
->max_txlen
[mcs
][0][0] = (rate
* dur
) >> 3;
1335 /* 40 MHz, No SGI */
1336 rate
= MCS_RATE(mcs
, TRUE
, FALSE
);
1337 ampdu
->max_txlen
[mcs
][1][0] = (rate
* dur
) >> 3;
1339 rate
= MCS_RATE(mcs
, FALSE
, TRUE
);
1340 ampdu
->max_txlen
[mcs
][0][1] = (rate
* dur
) >> 3;
1342 rate
= MCS_RATE(mcs
, TRUE
, TRUE
);
1343 ampdu
->max_txlen
[mcs
][1][1] = (rate
* dur
) >> 3;
1348 wlc_ampdu_null_delim_cnt(ampdu_info_t
*ampdu
, struct scb
*scb
,
1349 ratespec_t rspec
, int phylen
)
1351 scb_ampdu_t
*scb_ampdu
;
1352 int bytes
, cnt
, tmp
;
1356 ASSERT(SCB_AMPDU(scb
));
1358 scb_ampdu
= SCB_AMPDU_CUBBY(ampdu
, scb
);
1361 if (scb_ampdu
->mpdu_density
== 0)
1364 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1365 density x is in 2^(x-4) usec
1366 ==> # of bytes needed for req density = rate/2^(17-x)
1367 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1370 tx_density
= scb_ampdu
->mpdu_density
;
1372 ASSERT(tx_density
<= AMPDU_MAX_MPDU_DENSITY
);
1373 tmp
= 1 << (17 - tx_density
);
1374 bytes
= CEIL(RSPEC2RATE(rspec
), tmp
);
1376 if (bytes
> phylen
) {
1377 cnt
= CEIL(bytes
- phylen
, AMPDU_DELIMITER_LEN
);
1384 void wlc_ampdu_macaddr_upd(wlc_info_t
*wlc
)
1386 char template[T_RAM_ACCESS_SZ
* 2];
1388 /* driver needs to write the ta in the template; ta is at offset 16 */
1389 bzero(template, sizeof(template));
1390 bcopy((char *)wlc
->pub
->cur_etheraddr
.octet
, template, ETHER_ADDR_LEN
);
1391 wlc_write_template_ram(wlc
, (T_BA_TPL_BASE
+ 16), (T_RAM_ACCESS_SZ
* 2),
1395 bool wlc_aggregatable(wlc_info_t
*wlc
, u8 tid
)
1397 return wlc
->ampdu
->ini_enable
[tid
];
1400 void wlc_ampdu_shm_upd(ampdu_info_t
*ampdu
)
1402 wlc_info_t
*wlc
= ampdu
->wlc
;
1404 /* Extend ucode internal watchdog timer to match larger received frames */
1405 if ((ampdu
->rx_factor
& HT_PARAMS_RX_FACTOR_MASK
) ==
1406 AMPDU_RX_FACTOR_64K
) {
1407 wlc_write_shm(wlc
, M_MIMO_MAXSYM
, MIMO_MAXSYM_MAX
);
1408 wlc_write_shm(wlc
, M_WATCHDOG_8TU
, WATCHDOG_8TU_MAX
);
1410 wlc_write_shm(wlc
, M_MIMO_MAXSYM
, MIMO_MAXSYM_DEF
);
1411 wlc_write_shm(wlc
, M_WATCHDOG_8TU
, WATCHDOG_8TU_DEF
);