blob: f7e290401068108cf67e9828c3d52db0d16f187e [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>
Brett Rudley33279892010-10-01 18:03:27 -070019#include <linuxver.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070020#include <bcmdefs.h>
21#include <osl.h>
22#include <bcmutils.h>
23#include <siutils.h>
24#include <bcmendian.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070025#include <wlioctl.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070026#include <sbhnddma.h>
27#include <hnddma.h>
28#include <d11.h>
29#include <wlc_rate.h>
30#include <wlc_pub.h>
31#include <wlc_key.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070032#include <wlc_mac80211.h>
33#include <wlc_phy_hal.h>
34#include <wlc_antsel.h>
35#include <wlc_scb.h>
36#include <net/mac80211.h>
37#include <wlc_ampdu.h>
38#include <wl_export.h>
39
40#ifdef WLC_HIGH_ONLY
41#include <bcm_rpc_tp.h>
42#include <wlc_rpctx.h>
43#endif
44
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 */
58
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
61 * without underflows
62 */
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.
68 */
69
Jason Cooper0d706ef2010-09-14 09:45:39 -040070#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070071
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)
75
76#ifdef BCMDBG
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -070077u32 wl_ampdu_dbg =
Henry Ptasinskia9533e72010-09-08 21:04:42 -070078 WL_AMPDU_UPDN_VAL |
79 WL_AMPDU_ERR_VAL |
80 WL_AMPDU_TX_VAL |
81 WL_AMPDU_RX_VAL |
82 WL_AMPDU_CTL_VAL |
83 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
84#endif
85
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.
91 */
92typedef struct wlc_fifo_info {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070093 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -070094 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070095 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -070096 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 */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700100} wlc_fifo_info_t;
101
102/* AMPDU module specific state */
103struct ampdu_info {
104 wlc_info_t *wlc; /* pointer to main wlc structure */
105 int scb_handle; /* scb cubby handle to retrieve data from scb */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700106 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 */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700112 /* per-tid mpdu transmit retry limit at regular rate */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700113 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
114 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
Greg Kroah-Hartman562c8852010-10-05 11:04:17 -0700115 s8 max_pdu; /* max pdus allowed in ampdu */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700116 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 */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700119 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 */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700121 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
122 bool mfbr; /* enable multiple fallback rate */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700123 u32 tx_max_funl; /* underflows should be kept such that
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700124 * (tx_max_funfl*underflows) < tx frames
125 */
126 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
127
128#ifdef WLC_HIGH_ONLY
129 void *p;
130 tx_status_t txs;
131 bool waiting_status; /* To help sanity checks */
132#endif
133};
134
135#define AMPDU_CLEANUPFLAG_RX (0x1)
136#define AMPDU_CLEANUPFLAG_TX (0x2)
137
138#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
139#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
140
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400141static void wlc_ffpld_init(ampdu_info_t *ampdu);
142static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int f);
143static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700144
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400145static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
146 scb_ampdu_t *scb_ampdu,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700147 u8 tid, bool override);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400148static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700149 u8 tid, bool force);
150static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400151static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
152static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700153
154#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
155
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400156static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
157 void *p, tx_status_t *txs,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700158 u32 frmtxstatus,
159 u32 frmtxstatus2);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700160
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700161static inline u16 pkt_txh_seqnum(wlc_info_t *wlc, void *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700162{
163 d11txh_t *txh;
164 struct dot11_header *h;
165 txh = (d11txh_t *) PKTDATA(p);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700166 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
Jason Cooper90ea2292010-09-14 09:45:32 -0400167 return ltoh16(h->seq) >> SEQNUM_SHIFT;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700168}
169
Jason Coopera2627bc2010-09-14 09:45:31 -0400170ampdu_info_t *BCMATTACHFN(wlc_ampdu_attach) (wlc_info_t *wlc)
171{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700172 ampdu_info_t *ampdu;
173 int i;
174
175 /* some code depends on packed structures */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700176 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700177 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);
181
Jason Cooperca8c1e52010-09-14 09:45:33 -0400182 ampdu = (ampdu_info_t *) MALLOC(wlc->osh, sizeof(ampdu_info_t));
183 if (!ampdu) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700184 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem, malloced %d bytes\n", wlc->pub->unit, MALLOCED(wlc->osh)));
185 return NULL;
186 }
187 bzero((char *)ampdu, sizeof(ampdu_info_t));
188 ampdu->wlc = wlc;
189
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;
195
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;
199
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;
206
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;
211 else
212 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
213#ifdef WLC_HIGH_ONLY
214 /* Restrict to smaller rcv size for BMAC dongle */
215 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
216#endif
217 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
218 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
219
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;
223 }
224
225 ampdu_update_max_txlen(ampdu, ampdu->dur);
226 ampdu->mfbr = FALSE;
227 /* try to set ampdu to the default value */
228 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
229
230 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
231 wlc_ffpld_init(ampdu);
232
233 return ampdu;
234}
235
Jason Coopera2627bc2010-09-14 09:45:31 -0400236void BCMATTACHFN(wlc_ampdu_detach) (ampdu_info_t *ampdu)
237{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700238 int i;
239
240 if (!ampdu)
241 return;
242
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));
248 }
249 }
250
251 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
252 MFREE(ampdu->wlc->osh, ampdu, sizeof(ampdu_info_t));
253}
254
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400255void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700256{
257 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700258 u8 tid;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700259
260 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
261 ASSERT(scb_ampdu);
262
263 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
264 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, FALSE);
265 }
266}
267
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
270 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400271void wlc_ampdu_reset(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700272{
273 WL_NONE(("%s: Entering\n", __func__));
274}
275
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400276static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700277{
278 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
279 int i;
280
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700281 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700282
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;
287 }
288
289 /* apply user override */
290 if (ampdu->max_pdu != AUTO)
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700291 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700292
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700293 scb_ampdu->release = min(scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700294
295 if (scb_ampdu->max_rxlen)
296 scb_ampdu->release =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700297 min(scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700298
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700299 scb_ampdu->release = min(scb_ampdu->release,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700300 ampdu->fifo_tb[TX_AC_BE_FIFO].
301 mcs2ampdu_table[FFPLD_MAX_MCS]);
302
303 ASSERT(scb_ampdu->release);
304}
305
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400306void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700307{
308 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
309}
310
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400311static void wlc_ffpld_init(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700312{
313 int i, j;
314 wlc_fifo_info_t *fifo;
315
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;
325
326 }
327}
328
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.
334 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400335static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int fid)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700336{
337 ampdu_info_t *ampdu = wlc->ampdu;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700338 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, TRUE, FALSE);
339 u32 txunfl_ratio;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700340 u8 max_mpdu;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700341 u32 current_ampdu_cnt = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700342 u16 max_pld_size;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700343 u32 new_txunfl;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700344 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
345 uint xmtfifo_sz;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700346 u16 cur_txunfl;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700347
348 /* return if we got here for a different reason than underflows */
349 cur_txunfl =
350 wlc_read_shm(wlc,
351 M_UCODE_MACSTAT + OFFSETOF(macstat_t, txfunfl[fid]));
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700352 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700353 if (new_txunfl == 0) {
354 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
355 return -1;
356 }
357 fifo->prev_txfunfl = cur_txunfl;
358
359 if (!ampdu->tx_max_funl)
360 return 1;
361
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"));
365 return -1;
366 }
367
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700368 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700369 return 1;
370
371 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
372 fifo->accum_txfunfl += new_txunfl;
373
374 /* we need to wait for at least 10 underflows */
375 if (fifo->accum_txfunfl < 10)
376 return 0;
377
378 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
379 current_ampdu_cnt, fifo->accum_txfunfl));
380
381 /*
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.
388 */
389
390 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
391
392 if (txunfl_ratio > ampdu->tx_max_funl) {
393 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
394 fifo->accum_txfunfl = 0;
395 }
396 return 0;
397 }
398 max_mpdu =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700399 min(fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700400
401 /* In case max value max_pdu is already lower than
402 the fifo depth, there is nothing more we can do.
403 */
404
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;
408 return 0;
409 }
410
411 if (fifo->ampdu_pld_size < max_pld_size) {
412
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;
417
418 /* update scb release size */
419 scb_ampdu_update_config_all(ampdu);
420
421 /*
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.
425 */
426 /* note : we divide/multiply by 100 to avoid integer overflows */
427 fifo->dmaxferrate =
428 (((phy_rate / 100) *
429 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
430 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
431
432 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
433 fifo->dmaxferrate, fifo->ampdu_pld_size));
434 } else {
435
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;
441 else
442 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
443
444 /* recompute the table */
445 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
446
447 /* update scb release size */
448 scb_ampdu_update_config_all(ampdu);
449 }
450 }
451 fifo->accum_txfunfl = 0;
452 return 0;
453}
454
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400455static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700456{
457 int i;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700458 u32 phy_rate, dma_rate, tmp;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700459 u8 max_mpdu;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700460 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
461
462 /* recompute the dma rate */
463 /* note : we divide/multiply by 100 to avoid integer overflows */
464 max_mpdu =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700465 min(fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700466 phy_rate = MCS_RATE(FFPLD_MAX_MCS, TRUE, FALSE);
467 dma_rate =
468 (((phy_rate / 100) *
469 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
470 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
471 fifo->dmaxferrate = dma_rate;
472
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;
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700481 tmp = min(tmp, 255);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700482 fifo->mcs2ampdu_table[i] = (u8) tmp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700483 }
484 }
485}
486
487static void BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400488wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, void *p, uint prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700489{
490 scb_ampdu_t *scb_ampdu;
491 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700492 u8 tid = (u8) PKTPRIO(p);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700493
494 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
495
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);
500 }
501 return;
502}
503
504int BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400505wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, void **pdu, int prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700506{
507 wlc_info_t *wlc;
508 osl_t *osh;
509 void *p, *pkt[AMPDU_MAX_MPDU];
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700510 u8 tid, ndelim;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700511 int err = 0;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700512 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;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700516
517 bool rr = TRUE, fbr = FALSE;
518 uint i, count = 0, fifo, seg_cnt = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700519 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700520 u32 ampdu_len, maxlen = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700521 d11txh_t *txh = NULL;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700522 u8 *plcp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700523 struct dot11_header *h;
524 struct scb *scb;
525 scb_ampdu_t *scb_ampdu;
526 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700527 u8 mcs = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700528 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;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700531 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700532 struct dot11_rts_frame *rts;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700533 u8 rr_retry_limit;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700534 wlc_fifo_info_t *f;
535 bool fbr_iscck;
536 struct ieee80211_tx_info *tx_info;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700537 u16 qlen;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700538
539 wlc = ampdu->wlc;
540 osh = wlc->osh;
541 p = *pdu;
542
543 ASSERT(p);
544
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700545 tid = (u8) PKTPRIO(p);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700546 ASSERT(tid < AMPDU_MAX_SCB_TID);
547
548 f = ampdu->fifo_tb + prio2fifo[tid];
549
550 scb = wlc->pub->global_scb;
551 ASSERT(scb->magic == SCB_MAGIC);
552
553 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
554 ASSERT(scb_ampdu);
555 ini = &scb_ampdu->ini[tid];
556
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) {
560 return BCME_BUSY;
561 }
562
563 wlc_ampdu_agg(ampdu, scb, p, tid);
564
565 if (wlc->block_datafifo) {
566 WL_ERROR(("%s: Fifo blocked\n", __func__));
567 return BCME_BUSY;
568 }
569 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
570 ampdu_len = 0;
571 dma_len = 0;
572 while (p) {
573 struct ieee80211_tx_rate *txrate;
574
575 tx_info = IEEE80211_SKB_CB(p);
576 txrate = tx_info->status.rates;
577
578 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
579 err = wlc_prep_pdu(wlc, p, &fifo);
580 } else {
581 WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
582 *pdu = NULL;
583 err = 0;
584 break;
585 }
586
587 if (err) {
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);
591 *pdu = p;
592 break;
593 }
594
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);
598
599 *pdu = NULL;
600 break;
601 }
602
603 /* pkt is good to be aggregated */
604 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
605 txh = (d11txh_t *) PKTDATA(p);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700606 plcp = (u8 *) (txh + 1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700607 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
608 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
609 index = TX_SEQ_TO_INDEX(seq);
610
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);
615 ASSERT(!fbr_iscck);
616 txh->PreloadSize = 0; /* always default to 0 */
617
618 /* Handle retry limits */
619 if (txrate[0].count <= rr_retry_limit) {
620 txrate[0].count++;
621 rr = TRUE;
622 fbr = FALSE;
623 ASSERT(!fbr);
624 } else {
625 fbr = TRUE;
626 rr = FALSE;
627 txrate[1].count++;
628 }
629
630 /* extract the length info */
631 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
632 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
633
634 /* retrieve null delimiter count */
635 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
636 seg_cnt += 1;
637
638 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
639 wlc->pub->unit, count, len));
640
641 /*
642 * aggregateable mpdu. For ucode/hw agg,
643 * test whether need to break or change the epoch
644 */
645 if (count == 0) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700646 u16 fc;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700647 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) {
653 mcl |= TXC_SENDRTS;
654 use_rts = TRUE;
655 }
656 if ((fc & FC_KIND_MASK) == FC_CTS) {
657 mcl |= TXC_SENDCTS;
658 use_cts = TRUE;
659 }
660 } else {
661 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
662 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
663 }
664
665 len = ROUNDUP(len, 4);
666 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
667
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700668 dma_len += (u16) pkttotlen(osh, p);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700669
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));
671
672 txh->MacTxControlLow = htol16(mcl);
673
674 /* this packet is added */
675 pkt[count++] = p;
676
677 /* patch the first MPDU */
678 if (count == 1) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700679 u8 plcp0, plcp3, is40, sgi;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700680 struct ieee80211_sta *sta;
681
682 sta = tx_info->control.sta;
683
684 if (rr) {
685 plcp0 = plcp[0];
686 plcp3 = plcp[3];
687 } else {
688 plcp0 = txh->FragPLCPFallback[0];
689 plcp3 = txh->FragPLCPFallback[3];
690
691 }
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);
696 maxlen =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700697 min(scb_ampdu->max_rxlen,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700698 ampdu->max_txlen[mcs][is40][sgi]);
699
700 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
701 is40, mcs));
702
703 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
704
705 if (is40)
706 mimo_ctlchbw =
707 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
708 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
709
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);
715
716 if (fbr_iscck) /* CCK */
717 rspec_fallback =
718 CCK_RSPEC(CCK_PHY2MAC_RATE
719 (txh->FragPLCPFallback[0]));
720 else { /* MIMO */
721 rspec_fallback = RSPEC_MIMORATE;
722 rspec_fallback |=
723 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
724 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
725 rspec_fallback |=
726 (PHY_TXC1_BW_40MHZ <<
727 RSPEC_BW_SHIFT);
728 }
729
730 if (use_rts || use_cts) {
731 rts_rspec =
732 wlc_rspec_to_rts_rspec(wlc, rspec, FALSE,
733 mimo_ctlchbw);
734 rts_rspec_fallback =
735 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
736 FALSE, mimo_ctlchbw);
737 }
738 }
739
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));
745 break;
746 }
747
748 if (count == scb_ampdu->max_pdu) {
749 WL_NONE(("Stop taking from q, reached %d deep\n",
750 scb_ampdu->max_pdu));
751 break;
752 }
753
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 */
757
758 if (p) {
759 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700760 ((u8) PKTPRIO(p) == tid)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700761
762 plen =
763 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
764 plen = MAX(scb_ampdu->min_len, plen);
765
766 if ((plen + ampdu_len) > maxlen) {
767 p = NULL;
768 WL_ERROR(("%s: Bogus plen #1\n",
769 __func__));
770 ASSERT(3 == 4);
771 continue;
772 }
773
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__));
777 p = NULL;
778 continue;
779 }
780 p = pktq_pdeq(&qi->q, prec);
781 ASSERT(p);
782 } else {
783 p = NULL;
784 }
785 }
786 } /* end while(p) */
787
788 ini->tx_in_transit += count;
789
790 if (count) {
791 WLCNTADD(ampdu->cnt->txmpdu, count);
792
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);
799
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;
804
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;
810
811 /* patch up the first txh & plcp */
812 txh = (d11txh_t *) PKTDATA(pkt[0]);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700813 plcp = (u8 *) (txh + 1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700814
815 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
816 /* mark plcp to indicate ampdu */
817 WLC_SET_MIMO_PLCP_AMPDU(plcp);
818
819 /* reset the mixed mode header durations */
820 if (txh->MModeLen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700821 u16 mmodelen =
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700822 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
823 txh->MModeLen = htol16(mmodelen);
824 preamble_type = WLC_MM_PREAMBLE;
825 }
826 if (txh->MModeFbrLen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700827 u16 mmfbrlen =
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700828 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
829 txh->MModeFbrLen = htol16(mmfbrlen);
830 fbr_preamble_type = WLC_MM_PREAMBLE;
831 }
832
833 /* set the preload length */
834 if (MCS_RATE(mcs, TRUE, FALSE) >= f->dmaxferrate) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700835 dma_len = min(dma_len, f->ampdu_pld_size);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700836 txh->PreloadSize = htol16(dma_len);
837 } else
838 txh->PreloadSize = 0;
839
840 mch = ltoh16(txh->MacTxControlHigh);
841
842 /* update RTS dur fields */
843 if (use_rts || use_cts) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700844 u16 durid;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700845 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;
849
850 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
851 TXC_PREAMBLE_RTS_FB_SHORT)
852 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
853
854 durid =
855 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
856 rspec, rts_preamble_type,
857 preamble_type, ampdu_len,
858 TRUE);
859 rts->durid = htol16(durid);
860 durid = wlc_compute_rtscts_dur(wlc, use_cts,
861 rts_rspec_fallback,
862 rspec_fallback,
863 rts_fbr_preamble_type,
864 fbr_preamble_type,
865 ampdu_len, TRUE);
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;
871 }
872
873 /* set flag and plcp for fallback rate */
874 if (fbr) {
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);
881 }
882
883 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
884 wlc->pub->unit, count, ampdu_len));
885
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__));
890 }
891#ifdef WLC_HIGH_ONLY
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);
895#endif
896 for (i = 0; i < count; i++)
897 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
898 ampdu->txpkt_weight);
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, FALSE);
903#endif
904
905 }
906 /* endif (count) */
907 return err;
908}
909
910void BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400911wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, void *p,
912 tx_status_t *txs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700913{
914 scb_ampdu_t *scb_ampdu;
915 wlc_info_t *wlc = ampdu->wlc;
916 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700917 u32 s1 = 0, s2 = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700918 struct ieee80211_tx_info *tx_info;
919
920 tx_info = IEEE80211_SKB_CB(p);
921 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
922 ASSERT(scb);
923 ASSERT(scb->magic == SCB_MAGIC);
924 ASSERT(txs->status & TX_STATUS_AMPDU);
925 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
926 ASSERT(scb_ampdu);
927 ini = SCB_AMPDU_INI(scb_ampdu, PKTPRIO(p));
928 ASSERT(ini->scb == scb);
929
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
932 * call the first one
933 */
934 if (txs->status & TX_STATUS_ACK_RCV) {
935#ifdef WLC_LOW
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700936 u8 status_delay = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700937
938 /* wait till the next 8 bytes of txstatus is available */
939 while (((s1 =
940 R_REG(wlc->osh,
941 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
942 OSL_DELAY(1);
943 status_delay++;
944 if (status_delay > 10) {
945 ASSERT(status_delay <= 10);
946 return;
947 }
948 }
949
950 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
951 ASSERT(s1 & TX_STATUS_AMPDU);
952 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
953#else /* WLC_LOW */
954
955 /* Store the relevant information in ampdu structure */
956 WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
957 wlc->pub->unit));
958
959 ASSERT(!ampdu->p);
960 ampdu->p = p;
961 bcopy(txs, &ampdu->txs, sizeof(tx_status_t));
962 ampdu->waiting_status = TRUE;
963 return;
964#endif /* WLC_LOW */
965 }
966
967 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
968 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
969}
970
971#ifdef WLC_HIGH_ONLY
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700972void wlc_ampdu_txstatus_complete(ampdu_info_t *ampdu, u32 s1, u32 s2)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700973{
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));
975
976 ASSERT(ampdu->waiting_status);
977
978 /* The packet may have been freed if the SCB went away, if so, then still free the
979 * DMA chain
980 */
981 if (ampdu->p) {
982 struct ieee80211_tx_info *tx_info;
983 struct scb *scb;
984
985 tx_info = IEEE80211_SKB_CB(ampdu->p);
986 scb = (struct scb *)tx_info->control.sta->drv_priv;
987
988 wlc_ampdu_dotxstatus_complete(ampdu, scb, ampdu->p, &ampdu->txs,
989 s1, s2);
990 ampdu->p = NULL;
991 }
992
993 ampdu->waiting_status = FALSE;
994}
995#endif /* WLC_HIGH_ONLY */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400996void rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700997 tx_status_t *txs, u8 mcs);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700998
999void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001000rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001001 tx_status_t *txs, u8 mcs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001002{
1003 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
1004 int i;
1005
1006 /* clear the rest of the rates */
1007 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
1008 txrate[i].idx = -1;
1009 txrate[i].count = 0;
1010 }
1011}
1012
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001013extern void wlc_txq_enq(wlc_info_t *wlc, struct scb *scb, void *sdu,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001014 uint prec);
1015
1016#define SHORTNAME "AMPDU status"
1017
1018static void BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001019wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb, void *p,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001020 tx_status_t *txs, u32 s1, u32 s2)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001021{
1022 scb_ampdu_t *scb_ampdu;
1023 wlc_info_t *wlc = ampdu->wlc;
1024 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001025 u8 bitmap[8], queue, tid;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001026 d11txh_t *txh;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001027 u8 *plcp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001028 struct dot11_header *h;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001029 u16 seq, start_seq = 0, bindex, index, mcl;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001030 u8 mcs = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001031 bool ba_recd = FALSE, ack_recd = FALSE;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001032 u8 suc_mpdu = 0, tot_mpdu = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001033 uint supr_status;
1034 bool update_rate = TRUE, retry = TRUE, tx_error = FALSE;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001035 u16 mimoantsel = 0;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001036 u8 antselid = 0;
1037 u8 retry_limit, rr_retry_limit;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001038 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
1039
1040#ifdef BCMDBG
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001041 u8 hole[AMPDU_MAX_MPDU];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001042 bzero(hole, sizeof(hole));
1043#endif
1044
1045 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
1046 ASSERT(txs->status & TX_STATUS_AMPDU);
1047
1048 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1049 ASSERT(scb_ampdu);
1050
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001051 tid = (u8) PKTPRIO(p);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001052
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];
1056
1057 ASSERT(ini->scb == scb);
1058
1059 bzero(bitmap, sizeof(bitmap));
1060 queue = txs->frameid & TXFID_QUEUE_MASK;
1061 ASSERT(queue < AC_COUNT);
1062
1063 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1064
1065 if (txs->status & TX_STATUS_ACK_RCV) {
1066 if (TX_STATUS_SUPR_UF == supr_status) {
1067 update_rate = FALSE;
1068 }
1069
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;
1074
1075 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1076 ASSERT(s1 & TX_STATUS_AMPDU);
1077
1078 bitmap[0] |=
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;
1084
1085 bitmap[4] = s2 & 0xff;
1086 bitmap[5] = (s2 >> 8) & 0xff;
1087 bitmap[6] = (s2 >> 16) & 0xff;
1088 bitmap[7] = (s2 >> 24) & 0xff;
1089
1090 ba_recd = TRUE;
1091 } else {
1092 WLCNTINCR(ampdu->cnt->noba);
1093 if (supr_status) {
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)));
1097 } else {
1098 if (supr_status == TX_STATUS_SUPR_FRAG)
1099 WL_NONE(("%s: AMPDU frag err\n",
1100 __func__));
1101 else
1102 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
1103 }
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) {
1107 retry = FALSE;
1108 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1109 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1110
1111 WLCNTINCR(wlc->pub->_cnt->txexptime);
1112
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.
1117 */
1118 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1119 > 0) {
1120 tx_error = TRUE;
1121#ifdef WLC_HIGH_ONLY
1122 /* With BMAC, TX Underflows should not happen */
1123 WL_ERROR(("wl%d: BMAC TX Underflow?",
1124 wlc->pub->unit));
1125#endif
1126 }
1127 }
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));
1132
1133#ifdef BCMDBG
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);
1138 }
1139#endif /* BCMDBG */
1140 }
1141 }
1142
1143 /* loop through all pkts and retry if not acked */
1144 while (p) {
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);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001149 plcp = (u8 *) (txh + 1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001150 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1151 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1152
1153 if (tot_mpdu == 0) {
1154 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1155 mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1156 }
1157
1158 index = TX_SEQ_TO_INDEX(seq);
1159 ack_recd = FALSE;
1160 if (ba_recd) {
1161 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1162
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));
1167
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;
1173
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;
1180
1181 /* XXX TODO: Make these accurate. */
1182 tx_info->status.ampdu_ack_len =
1183 (txs->
1184 status & TX_STATUS_FRM_RTX_MASK) >>
1185 TX_STATUS_FRM_RTX_SHIFT;
1186 tx_info->status.ampdu_len =
1187 (txs->
1188 status & TX_STATUS_FRM_RTX_MASK) >>
1189 TX_STATUS_FRM_RTX_SHIFT;
1190
1191 PKTPULL(p, D11_PHY_HDR_LEN);
1192 PKTPULL(p, D11_TXH_LEN);
1193
1194 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1195 p);
1196 ack_recd = TRUE;
1197 suc_mpdu++;
1198 }
1199 }
1200 /* either retransmit or send bar if ack not recd */
1201 if (!ack_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));
1211 } else {
1212 /* Retry timeout */
1213 ini->tx_in_transit--;
1214 ieee80211_tx_info_clear_status(tx_info);
1215 tx_info->flags |=
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,
1221 p);
1222 }
1223 }
1224 tot_mpdu++;
1225
1226 /* break out if last packet of ampdu */
1227 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1228 TXC_AMPDU_LAST)
1229 break;
1230
1231 p = GETNEXTTXP(wlc, queue);
1232 if (p == NULL) {
1233 ASSERT(p);
1234 break;
1235 }
1236 }
1237 wlc_send_q(wlc, wlc->active_queue);
1238
1239 /* update rate state */
1240 if (WLANTSEL_ENAB(wlc))
1241 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1242
1243 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1244}
1245
1246static void
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001247ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001248 bool force)
1249{
1250 scb_ampdu_tid_ini_t *ini;
Jason Cooperca8c1e52010-09-14 09:45:33 -04001251 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1252 if (!ini)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001253 return;
1254
1255 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1256 ampdu->wlc->pub->unit, tid));
1257
1258 if (ini->tx_in_transit && !force)
1259 return;
1260
1261 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1262 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1263
1264 /* free all buffered tx packets */
1265 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, TRUE, NULL, 0);
1266}
1267
1268/* initialize the initiator code for tid */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001269static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
1270 scb_ampdu_t *scb_ampdu,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001271 u8 tid, bool override)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001272{
1273 scb_ampdu_tid_ini_t *ini;
1274
1275 ASSERT(scb_ampdu);
1276 ASSERT(scb_ampdu->scb);
1277 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1278 ASSERT(tid < AMPDU_MAX_SCB_TID);
1279
1280 /* check for per-tid control of ampdu */
1281 if (!ampdu->ini_enable[tid]) {
1282 WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
1283 return NULL;
1284 }
1285
1286 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1287 ini->tid = tid;
1288 ini->scb = scb_ampdu->scb;
1289 ini->magic = INI_MAGIC;
1290 WLCNTINCR(ampdu->cnt->txaddbareq);
1291
1292 return ini;
1293}
1294
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001295int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001296{
1297 wlc_info_t *wlc = ampdu->wlc;
1298
1299 wlc->pub->_ampdu = FALSE;
1300
1301 if (on) {
1302 if (!N_ENAB(wlc->pub)) {
1303 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1304 wlc->pub->unit));
1305 return BCME_UNSUPPORTED;
1306 }
1307 if (!wlc_ampdu_cap(ampdu)) {
1308 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1309 wlc->pub->unit));
1310 return BCME_UNSUPPORTED;
1311 }
1312 wlc->pub->_ampdu = on;
1313 }
1314
1315 return 0;
1316}
1317
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001318bool wlc_ampdu_cap(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001319{
1320 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
1321 return TRUE;
1322 else
1323 return FALSE;
1324}
1325
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001326static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001327{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001328 u32 rate, mcs;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001329
1330 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1331 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1332 /* 20MHz, No SGI */
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;
1338 /* 20MHz, SGI */
1339 rate = MCS_RATE(mcs, FALSE, TRUE);
1340 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1341 /* 40 MHz, SGI */
1342 rate = MCS_RATE(mcs, TRUE, TRUE);
1343 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1344 }
1345}
1346
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001347u8 BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001348wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001349 ratespec_t rspec, int phylen)
1350{
1351 scb_ampdu_t *scb_ampdu;
1352 int bytes, cnt, tmp;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001353 u8 tx_density;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001354
1355 ASSERT(scb);
1356 ASSERT(SCB_AMPDU(scb));
1357
1358 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1359 ASSERT(scb_ampdu);
1360
1361 if (scb_ampdu->mpdu_density == 0)
1362 return 0;
1363
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)
1368 */
1369
1370 tx_density = scb_ampdu->mpdu_density;
1371
1372 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1373 tmp = 1 << (17 - tx_density);
1374 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1375
1376 if (bytes > phylen) {
1377 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1378 ASSERT(cnt <= 255);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001379 return (u8) cnt;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001380 } else
1381 return 0;
1382}
1383
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001384void wlc_ampdu_macaddr_upd(wlc_info_t *wlc)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001385{
1386 char template[T_RAM_ACCESS_SZ * 2];
1387
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),
1392 template);
1393}
1394
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001395bool wlc_aggregatable(wlc_info_t *wlc, u8 tid)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001396{
Jason Cooper90ea2292010-09-14 09:45:32 -04001397 return wlc->ampdu->ini_enable[tid];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001398}
1399
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001400void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001401{
1402 wlc_info_t *wlc = ampdu->wlc;
1403
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);
1409 } else {
1410 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1411 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
1412 }
1413}