blob: a47cb981d30ae680569da35371582a1f77d02664 [file] [log] [blame]
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
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.
7 *
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.
15 */
16
17#include <wlc_cfg.h>
18#include <typedefs.h>
19#include <bcmdefs.h>
20#include <osl.h>
21#include <bcmutils.h>
22#include <siutils.h>
23#include <bcmendian.h>
24#include <proto/802.11.h>
25#include <wlioctl.h>
26#include <sbhndpio.h>
27#include <sbhnddma.h>
28#include <hnddma.h>
29#include <d11.h>
30#include <wlc_rate.h>
31#include <wlc_pub.h>
32#include <wlc_key.h>
33#include <wlc_bsscfg.h>
34#include <wlc_mac80211.h>
35#include <wlc_phy_hal.h>
36#include <wlc_antsel.h>
37#include <wlc_scb.h>
38#include <net/mac80211.h>
39#include <wlc_ampdu.h>
40#include <wl_export.h>
41
42#ifdef WLC_HIGH_ONLY
43#include <bcm_rpc_tp.h>
44#include <wlc_rpctx.h>
45#endif
46
47#define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
48#define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
49#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
50#define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
51#define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
52#define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
53#define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
54#define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
55#define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
56#define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
57#define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
58#define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
59#define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
60
61#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
62#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
63 * without underflows
64 */
65#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
66#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
67#define FFPLD_PLD_INCR 1000 /* increments in bytes */
68#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
69 * accumulate between resets.
70 */
71
72#define TX_SEQ_TO_INDEX(seq) (seq) % AMPDU_TX_BA_MAX_WSIZE
73
74/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
75#define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
76 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
77
78#ifdef BCMDBG
79uint32 wl_ampdu_dbg =
80 WL_AMPDU_UPDN_VAL |
81 WL_AMPDU_ERR_VAL |
82 WL_AMPDU_TX_VAL |
83 WL_AMPDU_RX_VAL |
84 WL_AMPDU_CTL_VAL |
85 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
86#endif
87
88/* structure to hold tx fifo information and pre-loading state
89 * counters specific to tx underflows of ampdus
90 * some counters might be redundant with the ones in wlc or ampdu structures.
91 * This allows to maintain a specific state independantly of
92 * how often and/or when the wlc counters are updated.
93 */
94typedef struct wlc_fifo_info {
95 uint16 ampdu_pld_size; /* number of bytes to be pre-loaded */
96 uint8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
97 uint16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
98 uint32 accum_txfunfl; /* num of underflows since we modified pld params */
99 uint32 accum_txampdu; /* num of tx ampdu since we modified pld params */
100 uint32 prev_txampdu; /* previous reading of tx ampdu */
101 uint32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
102} wlc_fifo_info_t;
103
104/* AMPDU module specific state */
105struct ampdu_info {
106 wlc_info_t *wlc; /* pointer to main wlc structure */
107 int scb_handle; /* scb cubby handle to retrieve data from scb */
108 uint8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
109 uint8 ba_tx_wsize; /* Tx ba window size (in pdu) */
110 uint8 ba_rx_wsize; /* Rx ba window size (in pdu) */
111 uint8 retry_limit; /* mpdu transmit retry limit */
112 uint8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
113 uint8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
114 /* per-tid mpdu transmit retry limit at regular rate */
115 uint8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
116 uint8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
117 int8 max_pdu; /* max pdus allowed in ampdu */
118 uint8 dur; /* max duration of an ampdu (in msec) */
119 uint8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
120 uint8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
121 uint32 ffpld_rsvd; /* number of bytes to reserve for preload */
122 uint32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
123 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
124 bool mfbr; /* enable multiple fallback rate */
125 uint32 tx_max_funl; /* underflows should be kept such that
126 * (tx_max_funfl*underflows) < tx frames
127 */
128 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
129
130#ifdef WLC_HIGH_ONLY
131 void *p;
132 tx_status_t txs;
133 bool waiting_status; /* To help sanity checks */
134#endif
135};
136
137#define AMPDU_CLEANUPFLAG_RX (0x1)
138#define AMPDU_CLEANUPFLAG_TX (0x2)
139
140#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
141#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
142
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400143static void wlc_ffpld_init(ampdu_info_t *ampdu);
144static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int f);
145static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700146
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400147static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
148 scb_ampdu_t *scb_ampdu,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700149 uint8 tid, bool override);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400150static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700151 uint8 tid, bool force);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400152static void ampdu_update_max_txlen(ampdu_info_t *ampdu, uint8 dur);
153static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
154static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700155
156#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
157
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400158static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
159 void *p, tx_status_t *txs,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700160 uint32 frmtxstatus,
161 uint32 frmtxstatus2);
162
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400163static INLINE uint16 pkt_txh_seqnum(wlc_info_t *wlc, void *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700164{
165 d11txh_t *txh;
166 struct dot11_header *h;
167 txh = (d11txh_t *) PKTDATA(p);
168 h = (struct dot11_header *)((uint8 *) (txh + 1) + D11_PHY_HDR_LEN);
Jason Cooper90ea2292010-09-14 09:45:32 -0400169 return ltoh16(h->seq) >> SEQNUM_SHIFT;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700170}
171
Jason Coopera2627bc2010-09-14 09:45:31 -0400172ampdu_info_t *BCMATTACHFN(wlc_ampdu_attach) (wlc_info_t *wlc)
173{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700174 ampdu_info_t *ampdu;
175 int i;
176
177 /* some code depends on packed structures */
178 ASSERT(sizeof(struct dot11_bar) == DOT11_BAR_LEN);
179 ASSERT(sizeof(struct dot11_ba) == DOT11_BA_LEN + DOT11_BA_BITMAP_LEN);
180 ASSERT(sizeof(struct dot11_ctl_header) == DOT11_CTL_HDR_LEN);
181 ASSERT(sizeof(struct dot11_addba_req) == DOT11_ADDBA_REQ_LEN);
182 ASSERT(sizeof(struct dot11_addba_resp) == DOT11_ADDBA_RESP_LEN);
183 ASSERT(sizeof(struct dot11_delba) == DOT11_DELBA_LEN);
184 ASSERT(DOT11_MAXNUMFRAGS == NBITS(uint16));
185 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
186 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
187 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
188 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
189
Jason Cooperca8c1e52010-09-14 09:45:33 -0400190 ampdu = (ampdu_info_t *) MALLOC(wlc->osh, sizeof(ampdu_info_t));
191 if (!ampdu) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700192 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem, malloced %d bytes\n", wlc->pub->unit, MALLOCED(wlc->osh)));
193 return NULL;
194 }
195 bzero((char *)ampdu, sizeof(ampdu_info_t));
196 ampdu->wlc = wlc;
197
198 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
199 ampdu->ini_enable[i] = TRUE;
200 /* Disable ampdu for VO by default */
201 ampdu->ini_enable[PRIO_8021D_VO] = FALSE;
202 ampdu->ini_enable[PRIO_8021D_NC] = FALSE;
203
204 /* Disable ampdu for BK by default since not enough fifo space */
205 ampdu->ini_enable[PRIO_8021D_NONE] = FALSE;
206 ampdu->ini_enable[PRIO_8021D_BK] = FALSE;
207
208 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
209 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
210 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
211 ampdu->max_pdu = AUTO;
212 ampdu->dur = AMPDU_MAX_DUR;
213 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
214
215 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
216 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
217 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
218 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
219 else
220 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
221#ifdef WLC_HIGH_ONLY
222 /* Restrict to smaller rcv size for BMAC dongle */
223 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
224#endif
225 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
226 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
227
228 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
229 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
230 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
231 }
232
233 ampdu_update_max_txlen(ampdu, ampdu->dur);
234 ampdu->mfbr = FALSE;
235 /* try to set ampdu to the default value */
236 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
237
238 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
239 wlc_ffpld_init(ampdu);
240
241 return ampdu;
242}
243
Jason Coopera2627bc2010-09-14 09:45:31 -0400244void BCMATTACHFN(wlc_ampdu_detach) (ampdu_info_t *ampdu)
245{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700246 int i;
247
248 if (!ampdu)
249 return;
250
251 /* free all ini's which were to be freed on callbacks which were never called */
252 for (i = 0; i < AMPDU_INI_FREE; i++) {
253 if (ampdu->ini_free[i]) {
254 MFREE(ampdu->wlc->osh, ampdu->ini_free[i],
255 sizeof(scb_ampdu_tid_ini_t));
256 }
257 }
258
259 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
260 MFREE(ampdu->wlc->osh, ampdu, sizeof(ampdu_info_t));
261}
262
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400263void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700264{
265 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
266 uint8 tid;
267
268 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
269 ASSERT(scb_ampdu);
270
271 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
272 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, FALSE);
273 }
274}
275
276/* reset the ampdu state machine so that it can gracefully handle packets that were
277 * freed from the dma and tx queues during reinit
278 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400279void wlc_ampdu_reset(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700280{
281 WL_NONE(("%s: Entering\n", __func__));
282}
283
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400284static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700285{
286 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
287 int i;
288
289 scb_ampdu->max_pdu = (uint8) ampdu->wlc->pub->tunables->ampdunummpdu;
290
291 /* go back to legacy size if some preloading is occuring */
292 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
293 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
294 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
295 }
296
297 /* apply user override */
298 if (ampdu->max_pdu != AUTO)
299 scb_ampdu->max_pdu = (uint8) ampdu->max_pdu;
300
301 scb_ampdu->release = MIN(scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
302
303 if (scb_ampdu->max_rxlen)
304 scb_ampdu->release =
305 MIN(scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
306
307 scb_ampdu->release = MIN(scb_ampdu->release,
308 ampdu->fifo_tb[TX_AC_BE_FIFO].
309 mcs2ampdu_table[FFPLD_MAX_MCS]);
310
311 ASSERT(scb_ampdu->release);
312}
313
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400314void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700315{
316 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
317}
318
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400319static void wlc_ffpld_init(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700320{
321 int i, j;
322 wlc_fifo_info_t *fifo;
323
324 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
325 fifo = (ampdu->fifo_tb + j);
326 fifo->ampdu_pld_size = 0;
327 for (i = 0; i <= FFPLD_MAX_MCS; i++)
328 fifo->mcs2ampdu_table[i] = 255;
329 fifo->dmaxferrate = 0;
330 fifo->accum_txampdu = 0;
331 fifo->prev_txfunfl = 0;
332 fifo->accum_txfunfl = 0;
333
334 }
335}
336
337/* evaluate the dma transfer rate using the tx underflows as feedback.
338 * If necessary, increase tx fifo preloading. If not enough,
339 * decrease maximum ampdu size for each mcs till underflows stop
340 * Return 1 if pre-loading not active, -1 if not an underflow event,
341 * 0 if pre-loading module took care of the event.
342 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400343static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int fid)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700344{
345 ampdu_info_t *ampdu = wlc->ampdu;
346 uint32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, TRUE, FALSE);
347 uint32 txunfl_ratio;
348 uint8 max_mpdu;
349 uint32 current_ampdu_cnt = 0;
350 uint16 max_pld_size;
351 uint32 new_txunfl;
352 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
353 uint xmtfifo_sz;
354 uint16 cur_txunfl;
355
356 /* return if we got here for a different reason than underflows */
357 cur_txunfl =
358 wlc_read_shm(wlc,
359 M_UCODE_MACSTAT + OFFSETOF(macstat_t, txfunfl[fid]));
360 new_txunfl = (uint16) (cur_txunfl - fifo->prev_txfunfl);
361 if (new_txunfl == 0) {
362 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
363 return -1;
364 }
365 fifo->prev_txfunfl = cur_txunfl;
366
367 if (!ampdu->tx_max_funl)
368 return 1;
369
370 /* check if fifo is big enough */
371 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
372 WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
373 return -1;
374 }
375
376 if ((TXFIFO_SIZE_UNIT * (uint32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
377 return 1;
378
379 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
380 fifo->accum_txfunfl += new_txunfl;
381
382 /* we need to wait for at least 10 underflows */
383 if (fifo->accum_txfunfl < 10)
384 return 0;
385
386 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
387 current_ampdu_cnt, fifo->accum_txfunfl));
388
389 /*
390 compute the current ratio of tx unfl per ampdu.
391 When the current ampdu count becomes too
392 big while the ratio remains small, we reset
393 the current count in order to not
394 introduce too big of a latency in detecting a
395 large amount of tx underflows later.
396 */
397
398 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
399
400 if (txunfl_ratio > ampdu->tx_max_funl) {
401 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
402 fifo->accum_txfunfl = 0;
403 }
404 return 0;
405 }
406 max_mpdu =
407 MIN(fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
408
409 /* In case max value max_pdu is already lower than
410 the fifo depth, there is nothing more we can do.
411 */
412
413 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
414 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
415 fifo->accum_txfunfl = 0;
416 return 0;
417 }
418
419 if (fifo->ampdu_pld_size < max_pld_size) {
420
421 /* increment by TX_FIFO_PLD_INC bytes */
422 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
423 if (fifo->ampdu_pld_size > max_pld_size)
424 fifo->ampdu_pld_size = max_pld_size;
425
426 /* update scb release size */
427 scb_ampdu_update_config_all(ampdu);
428
429 /*
430 compute a new dma xfer rate for max_mpdu @ max mcs.
431 This is the minimum dma rate that
432 can acheive no unferflow condition for the current mpdu size.
433 */
434 /* note : we divide/multiply by 100 to avoid integer overflows */
435 fifo->dmaxferrate =
436 (((phy_rate / 100) *
437 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
438 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
439
440 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
441 fifo->dmaxferrate, fifo->ampdu_pld_size));
442 } else {
443
444 /* decrease ampdu size */
445 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
446 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
447 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
448 AMPDU_NUM_MPDU_LEGACY - 1;
449 else
450 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
451
452 /* recompute the table */
453 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
454
455 /* update scb release size */
456 scb_ampdu_update_config_all(ampdu);
457 }
458 }
459 fifo->accum_txfunfl = 0;
460 return 0;
461}
462
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400463static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700464{
465 int i;
466 uint32 phy_rate, dma_rate, tmp;
467 uint8 max_mpdu;
468 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
469
470 /* recompute the dma rate */
471 /* note : we divide/multiply by 100 to avoid integer overflows */
472 max_mpdu =
473 MIN(fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
474 phy_rate = MCS_RATE(FFPLD_MAX_MCS, TRUE, FALSE);
475 dma_rate =
476 (((phy_rate / 100) *
477 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
478 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
479 fifo->dmaxferrate = dma_rate;
480
481 /* fill up the mcs2ampdu table; do not recalc the last mcs */
482 dma_rate = dma_rate >> 7;
483 for (i = 0; i < FFPLD_MAX_MCS; i++) {
484 /* shifting to keep it within integer range */
485 phy_rate = MCS_RATE(i, TRUE, FALSE) >> 7;
486 if (phy_rate > dma_rate) {
487 tmp = ((fifo->ampdu_pld_size * phy_rate) /
488 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
489 tmp = MIN(tmp, 255);
490 fifo->mcs2ampdu_table[i] = (uint8) tmp;
491 }
492 }
493}
494
495static void BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400496wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, void *p, uint prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700497{
498 scb_ampdu_t *scb_ampdu;
499 scb_ampdu_tid_ini_t *ini;
500 uint8 tid = (uint8) PKTPRIO(p);
501
502 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
503
504 /* initialize initiator on first packet; sends addba req */
505 ini = SCB_AMPDU_INI(scb_ampdu, tid);
506 if (ini->magic != INI_MAGIC) {
507 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, FALSE);
508 }
509 return;
510}
511
512int BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400513wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, void **pdu, int prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700514{
515 wlc_info_t *wlc;
516 osl_t *osh;
517 void *p, *pkt[AMPDU_MAX_MPDU];
518 uint8 tid, ndelim;
519 int err = 0;
520 uint8 preamble_type = WLC_GF_PREAMBLE;
521 uint8 fbr_preamble_type = WLC_GF_PREAMBLE;
522 uint8 rts_preamble_type = WLC_LONG_PREAMBLE;
523 uint8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
524
525 bool rr = TRUE, fbr = FALSE;
526 uint i, count = 0, fifo, seg_cnt = 0;
527 uint16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
528 uint32 ampdu_len, maxlen = 0;
529 d11txh_t *txh = NULL;
530 uint8 *plcp;
531 struct dot11_header *h;
532 struct scb *scb;
533 scb_ampdu_t *scb_ampdu;
534 scb_ampdu_tid_ini_t *ini;
535 uint8 mcs = 0;
536 bool use_rts = FALSE, use_cts = FALSE;
537 ratespec_t rspec = 0, rspec_fallback = 0;
538 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
539 uint16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
540 struct dot11_rts_frame *rts;
541 uint8 rr_retry_limit;
542 wlc_fifo_info_t *f;
543 bool fbr_iscck;
544 struct ieee80211_tx_info *tx_info;
545 uint16 qlen;
546
547 wlc = ampdu->wlc;
548 osh = wlc->osh;
549 p = *pdu;
550
551 ASSERT(p);
552
553 tid = (uint8) PKTPRIO(p);
554 ASSERT(tid < AMPDU_MAX_SCB_TID);
555
556 f = ampdu->fifo_tb + prio2fifo[tid];
557
558 scb = wlc->pub->global_scb;
559 ASSERT(scb->magic == SCB_MAGIC);
560
561 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
562 ASSERT(scb_ampdu);
563 ini = &scb_ampdu->ini[tid];
564
565 /* Let pressure continue to build ... */
566 qlen = pktq_plen(&qi->q, prec);
567 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
568 return BCME_BUSY;
569 }
570
571 wlc_ampdu_agg(ampdu, scb, p, tid);
572
573 if (wlc->block_datafifo) {
574 WL_ERROR(("%s: Fifo blocked\n", __func__));
575 return BCME_BUSY;
576 }
577 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
578 ampdu_len = 0;
579 dma_len = 0;
580 while (p) {
581 struct ieee80211_tx_rate *txrate;
582
583 tx_info = IEEE80211_SKB_CB(p);
584 txrate = tx_info->status.rates;
585
586 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
587 err = wlc_prep_pdu(wlc, p, &fifo);
588 } else {
589 WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
590 *pdu = NULL;
591 err = 0;
592 break;
593 }
594
595 if (err) {
596 if (err == BCME_BUSY) {
597 WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq));
598 WLCNTINCR(ampdu->cnt->sduretry);
599 *pdu = p;
600 break;
601 }
602
603 /* error in the packet; reject it */
604 WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc->pub->unit, seq));
605 WLCNTINCR(ampdu->cnt->sdurejected);
606
607 *pdu = NULL;
608 break;
609 }
610
611 /* pkt is good to be aggregated */
612 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
613 txh = (d11txh_t *) PKTDATA(p);
614 plcp = (uint8 *) (txh + 1);
615 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
616 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
617 index = TX_SEQ_TO_INDEX(seq);
618
619 /* check mcl fields and test whether it can be agg'd */
620 mcl = ltoh16(txh->MacTxControlLow);
621 mcl &= ~TXC_AMPDU_MASK;
622 fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
623 ASSERT(!fbr_iscck);
624 txh->PreloadSize = 0; /* always default to 0 */
625
626 /* Handle retry limits */
627 if (txrate[0].count <= rr_retry_limit) {
628 txrate[0].count++;
629 rr = TRUE;
630 fbr = FALSE;
631 ASSERT(!fbr);
632 } else {
633 fbr = TRUE;
634 rr = FALSE;
635 txrate[1].count++;
636 }
637
638 /* extract the length info */
639 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
640 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
641
642 /* retrieve null delimiter count */
643 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
644 seg_cnt += 1;
645
646 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
647 wlc->pub->unit, count, len));
648
649 /*
650 * aggregateable mpdu. For ucode/hw agg,
651 * test whether need to break or change the epoch
652 */
653 if (count == 0) {
654 uint16 fc;
655 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
656 /* refill the bits since might be a retx mpdu */
657 mcl |= TXC_STARTMSDU;
658 rts = (struct dot11_rts_frame *)&txh->rts_frame;
659 fc = ltoh16(rts->fc);
660 if ((fc & FC_KIND_MASK) == FC_RTS) {
661 mcl |= TXC_SENDRTS;
662 use_rts = TRUE;
663 }
664 if ((fc & FC_KIND_MASK) == FC_CTS) {
665 mcl |= TXC_SENDCTS;
666 use_cts = TRUE;
667 }
668 } else {
669 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
670 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
671 }
672
673 len = ROUNDUP(len, 4);
674 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
675
676 dma_len += (uint16) pkttotlen(osh, p);
677
678 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));
679
680 txh->MacTxControlLow = htol16(mcl);
681
682 /* this packet is added */
683 pkt[count++] = p;
684
685 /* patch the first MPDU */
686 if (count == 1) {
687 uint8 plcp0, plcp3, is40, sgi;
688 struct ieee80211_sta *sta;
689
690 sta = tx_info->control.sta;
691
692 if (rr) {
693 plcp0 = plcp[0];
694 plcp3 = plcp[3];
695 } else {
696 plcp0 = txh->FragPLCPFallback[0];
697 plcp3 = txh->FragPLCPFallback[3];
698
699 }
700 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
701 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
702 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
703 ASSERT(mcs < MCS_TABLE_SIZE);
704 maxlen =
705 MIN(scb_ampdu->max_rxlen,
706 ampdu->max_txlen[mcs][is40][sgi]);
707
708 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
709 is40, mcs));
710
711 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
712
713 if (is40)
714 mimo_ctlchbw =
715 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
716 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
717
718 /* rebuild the rspec and rspec_fallback */
719 rspec = RSPEC_MIMORATE;
720 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
721 if (plcp[0] & MIMO_PLCP_40MHZ)
722 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
723
724 if (fbr_iscck) /* CCK */
725 rspec_fallback =
726 CCK_RSPEC(CCK_PHY2MAC_RATE
727 (txh->FragPLCPFallback[0]));
728 else { /* MIMO */
729 rspec_fallback = RSPEC_MIMORATE;
730 rspec_fallback |=
731 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
732 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
733 rspec_fallback |=
734 (PHY_TXC1_BW_40MHZ <<
735 RSPEC_BW_SHIFT);
736 }
737
738 if (use_rts || use_cts) {
739 rts_rspec =
740 wlc_rspec_to_rts_rspec(wlc, rspec, FALSE,
741 mimo_ctlchbw);
742 rts_rspec_fallback =
743 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
744 FALSE, mimo_ctlchbw);
745 }
746 }
747
748 /* if (first mpdu for host agg) */
749 /* test whether to add more */
750 if ((MCS_RATE(mcs, TRUE, FALSE) >= f->dmaxferrate) &&
751 (count == f->mcs2ampdu_table[mcs])) {
752 WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc->pub->unit, count, mcs));
753 break;
754 }
755
756 if (count == scb_ampdu->max_pdu) {
757 WL_NONE(("Stop taking from q, reached %d deep\n",
758 scb_ampdu->max_pdu));
759 break;
760 }
761
762 /* check to see if the next pkt is a candidate for aggregation */
763 p = pktq_ppeek(&qi->q, prec);
764 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
765
766 if (p) {
767 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
768 ((uint8) PKTPRIO(p) == tid)) {
769
770 plen =
771 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
772 plen = MAX(scb_ampdu->min_len, plen);
773
774 if ((plen + ampdu_len) > maxlen) {
775 p = NULL;
776 WL_ERROR(("%s: Bogus plen #1\n",
777 __func__));
778 ASSERT(3 == 4);
779 continue;
780 }
781
782 /* check if there are enough descriptors available */
783 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
784 WL_ERROR(("%s: No fifo space !!!!!!\n", __func__));
785 p = NULL;
786 continue;
787 }
788 p = pktq_pdeq(&qi->q, prec);
789 ASSERT(p);
790 } else {
791 p = NULL;
792 }
793 }
794 } /* end while(p) */
795
796 ini->tx_in_transit += count;
797
798 if (count) {
799 WLCNTADD(ampdu->cnt->txmpdu, count);
800
801 /* patch up the last txh */
802 txh = (d11txh_t *) PKTDATA(pkt[count - 1]);
803 mcl = ltoh16(txh->MacTxControlLow);
804 mcl &= ~TXC_AMPDU_MASK;
805 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
806 txh->MacTxControlLow = htol16(mcl);
807
808 /* remove the null delimiter after last mpdu */
809 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
810 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
811 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
812
813 /* remove the pad len from last mpdu */
814 fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
815 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
816 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
817 ampdu_len -= ROUNDUP(len, 4) - len;
818
819 /* patch up the first txh & plcp */
820 txh = (d11txh_t *) PKTDATA(pkt[0]);
821 plcp = (uint8 *) (txh + 1);
822
823 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
824 /* mark plcp to indicate ampdu */
825 WLC_SET_MIMO_PLCP_AMPDU(plcp);
826
827 /* reset the mixed mode header durations */
828 if (txh->MModeLen) {
829 uint16 mmodelen =
830 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
831 txh->MModeLen = htol16(mmodelen);
832 preamble_type = WLC_MM_PREAMBLE;
833 }
834 if (txh->MModeFbrLen) {
835 uint16 mmfbrlen =
836 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
837 txh->MModeFbrLen = htol16(mmfbrlen);
838 fbr_preamble_type = WLC_MM_PREAMBLE;
839 }
840
841 /* set the preload length */
842 if (MCS_RATE(mcs, TRUE, FALSE) >= f->dmaxferrate) {
843 dma_len = MIN(dma_len, f->ampdu_pld_size);
844 txh->PreloadSize = htol16(dma_len);
845 } else
846 txh->PreloadSize = 0;
847
848 mch = ltoh16(txh->MacTxControlHigh);
849
850 /* update RTS dur fields */
851 if (use_rts || use_cts) {
852 uint16 durid;
853 rts = (struct dot11_rts_frame *)&txh->rts_frame;
854 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
855 TXC_PREAMBLE_RTS_MAIN_SHORT)
856 rts_preamble_type = WLC_SHORT_PREAMBLE;
857
858 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
859 TXC_PREAMBLE_RTS_FB_SHORT)
860 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
861
862 durid =
863 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
864 rspec, rts_preamble_type,
865 preamble_type, ampdu_len,
866 TRUE);
867 rts->durid = htol16(durid);
868 durid = wlc_compute_rtscts_dur(wlc, use_cts,
869 rts_rspec_fallback,
870 rspec_fallback,
871 rts_fbr_preamble_type,
872 fbr_preamble_type,
873 ampdu_len, TRUE);
874 txh->RTSDurFallback = htol16(durid);
875 /* set TxFesTimeNormal */
876 txh->TxFesTimeNormal = rts->durid;
877 /* set fallback rate version of TxFesTimeNormal */
878 txh->TxFesTimeFallback = txh->RTSDurFallback;
879 }
880
881 /* set flag and plcp for fallback rate */
882 if (fbr) {
883 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
884 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
885 mch |= TXC_AMPDU_FBR;
886 txh->MacTxControlHigh = htol16(mch);
887 WLC_SET_MIMO_PLCP_AMPDU(plcp);
888 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
889 }
890
891 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
892 wlc->pub->unit, count, ampdu_len));
893
894 /* inform rate_sel if it this is a rate probe pkt */
895 frameid = ltoh16(txh->TxFrameID);
896 if (frameid & TXFID_RATE_PROBE_MASK) {
897 WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__));
898 }
899#ifdef WLC_HIGH_ONLY
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, TRUE);
903#endif
904 for (i = 0; i < count; i++)
905 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
906 ampdu->txpkt_weight);
907#ifdef WLC_HIGH_ONLY
908 if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
909 bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
910 BCM_RPC_TP_HOST_AGG_AMPDU, FALSE);
911#endif
912
913 }
914 /* endif (count) */
915 return err;
916}
917
918void BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400919wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, void *p,
920 tx_status_t *txs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700921{
922 scb_ampdu_t *scb_ampdu;
923 wlc_info_t *wlc = ampdu->wlc;
924 scb_ampdu_tid_ini_t *ini;
925 uint32 s1 = 0, s2 = 0;
926 struct ieee80211_tx_info *tx_info;
927
928 tx_info = IEEE80211_SKB_CB(p);
929 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
930 ASSERT(scb);
931 ASSERT(scb->magic == SCB_MAGIC);
932 ASSERT(txs->status & TX_STATUS_AMPDU);
933 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
934 ASSERT(scb_ampdu);
935 ini = SCB_AMPDU_INI(scb_ampdu, PKTPRIO(p));
936 ASSERT(ini->scb == scb);
937
938 /* BMAC_NOTE: For the split driver, second level txstatus comes later
939 * So if the ACK was received then wait for the second level else just
940 * call the first one
941 */
942 if (txs->status & TX_STATUS_ACK_RCV) {
943#ifdef WLC_LOW
944 uint8 status_delay = 0;
945
946 /* wait till the next 8 bytes of txstatus is available */
947 while (((s1 =
948 R_REG(wlc->osh,
949 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
950 OSL_DELAY(1);
951 status_delay++;
952 if (status_delay > 10) {
953 ASSERT(status_delay <= 10);
954 return;
955 }
956 }
957
958 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
959 ASSERT(s1 & TX_STATUS_AMPDU);
960 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
961#else /* WLC_LOW */
962
963 /* Store the relevant information in ampdu structure */
964 WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
965 wlc->pub->unit));
966
967 ASSERT(!ampdu->p);
968 ampdu->p = p;
969 bcopy(txs, &ampdu->txs, sizeof(tx_status_t));
970 ampdu->waiting_status = TRUE;
971 return;
972#endif /* WLC_LOW */
973 }
974
975 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
976 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
977}
978
979#ifdef WLC_HIGH_ONLY
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400980void wlc_ampdu_txstatus_complete(ampdu_info_t *ampdu, uint32 s1, uint32 s2)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700981{
982 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));
983
984 ASSERT(ampdu->waiting_status);
985
986 /* The packet may have been freed if the SCB went away, if so, then still free the
987 * DMA chain
988 */
989 if (ampdu->p) {
990 struct ieee80211_tx_info *tx_info;
991 struct scb *scb;
992
993 tx_info = IEEE80211_SKB_CB(ampdu->p);
994 scb = (struct scb *)tx_info->control.sta->drv_priv;
995
996 wlc_ampdu_dotxstatus_complete(ampdu, scb, ampdu->p, &ampdu->txs,
997 s1, s2);
998 ampdu->p = NULL;
999 }
1000
1001 ampdu->waiting_status = FALSE;
1002}
1003#endif /* WLC_HIGH_ONLY */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001004void rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
1005 tx_status_t *txs, uint8 mcs);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001006
1007void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001008rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
1009 tx_status_t *txs, uint8 mcs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001010{
1011 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
1012 int i;
1013
1014 /* clear the rest of the rates */
1015 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
1016 txrate[i].idx = -1;
1017 txrate[i].count = 0;
1018 }
1019}
1020
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001021extern void wlc_txq_enq(wlc_info_t *wlc, struct scb *scb, void *sdu,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001022 uint prec);
1023
1024#define SHORTNAME "AMPDU status"
1025
1026static void BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001027wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb, void *p,
1028 tx_status_t *txs, uint32 s1, uint32 s2)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001029{
1030 scb_ampdu_t *scb_ampdu;
1031 wlc_info_t *wlc = ampdu->wlc;
1032 scb_ampdu_tid_ini_t *ini;
1033 uint8 bitmap[8], queue, tid;
1034 d11txh_t *txh;
1035 uint8 *plcp;
1036 struct dot11_header *h;
1037 uint16 seq, start_seq = 0, bindex, index, mcl;
1038 uint8 mcs = 0;
1039 bool ba_recd = FALSE, ack_recd = FALSE;
1040 uint8 suc_mpdu = 0, tot_mpdu = 0;
1041 uint supr_status;
1042 bool update_rate = TRUE, retry = TRUE, tx_error = FALSE;
1043 uint16 mimoantsel = 0;
1044 uint8 antselid = 0;
1045 uint8 retry_limit, rr_retry_limit;
1046 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
1047
1048#ifdef BCMDBG
1049 uint8 hole[AMPDU_MAX_MPDU];
1050 bzero(hole, sizeof(hole));
1051#endif
1052
1053 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1054 ASSERT(txs->status & TX_STATUS_AMPDU);
1055
1056 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1057 ASSERT(scb_ampdu);
1058
1059 tid = (uint8) PKTPRIO(p);
1060
1061 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1062 retry_limit = ampdu->retry_limit_tid[tid];
1063 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
1064
1065 ASSERT(ini->scb == scb);
1066
1067 bzero(bitmap, sizeof(bitmap));
1068 queue = txs->frameid & TXFID_QUEUE_MASK;
1069 ASSERT(queue < AC_COUNT);
1070
1071 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1072
1073 if (txs->status & TX_STATUS_ACK_RCV) {
1074 if (TX_STATUS_SUPR_UF == supr_status) {
1075 update_rate = FALSE;
1076 }
1077
1078 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1079 start_seq = txs->sequence >> SEQNUM_SHIFT;
1080 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1081 TX_STATUS_BA_BMAP03_SHIFT;
1082
1083 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1084 ASSERT(s1 & TX_STATUS_AMPDU);
1085
1086 bitmap[0] |=
1087 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1088 TX_STATUS_BA_BMAP47_SHIFT;
1089 bitmap[1] = (s1 >> 8) & 0xff;
1090 bitmap[2] = (s1 >> 16) & 0xff;
1091 bitmap[3] = (s1 >> 24) & 0xff;
1092
1093 bitmap[4] = s2 & 0xff;
1094 bitmap[5] = (s2 >> 8) & 0xff;
1095 bitmap[6] = (s2 >> 16) & 0xff;
1096 bitmap[7] = (s2 >> 24) & 0xff;
1097
1098 ba_recd = TRUE;
1099 } else {
1100 WLCNTINCR(ampdu->cnt->noba);
1101 if (supr_status) {
1102 update_rate = FALSE;
1103 if (supr_status == TX_STATUS_SUPR_BADCH) {
1104 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
1105 } else {
1106 if (supr_status == TX_STATUS_SUPR_FRAG)
1107 WL_NONE(("%s: AMPDU frag err\n",
1108 __func__));
1109 else
1110 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
1111 }
1112 /* no need to retry for badch; will fail again */
1113 if (supr_status == TX_STATUS_SUPR_BADCH ||
1114 supr_status == TX_STATUS_SUPR_EXPTIME) {
1115 retry = FALSE;
1116 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1117 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1118
1119 WLCNTINCR(wlc->pub->_cnt->txexptime);
1120
1121 /* TX underflow : try tuning pre-loading or ampdu size */
1122 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1123 /* if there were underflows, but pre-loading is not active,
1124 notify rate adaptation.
1125 */
1126 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1127 > 0) {
1128 tx_error = TRUE;
1129#ifdef WLC_HIGH_ONLY
1130 /* With BMAC, TX Underflows should not happen */
1131 WL_ERROR(("wl%d: BMAC TX Underflow?",
1132 wlc->pub->unit));
1133#endif
1134 }
1135 }
1136 } else if (txs->phyerr) {
1137 update_rate = FALSE;
1138 WLCNTINCR(wlc->pub->_cnt->txphyerr);
1139 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc->pub->unit, txs->phyerr));
1140
1141#ifdef BCMDBG
1142 if (WL_ERROR_ON()) {
1143 prpkt("txpkt (AMPDU)", wlc->osh, p);
1144 wlc_print_txdesc((d11txh_t *) PKTDATA(p));
1145 wlc_print_txstatus(txs);
1146 }
1147#endif /* BCMDBG */
1148 }
1149 }
1150
1151 /* loop through all pkts and retry if not acked */
1152 while (p) {
1153 tx_info = IEEE80211_SKB_CB(p);
1154 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1155 txh = (d11txh_t *) PKTDATA(p);
1156 mcl = ltoh16(txh->MacTxControlLow);
1157 plcp = (uint8 *) (txh + 1);
1158 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1159 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1160
1161 if (tot_mpdu == 0) {
1162 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1163 mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1164 }
1165
1166 index = TX_SEQ_TO_INDEX(seq);
1167 ack_recd = FALSE;
1168 if (ba_recd) {
1169 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1170
1171 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1172 "bindex is %d set %d, index %d\n",
1173 __func__, tid, seq, start_seq, bindex,
1174 isset(bitmap, bindex), index));
1175
1176 /* if acked then clear bit and free packet */
1177 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1178 && isset(bitmap, bindex)) {
1179 ini->tx_in_transit--;
1180 ini->txretry[index] = 0;
1181
1182 /* ampdu_ack_len: number of acked aggregated frames */
1183 /* ampdu_ack_map: block ack bit map for the aggregation */
1184 /* ampdu_len: number of aggregated frames */
1185 rate_status(wlc, tx_info, txs, mcs);
1186 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1187 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1188
1189 /* XXX TODO: Make these accurate. */
1190 tx_info->status.ampdu_ack_len =
1191 (txs->
1192 status & TX_STATUS_FRM_RTX_MASK) >>
1193 TX_STATUS_FRM_RTX_SHIFT;
1194 tx_info->status.ampdu_len =
1195 (txs->
1196 status & TX_STATUS_FRM_RTX_MASK) >>
1197 TX_STATUS_FRM_RTX_SHIFT;
1198
1199 PKTPULL(p, D11_PHY_HDR_LEN);
1200 PKTPULL(p, D11_TXH_LEN);
1201
1202 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1203 p);
1204 ack_recd = TRUE;
1205 suc_mpdu++;
1206 }
1207 }
1208 /* either retransmit or send bar if ack not recd */
1209 if (!ack_recd) {
1210 struct ieee80211_tx_rate *txrate =
1211 tx_info->status.rates;
1212 if (retry && (txrate[0].count < (int)retry_limit)) {
1213 ini->txretry[index]++;
1214 ini->tx_in_transit--;
1215 /* Use high prededence for retransmit to give some punch */
1216 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1217 wlc_txq_enq(wlc, scb, p,
1218 WLC_PRIO_TO_HI_PREC(tid));
1219 } else {
1220 /* Retry timeout */
1221 ini->tx_in_transit--;
1222 ieee80211_tx_info_clear_status(tx_info);
1223 tx_info->flags |=
1224 IEEE80211_TX_STAT_AMPDU_NO_BACK;
1225 PKTPULL(p, D11_PHY_HDR_LEN);
1226 PKTPULL(p, D11_TXH_LEN);
1227 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME, seq, ini->tx_in_transit));
1228 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1229 p);
1230 }
1231 }
1232 tot_mpdu++;
1233
1234 /* break out if last packet of ampdu */
1235 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1236 TXC_AMPDU_LAST)
1237 break;
1238
1239 p = GETNEXTTXP(wlc, queue);
1240 if (p == NULL) {
1241 ASSERT(p);
1242 break;
1243 }
1244 }
1245 wlc_send_q(wlc, wlc->active_queue);
1246
1247 /* update rate state */
1248 if (WLANTSEL_ENAB(wlc))
1249 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1250
1251 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1252}
1253
1254static void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001255ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, uint8 tid,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001256 bool force)
1257{
1258 scb_ampdu_tid_ini_t *ini;
Jason Cooperca8c1e52010-09-14 09:45:33 -04001259 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1260 if (!ini)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001261 return;
1262
1263 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1264 ampdu->wlc->pub->unit, tid));
1265
1266 if (ini->tx_in_transit && !force)
1267 return;
1268
1269 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1270 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1271
1272 /* free all buffered tx packets */
1273 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, TRUE, NULL, 0);
1274}
1275
1276/* initialize the initiator code for tid */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001277static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
1278 scb_ampdu_t *scb_ampdu,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001279 uint8 tid, bool override)
1280{
1281 scb_ampdu_tid_ini_t *ini;
1282
1283 ASSERT(scb_ampdu);
1284 ASSERT(scb_ampdu->scb);
1285 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1286 ASSERT(tid < AMPDU_MAX_SCB_TID);
1287
1288 /* check for per-tid control of ampdu */
1289 if (!ampdu->ini_enable[tid]) {
1290 WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
1291 return NULL;
1292 }
1293
1294 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1295 ini->tid = tid;
1296 ini->scb = scb_ampdu->scb;
1297 ini->magic = INI_MAGIC;
1298 WLCNTINCR(ampdu->cnt->txaddbareq);
1299
1300 return ini;
1301}
1302
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001303int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001304{
1305 wlc_info_t *wlc = ampdu->wlc;
1306
1307 wlc->pub->_ampdu = FALSE;
1308
1309 if (on) {
1310 if (!N_ENAB(wlc->pub)) {
1311 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1312 wlc->pub->unit));
1313 return BCME_UNSUPPORTED;
1314 }
1315 if (!wlc_ampdu_cap(ampdu)) {
1316 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1317 wlc->pub->unit));
1318 return BCME_UNSUPPORTED;
1319 }
1320 wlc->pub->_ampdu = on;
1321 }
1322
1323 return 0;
1324}
1325
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001326bool wlc_ampdu_cap(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001327{
1328 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1329 return TRUE;
1330 else
1331 return FALSE;
1332}
1333
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001334static void ampdu_update_max_txlen(ampdu_info_t *ampdu, uint8 dur)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001335{
1336 uint32 rate, mcs;
1337
1338 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1339 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1340 /* 20MHz, No SGI */
1341 rate = MCS_RATE(mcs, FALSE, FALSE);
1342 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1343 /* 40 MHz, No SGI */
1344 rate = MCS_RATE(mcs, TRUE, FALSE);
1345 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1346 /* 20MHz, SGI */
1347 rate = MCS_RATE(mcs, FALSE, TRUE);
1348 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1349 /* 40 MHz, SGI */
1350 rate = MCS_RATE(mcs, TRUE, TRUE);
1351 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1352 }
1353}
1354
1355uint8 BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001356wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001357 ratespec_t rspec, int phylen)
1358{
1359 scb_ampdu_t *scb_ampdu;
1360 int bytes, cnt, tmp;
1361 uint8 tx_density;
1362
1363 ASSERT(scb);
1364 ASSERT(SCB_AMPDU(scb));
1365
1366 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1367 ASSERT(scb_ampdu);
1368
1369 if (scb_ampdu->mpdu_density == 0)
1370 return 0;
1371
1372 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1373 density x is in 2^(x-4) usec
1374 ==> # of bytes needed for req density = rate/2^(17-x)
1375 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1376 */
1377
1378 tx_density = scb_ampdu->mpdu_density;
1379
1380 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1381 tmp = 1 << (17 - tx_density);
1382 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1383
1384 if (bytes > phylen) {
1385 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1386 ASSERT(cnt <= 255);
1387 return (uint8) cnt;
1388 } else
1389 return 0;
1390}
1391
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001392void wlc_ampdu_macaddr_upd(wlc_info_t *wlc)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001393{
1394 char template[T_RAM_ACCESS_SZ * 2];
1395
1396 /* driver needs to write the ta in the template; ta is at offset 16 */
1397 bzero(template, sizeof(template));
1398 bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETHER_ADDR_LEN);
1399 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1400 template);
1401}
1402
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001403bool wlc_aggregatable(wlc_info_t *wlc, uint8 tid)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001404{
Jason Cooper90ea2292010-09-14 09:45:32 -04001405 return wlc->ampdu->ini_enable[tid];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001406}
1407
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001408void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001409{
1410 wlc_info_t *wlc = ampdu->wlc;
1411
1412 /* Extend ucode internal watchdog timer to match larger received frames */
1413 if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1414 AMPDU_RX_FACTOR_64K) {
1415 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1416 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1417 } else {
1418 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1419 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
1420 }
1421}