blob: 86563b5860a20962345c942bb33d6121c5eaaddb [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 */
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070016#include <linux/kernel.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070017#include <wlc_cfg.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070018#include <bcmdefs.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070019#include <osl.h>
20#include <bcmutils.h>
21#include <siutils.h>
22#include <bcmendian.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070023#include <wlioctl.h>
Brett Rudleya52ba662010-10-26 09:17:06 -070024#include <sbhndpio.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070025#include <sbhnddma.h>
26#include <hnddma.h>
27#include <d11.h>
28#include <wlc_rate.h>
29#include <wlc_pub.h>
30#include <wlc_key.h>
Brett Rudley69ec3032010-11-05 19:20:16 -070031#include <wlc_event.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>
Brett Rudley69ec3032010-11-05 19:20:16 -070039#include <wl_dbg.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070040
Henry Ptasinskia9533e72010-09-08 21:04:42 -070041
42#define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
43#define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
44#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
45#define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
46#define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
47#define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
48#define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
49#define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
50#define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
51#define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
52#define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
53#define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
54#define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
55
56#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
57#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
58 * without underflows
59 */
60#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
61#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
62#define FFPLD_PLD_INCR 1000 /* increments in bytes */
63#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
64 * accumulate between resets.
65 */
66
Jason Cooper0d706ef2010-09-14 09:45:39 -040067#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
Henry Ptasinskia9533e72010-09-08 21:04:42 -070068
69/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
70#define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
71 + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
72
73#ifdef BCMDBG
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -070074u32 wl_ampdu_dbg =
Henry Ptasinskia9533e72010-09-08 21:04:42 -070075 WL_AMPDU_UPDN_VAL |
76 WL_AMPDU_ERR_VAL |
77 WL_AMPDU_TX_VAL |
78 WL_AMPDU_RX_VAL |
79 WL_AMPDU_CTL_VAL |
80 WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
81#endif
82
83/* structure to hold tx fifo information and pre-loading state
84 * counters specific to tx underflows of ampdus
85 * some counters might be redundant with the ones in wlc or ampdu structures.
86 * This allows to maintain a specific state independantly of
87 * how often and/or when the wlc counters are updated.
88 */
89typedef struct wlc_fifo_info {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070090 u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -070091 u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070092 u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -070093 u32 accum_txfunfl; /* num of underflows since we modified pld params */
94 u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
95 u32 prev_txampdu; /* previous reading of tx ampdu */
96 u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
Henry Ptasinskia9533e72010-09-08 21:04:42 -070097} wlc_fifo_info_t;
98
99/* AMPDU module specific state */
100struct ampdu_info {
101 wlc_info_t *wlc; /* pointer to main wlc structure */
102 int scb_handle; /* scb cubby handle to retrieve data from scb */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700103 u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
104 u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
105 u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
106 u8 retry_limit; /* mpdu transmit retry limit */
107 u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
108 u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700109 /* per-tid mpdu transmit retry limit at regular rate */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700110 u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
111 u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
Greg Kroah-Hartman562c8852010-10-05 11:04:17 -0700112 s8 max_pdu; /* max pdus allowed in ampdu */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700113 u8 dur; /* max duration of an ampdu (in msec) */
114 u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
115 u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700116 u32 ffpld_rsvd; /* number of bytes to reserve for preload */
117 u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700118 void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
119 bool mfbr; /* enable multiple fallback rate */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700120 u32 tx_max_funl; /* underflows should be kept such that
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700121 * (tx_max_funfl*underflows) < tx frames
122 */
123 wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
124
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700125};
126
127#define AMPDU_CLEANUPFLAG_RX (0x1)
128#define AMPDU_CLEANUPFLAG_TX (0x2)
129
130#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
131#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
132
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400133static void wlc_ffpld_init(ampdu_info_t *ampdu);
134static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int f);
135static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700136
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400137static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
138 scb_ampdu_t *scb_ampdu,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700139 u8 tid, bool override);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400140static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700141 u8 tid, bool force);
142static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400143static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
144static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700145
146#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
147
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400148static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
Arend van Sprielc26b1372010-11-23 14:06:23 +0100149 struct sk_buff *p, tx_status_t *txs,
150 u32 frmtxstatus, u32 frmtxstatus2);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700151
Arend van Sprielc26b1372010-11-23 14:06:23 +0100152static inline u16 pkt_txh_seqnum(wlc_info_t *wlc, struct sk_buff *p)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700153{
154 d11txh_t *txh;
155 struct dot11_header *h;
Arend van Spriel54991ad2010-11-23 14:06:24 +0100156 txh = (d11txh_t *) p->data;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700157 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
Jason Cooper90ea2292010-09-14 09:45:32 -0400158 return ltoh16(h->seq) >> SEQNUM_SHIFT;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700159}
160
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700161ampdu_info_t *wlc_ampdu_attach(wlc_info_t *wlc)
Jason Coopera2627bc2010-09-14 09:45:31 -0400162{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700163 ampdu_info_t *ampdu;
164 int i;
165
166 /* some code depends on packed structures */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700167 ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700168 ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
169 ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
170 ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
171 ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
172
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200173 ampdu = kzalloc(sizeof(ampdu_info_t), GFP_ATOMIC);
Jason Cooperca8c1e52010-09-14 09:45:33 -0400174 if (!ampdu) {
mike.rapoport@gmail.com97e17d02010-10-13 00:09:09 +0200175 WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem\n", wlc->pub->unit));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700176 return NULL;
177 }
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700178 ampdu->wlc = wlc;
179
180 for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700181 ampdu->ini_enable[i] = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700182 /* Disable ampdu for VO by default */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700183 ampdu->ini_enable[PRIO_8021D_VO] = false;
184 ampdu->ini_enable[PRIO_8021D_NC] = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700185
186 /* Disable ampdu for BK by default since not enough fifo space */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700187 ampdu->ini_enable[PRIO_8021D_NONE] = false;
188 ampdu->ini_enable[PRIO_8021D_BK] = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700189
190 ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
191 ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
192 ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
193 ampdu->max_pdu = AUTO;
194 ampdu->dur = AMPDU_MAX_DUR;
195 ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
196
197 ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
198 /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
199 if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
200 ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
201 else
202 ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700203 ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
204 ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
205
206 for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
207 ampdu->retry_limit_tid[i] = ampdu->retry_limit;
208 ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
209 }
210
211 ampdu_update_max_txlen(ampdu, ampdu->dur);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700212 ampdu->mfbr = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700213 /* try to set ampdu to the default value */
214 wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
215
216 ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
217 wlc_ffpld_init(ampdu);
218
219 return ampdu;
220}
221
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700222void wlc_ampdu_detach(ampdu_info_t *ampdu)
Jason Coopera2627bc2010-09-14 09:45:31 -0400223{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700224 int i;
225
226 if (!ampdu)
227 return;
228
229 /* free all ini's which were to be freed on callbacks which were never called */
230 for (i = 0; i < AMPDU_INI_FREE; i++) {
231 if (ampdu->ini_free[i]) {
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200232 kfree(ampdu->ini_free[i]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700233 }
234 }
235
236 wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200237 kfree(ampdu);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700238}
239
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400240void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700241{
242 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700243 u8 tid;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700244
245 WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
246 ASSERT(scb_ampdu);
247
248 for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700249 ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700250 }
251}
252
253/* reset the ampdu state machine so that it can gracefully handle packets that were
254 * freed from the dma and tx queues during reinit
255 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400256void wlc_ampdu_reset(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700257{
258 WL_NONE(("%s: Entering\n", __func__));
259}
260
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400261static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700262{
263 scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
264 int i;
265
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700266 scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700267
268 /* go back to legacy size if some preloading is occuring */
269 for (i = 0; i < NUM_FFPLD_FIFO; i++) {
270 if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
271 scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
272 }
273
274 /* apply user override */
275 if (ampdu->max_pdu != AUTO)
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700276 scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700277
Greg Kroah-Hartman697d6002010-10-08 12:25:23 -0700278 scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700279
280 if (scb_ampdu->max_rxlen)
281 scb_ampdu->release =
Greg Kroah-Hartman697d6002010-10-08 12:25:23 -0700282 min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700283
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700284 scb_ampdu->release = min(scb_ampdu->release,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700285 ampdu->fifo_tb[TX_AC_BE_FIFO].
286 mcs2ampdu_table[FFPLD_MAX_MCS]);
287
288 ASSERT(scb_ampdu->release);
289}
290
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400291void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700292{
293 scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
294}
295
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400296static void wlc_ffpld_init(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700297{
298 int i, j;
299 wlc_fifo_info_t *fifo;
300
301 for (j = 0; j < NUM_FFPLD_FIFO; j++) {
302 fifo = (ampdu->fifo_tb + j);
303 fifo->ampdu_pld_size = 0;
304 for (i = 0; i <= FFPLD_MAX_MCS; i++)
305 fifo->mcs2ampdu_table[i] = 255;
306 fifo->dmaxferrate = 0;
307 fifo->accum_txampdu = 0;
308 fifo->prev_txfunfl = 0;
309 fifo->accum_txfunfl = 0;
310
311 }
312}
313
314/* evaluate the dma transfer rate using the tx underflows as feedback.
315 * If necessary, increase tx fifo preloading. If not enough,
316 * decrease maximum ampdu size for each mcs till underflows stop
317 * Return 1 if pre-loading not active, -1 if not an underflow event,
318 * 0 if pre-loading module took care of the event.
319 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400320static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int fid)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700321{
322 ampdu_info_t *ampdu = wlc->ampdu;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700323 u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700324 u32 txunfl_ratio;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700325 u8 max_mpdu;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700326 u32 current_ampdu_cnt = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700327 u16 max_pld_size;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700328 u32 new_txunfl;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700329 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
330 uint xmtfifo_sz;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700331 u16 cur_txunfl;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700332
333 /* return if we got here for a different reason than underflows */
334 cur_txunfl =
335 wlc_read_shm(wlc,
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -0700336 M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700337 new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700338 if (new_txunfl == 0) {
339 WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
340 return -1;
341 }
342 fifo->prev_txfunfl = cur_txunfl;
343
344 if (!ampdu->tx_max_funl)
345 return 1;
346
347 /* check if fifo is big enough */
348 if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
349 WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
350 return -1;
351 }
352
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700353 if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700354 return 1;
355
356 max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
357 fifo->accum_txfunfl += new_txunfl;
358
359 /* we need to wait for at least 10 underflows */
360 if (fifo->accum_txfunfl < 10)
361 return 0;
362
363 WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
364 current_ampdu_cnt, fifo->accum_txfunfl));
365
366 /*
367 compute the current ratio of tx unfl per ampdu.
368 When the current ampdu count becomes too
369 big while the ratio remains small, we reset
370 the current count in order to not
371 introduce too big of a latency in detecting a
372 large amount of tx underflows later.
373 */
374
375 txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
376
377 if (txunfl_ratio > ampdu->tx_max_funl) {
378 if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
379 fifo->accum_txfunfl = 0;
380 }
381 return 0;
382 }
383 max_mpdu =
Greg Kroah-Hartman697d6002010-10-08 12:25:23 -0700384 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700385
386 /* In case max value max_pdu is already lower than
387 the fifo depth, there is nothing more we can do.
388 */
389
390 if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
391 WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
392 fifo->accum_txfunfl = 0;
393 return 0;
394 }
395
396 if (fifo->ampdu_pld_size < max_pld_size) {
397
398 /* increment by TX_FIFO_PLD_INC bytes */
399 fifo->ampdu_pld_size += FFPLD_PLD_INCR;
400 if (fifo->ampdu_pld_size > max_pld_size)
401 fifo->ampdu_pld_size = max_pld_size;
402
403 /* update scb release size */
404 scb_ampdu_update_config_all(ampdu);
405
406 /*
407 compute a new dma xfer rate for max_mpdu @ max mcs.
408 This is the minimum dma rate that
409 can acheive no unferflow condition for the current mpdu size.
410 */
411 /* note : we divide/multiply by 100 to avoid integer overflows */
412 fifo->dmaxferrate =
413 (((phy_rate / 100) *
414 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
415 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
416
417 WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
418 fifo->dmaxferrate, fifo->ampdu_pld_size));
419 } else {
420
421 /* decrease ampdu size */
422 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
423 if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
424 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
425 AMPDU_NUM_MPDU_LEGACY - 1;
426 else
427 fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
428
429 /* recompute the table */
430 wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
431
432 /* update scb release size */
433 scb_ampdu_update_config_all(ampdu);
434 }
435 }
436 fifo->accum_txfunfl = 0;
437 return 0;
438}
439
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400440static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700441{
442 int i;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700443 u32 phy_rate, dma_rate, tmp;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700444 u8 max_mpdu;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700445 wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
446
447 /* recompute the dma rate */
448 /* note : we divide/multiply by 100 to avoid integer overflows */
449 max_mpdu =
Greg Kroah-Hartman697d6002010-10-08 12:25:23 -0700450 min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700451 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700452 dma_rate =
453 (((phy_rate / 100) *
454 (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
455 / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
456 fifo->dmaxferrate = dma_rate;
457
458 /* fill up the mcs2ampdu table; do not recalc the last mcs */
459 dma_rate = dma_rate >> 7;
460 for (i = 0; i < FFPLD_MAX_MCS; i++) {
461 /* shifting to keep it within integer range */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700462 phy_rate = MCS_RATE(i, true, false) >> 7;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700463 if (phy_rate > dma_rate) {
464 tmp = ((fifo->ampdu_pld_size * phy_rate) /
465 ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
Greg Kroah-Hartman697d6002010-10-08 12:25:23 -0700466 tmp = min_t(u32, tmp, 255);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700467 fifo->mcs2ampdu_table[i] = (u8) tmp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700468 }
469 }
470}
471
472static void BCMFASTPATH
Arend van Sprielc26b1372010-11-23 14:06:23 +0100473wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, struct sk_buff *p,
474 uint prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700475{
476 scb_ampdu_t *scb_ampdu;
477 scb_ampdu_tid_ini_t *ini;
Arend van Spriel54991ad2010-11-23 14:06:24 +0100478 u8 tid = (u8) (p->priority);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700479
480 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
481
482 /* initialize initiator on first packet; sends addba req */
483 ini = SCB_AMPDU_INI(scb_ampdu, tid);
484 if (ini->magic != INI_MAGIC) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700485 ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700486 }
487 return;
488}
489
490int BCMFASTPATH
Arend van Sprielc26b1372010-11-23 14:06:23 +0100491wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, struct sk_buff **pdu,
492 int prec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700493{
494 wlc_info_t *wlc;
Brett Rudleye69284f2010-11-16 15:45:48 -0800495 struct osl_info *osh;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100496 struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700497 u8 tid, ndelim;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700498 int err = 0;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700499 u8 preamble_type = WLC_GF_PREAMBLE;
500 u8 fbr_preamble_type = WLC_GF_PREAMBLE;
501 u8 rts_preamble_type = WLC_LONG_PREAMBLE;
502 u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700503
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700504 bool rr = true, fbr = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700505 uint i, count = 0, fifo, seg_cnt = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700506 u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700507 u32 ampdu_len, maxlen = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700508 d11txh_t *txh = NULL;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700509 u8 *plcp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700510 struct dot11_header *h;
511 struct scb *scb;
512 scb_ampdu_t *scb_ampdu;
513 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700514 u8 mcs = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700515 bool use_rts = false, use_cts = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700516 ratespec_t rspec = 0, rspec_fallback = 0;
517 ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700518 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700519 struct dot11_rts_frame *rts;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700520 u8 rr_retry_limit;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700521 wlc_fifo_info_t *f;
522 bool fbr_iscck;
523 struct ieee80211_tx_info *tx_info;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700524 u16 qlen;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700525
526 wlc = ampdu->wlc;
527 osh = wlc->osh;
528 p = *pdu;
529
530 ASSERT(p);
531
Arend van Spriel54991ad2010-11-23 14:06:24 +0100532 tid = (u8) (p->priority);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700533 ASSERT(tid < AMPDU_MAX_SCB_TID);
534
535 f = ampdu->fifo_tb + prio2fifo[tid];
536
537 scb = wlc->pub->global_scb;
538 ASSERT(scb->magic == SCB_MAGIC);
539
540 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
541 ASSERT(scb_ampdu);
542 ini = &scb_ampdu->ini[tid];
543
544 /* Let pressure continue to build ... */
545 qlen = pktq_plen(&qi->q, prec);
546 if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
547 return BCME_BUSY;
548 }
549
550 wlc_ampdu_agg(ampdu, scb, p, tid);
551
552 if (wlc->block_datafifo) {
553 WL_ERROR(("%s: Fifo blocked\n", __func__));
554 return BCME_BUSY;
555 }
556 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
557 ampdu_len = 0;
558 dma_len = 0;
559 while (p) {
560 struct ieee80211_tx_rate *txrate;
561
562 tx_info = IEEE80211_SKB_CB(p);
563 txrate = tx_info->status.rates;
564
565 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
566 err = wlc_prep_pdu(wlc, p, &fifo);
567 } else {
568 WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
569 *pdu = NULL;
570 err = 0;
571 break;
572 }
573
574 if (err) {
575 if (err == BCME_BUSY) {
576 WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq));
577 WLCNTINCR(ampdu->cnt->sduretry);
578 *pdu = p;
579 break;
580 }
581
582 /* error in the packet; reject it */
583 WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc->pub->unit, seq));
584 WLCNTINCR(ampdu->cnt->sdurejected);
585
586 *pdu = NULL;
587 break;
588 }
589
590 /* pkt is good to be aggregated */
591 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
Arend van Spriel54991ad2010-11-23 14:06:24 +0100592 txh = (d11txh_t *) p->data;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700593 plcp = (u8 *) (txh + 1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700594 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
595 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
596 index = TX_SEQ_TO_INDEX(seq);
597
598 /* check mcl fields and test whether it can be agg'd */
599 mcl = ltoh16(txh->MacTxControlLow);
600 mcl &= ~TXC_AMPDU_MASK;
601 fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
602 ASSERT(!fbr_iscck);
603 txh->PreloadSize = 0; /* always default to 0 */
604
605 /* Handle retry limits */
606 if (txrate[0].count <= rr_retry_limit) {
607 txrate[0].count++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700608 rr = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700609 fbr = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700610 ASSERT(!fbr);
611 } else {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700612 fbr = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700613 rr = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700614 txrate[1].count++;
615 }
616
617 /* extract the length info */
618 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
619 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
620
621 /* retrieve null delimiter count */
622 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
623 seg_cnt += 1;
624
625 WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
626 wlc->pub->unit, count, len));
627
628 /*
629 * aggregateable mpdu. For ucode/hw agg,
630 * test whether need to break or change the epoch
631 */
632 if (count == 0) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700633 u16 fc;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700634 mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
635 /* refill the bits since might be a retx mpdu */
636 mcl |= TXC_STARTMSDU;
637 rts = (struct dot11_rts_frame *)&txh->rts_frame;
638 fc = ltoh16(rts->fc);
639 if ((fc & FC_KIND_MASK) == FC_RTS) {
640 mcl |= TXC_SENDRTS;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700641 use_rts = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700642 }
643 if ((fc & FC_KIND_MASK) == FC_CTS) {
644 mcl |= TXC_SENDCTS;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700645 use_cts = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700646 }
647 } else {
648 mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
649 mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
650 }
651
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -0700652 len = roundup(len, 4);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700653 ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
654
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700655 dma_len += (u16) pkttotlen(osh, p);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700656
657 WL_AMPDU_TX(("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n", wlc->pub->unit, ampdu_len, seg_cnt, ndelim));
658
659 txh->MacTxControlLow = htol16(mcl);
660
661 /* this packet is added */
662 pkt[count++] = p;
663
664 /* patch the first MPDU */
665 if (count == 1) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700666 u8 plcp0, plcp3, is40, sgi;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700667 struct ieee80211_sta *sta;
668
669 sta = tx_info->control.sta;
670
671 if (rr) {
672 plcp0 = plcp[0];
673 plcp3 = plcp[3];
674 } else {
675 plcp0 = txh->FragPLCPFallback[0];
676 plcp3 = txh->FragPLCPFallback[3];
677
678 }
679 is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
680 sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
681 mcs = plcp0 & ~MIMO_PLCP_40MHZ;
682 ASSERT(mcs < MCS_TABLE_SIZE);
683 maxlen =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700684 min(scb_ampdu->max_rxlen,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700685 ampdu->max_txlen[mcs][is40][sgi]);
686
687 WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
688 is40, mcs));
689
690 maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
691
692 if (is40)
693 mimo_ctlchbw =
694 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
695 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
696
697 /* rebuild the rspec and rspec_fallback */
698 rspec = RSPEC_MIMORATE;
699 rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
700 if (plcp[0] & MIMO_PLCP_40MHZ)
701 rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
702
703 if (fbr_iscck) /* CCK */
704 rspec_fallback =
705 CCK_RSPEC(CCK_PHY2MAC_RATE
706 (txh->FragPLCPFallback[0]));
707 else { /* MIMO */
708 rspec_fallback = RSPEC_MIMORATE;
709 rspec_fallback |=
710 txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
711 if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
712 rspec_fallback |=
713 (PHY_TXC1_BW_40MHZ <<
714 RSPEC_BW_SHIFT);
715 }
716
717 if (use_rts || use_cts) {
718 rts_rspec =
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700719 wlc_rspec_to_rts_rspec(wlc, rspec, false,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700720 mimo_ctlchbw);
721 rts_rspec_fallback =
722 wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700723 false, mimo_ctlchbw);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700724 }
725 }
726
727 /* if (first mpdu for host agg) */
728 /* test whether to add more */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700729 if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700730 (count == f->mcs2ampdu_table[mcs])) {
731 WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc->pub->unit, count, mcs));
732 break;
733 }
734
735 if (count == scb_ampdu->max_pdu) {
736 WL_NONE(("Stop taking from q, reached %d deep\n",
737 scb_ampdu->max_pdu));
738 break;
739 }
740
741 /* check to see if the next pkt is a candidate for aggregation */
742 p = pktq_ppeek(&qi->q, prec);
743 tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
744
745 if (p) {
746 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
Arend van Spriel54991ad2010-11-23 14:06:24 +0100747 ((u8) (p->priority) == tid)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700748
749 plen =
750 pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -0700751 plen = max(scb_ampdu->min_len, plen);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700752
753 if ((plen + ampdu_len) > maxlen) {
754 p = NULL;
755 WL_ERROR(("%s: Bogus plen #1\n",
756 __func__));
757 ASSERT(3 == 4);
758 continue;
759 }
760
761 /* check if there are enough descriptors available */
762 if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
763 WL_ERROR(("%s: No fifo space !!!!!!\n", __func__));
764 p = NULL;
765 continue;
766 }
767 p = pktq_pdeq(&qi->q, prec);
768 ASSERT(p);
769 } else {
770 p = NULL;
771 }
772 }
773 } /* end while(p) */
774
775 ini->tx_in_transit += count;
776
777 if (count) {
778 WLCNTADD(ampdu->cnt->txmpdu, count);
779
780 /* patch up the last txh */
Arend van Spriel54991ad2010-11-23 14:06:24 +0100781 txh = (d11txh_t *) pkt[count - 1]->data;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700782 mcl = ltoh16(txh->MacTxControlLow);
783 mcl &= ~TXC_AMPDU_MASK;
784 mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
785 txh->MacTxControlLow = htol16(mcl);
786
787 /* remove the null delimiter after last mpdu */
788 ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
789 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
790 ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
791
792 /* remove the pad len from last mpdu */
793 fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
794 len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
795 : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -0700796 ampdu_len -= roundup(len, 4) - len;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700797
798 /* patch up the first txh & plcp */
Arend van Spriel54991ad2010-11-23 14:06:24 +0100799 txh = (d11txh_t *) pkt[0]->data;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700800 plcp = (u8 *) (txh + 1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700801
802 WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
803 /* mark plcp to indicate ampdu */
804 WLC_SET_MIMO_PLCP_AMPDU(plcp);
805
806 /* reset the mixed mode header durations */
807 if (txh->MModeLen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700808 u16 mmodelen =
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700809 wlc_calc_lsig_len(wlc, rspec, ampdu_len);
810 txh->MModeLen = htol16(mmodelen);
811 preamble_type = WLC_MM_PREAMBLE;
812 }
813 if (txh->MModeFbrLen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700814 u16 mmfbrlen =
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700815 wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
816 txh->MModeFbrLen = htol16(mmfbrlen);
817 fbr_preamble_type = WLC_MM_PREAMBLE;
818 }
819
820 /* set the preload length */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700821 if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -0700822 dma_len = min(dma_len, f->ampdu_pld_size);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700823 txh->PreloadSize = htol16(dma_len);
824 } else
825 txh->PreloadSize = 0;
826
827 mch = ltoh16(txh->MacTxControlHigh);
828
829 /* update RTS dur fields */
830 if (use_rts || use_cts) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700831 u16 durid;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700832 rts = (struct dot11_rts_frame *)&txh->rts_frame;
833 if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
834 TXC_PREAMBLE_RTS_MAIN_SHORT)
835 rts_preamble_type = WLC_SHORT_PREAMBLE;
836
837 if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
838 TXC_PREAMBLE_RTS_FB_SHORT)
839 rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
840
841 durid =
842 wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
843 rspec, rts_preamble_type,
844 preamble_type, ampdu_len,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700845 true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700846 rts->durid = htol16(durid);
847 durid = wlc_compute_rtscts_dur(wlc, use_cts,
848 rts_rspec_fallback,
849 rspec_fallback,
850 rts_fbr_preamble_type,
851 fbr_preamble_type,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700852 ampdu_len, true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700853 txh->RTSDurFallback = htol16(durid);
854 /* set TxFesTimeNormal */
855 txh->TxFesTimeNormal = rts->durid;
856 /* set fallback rate version of TxFesTimeNormal */
857 txh->TxFesTimeFallback = txh->RTSDurFallback;
858 }
859
860 /* set flag and plcp for fallback rate */
861 if (fbr) {
862 WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
863 WLCNTINCR(ampdu->cnt->txfbr_ampdu);
864 mch |= TXC_AMPDU_FBR;
865 txh->MacTxControlHigh = htol16(mch);
866 WLC_SET_MIMO_PLCP_AMPDU(plcp);
867 WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
868 }
869
870 WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
871 wlc->pub->unit, count, ampdu_len));
872
873 /* inform rate_sel if it this is a rate probe pkt */
874 frameid = ltoh16(txh->TxFrameID);
875 if (frameid & TXFID_RATE_PROBE_MASK) {
876 WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__));
877 }
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700878 for (i = 0; i < count; i++)
879 wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
880 ampdu->txpkt_weight);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700881
882 }
883 /* endif (count) */
884 return err;
885}
886
887void BCMFASTPATH
Arend van Sprielc26b1372010-11-23 14:06:23 +0100888wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, struct sk_buff *p,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400889 tx_status_t *txs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700890{
891 scb_ampdu_t *scb_ampdu;
892 wlc_info_t *wlc = ampdu->wlc;
893 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700894 u32 s1 = 0, s2 = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700895 struct ieee80211_tx_info *tx_info;
896
897 tx_info = IEEE80211_SKB_CB(p);
898 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
899 ASSERT(scb);
900 ASSERT(scb->magic == SCB_MAGIC);
901 ASSERT(txs->status & TX_STATUS_AMPDU);
902 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
903 ASSERT(scb_ampdu);
Arend van Spriel54991ad2010-11-23 14:06:24 +0100904 ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700905 ASSERT(ini->scb == scb);
906
907 /* BMAC_NOTE: For the split driver, second level txstatus comes later
908 * So if the ACK was received then wait for the second level else just
909 * call the first one
910 */
911 if (txs->status & TX_STATUS_ACK_RCV) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700912 u8 status_delay = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700913
914 /* wait till the next 8 bytes of txstatus is available */
915 while (((s1 =
916 R_REG(wlc->osh,
917 &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
mike.rapoport@gmail.com73831412010-10-13 00:09:07 +0200918 udelay(1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700919 status_delay++;
920 if (status_delay > 10) {
921 ASSERT(status_delay <= 10);
922 return;
923 }
924 }
925
926 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
927 ASSERT(s1 & TX_STATUS_AMPDU);
928 s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700929 }
930
931 wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
932 wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
933}
934
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400935void rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700936 tx_status_t *txs, u8 mcs);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700937
938void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400939rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700940 tx_status_t *txs, u8 mcs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700941{
942 struct ieee80211_tx_rate *txrate = tx_info->status.rates;
943 int i;
944
945 /* clear the rest of the rates */
946 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
947 txrate[i].idx = -1;
948 txrate[i].count = 0;
949 }
950}
951
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700952#define SHORTNAME "AMPDU status"
953
954static void BCMFASTPATH
Arend van Sprielc26b1372010-11-23 14:06:23 +0100955wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
956 struct sk_buff *p, tx_status_t *txs,
957 u32 s1, u32 s2)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700958{
959 scb_ampdu_t *scb_ampdu;
960 wlc_info_t *wlc = ampdu->wlc;
961 scb_ampdu_tid_ini_t *ini;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700962 u8 bitmap[8], queue, tid;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700963 d11txh_t *txh;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700964 u8 *plcp;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700965 struct dot11_header *h;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700966 u16 seq, start_seq = 0, bindex, index, mcl;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700967 u8 mcs = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700968 bool ba_recd = false, ack_recd = false;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700969 u8 suc_mpdu = 0, tot_mpdu = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700970 uint supr_status;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700971 bool update_rate = true, retry = true, tx_error = false;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700972 u16 mimoantsel = 0;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700973 u8 antselid = 0;
974 u8 retry_limit, rr_retry_limit;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700975 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
976
977#ifdef BCMDBG
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700978 u8 hole[AMPDU_MAX_MPDU];
Brett Rudley9249ede2010-11-30 20:09:49 -0800979 memset(hole, 0, sizeof(hole));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700980#endif
981
982 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
983 ASSERT(txs->status & TX_STATUS_AMPDU);
984
985 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
986 ASSERT(scb_ampdu);
987
Arend van Spriel54991ad2010-11-23 14:06:24 +0100988 tid = (u8) (p->priority);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700989
990 ini = SCB_AMPDU_INI(scb_ampdu, tid);
991 retry_limit = ampdu->retry_limit_tid[tid];
992 rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
993
994 ASSERT(ini->scb == scb);
995
Brett Rudley9249ede2010-11-30 20:09:49 -0800996 memset(bitmap, 0, sizeof(bitmap));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700997 queue = txs->frameid & TXFID_QUEUE_MASK;
998 ASSERT(queue < AC_COUNT);
999
1000 supr_status = txs->status & TX_STATUS_SUPR_MASK;
1001
1002 if (txs->status & TX_STATUS_ACK_RCV) {
1003 if (TX_STATUS_SUPR_UF == supr_status) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001004 update_rate = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001005 }
1006
1007 ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
1008 start_seq = txs->sequence >> SEQNUM_SHIFT;
1009 bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
1010 TX_STATUS_BA_BMAP03_SHIFT;
1011
1012 ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
1013 ASSERT(s1 & TX_STATUS_AMPDU);
1014
1015 bitmap[0] |=
1016 (s1 & TX_STATUS_BA_BMAP47_MASK) <<
1017 TX_STATUS_BA_BMAP47_SHIFT;
1018 bitmap[1] = (s1 >> 8) & 0xff;
1019 bitmap[2] = (s1 >> 16) & 0xff;
1020 bitmap[3] = (s1 >> 24) & 0xff;
1021
1022 bitmap[4] = s2 & 0xff;
1023 bitmap[5] = (s2 >> 8) & 0xff;
1024 bitmap[6] = (s2 >> 16) & 0xff;
1025 bitmap[7] = (s2 >> 24) & 0xff;
1026
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001027 ba_recd = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001028 } else {
1029 WLCNTINCR(ampdu->cnt->noba);
1030 if (supr_status) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001031 update_rate = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001032 if (supr_status == TX_STATUS_SUPR_BADCH) {
1033 WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
1034 } else {
1035 if (supr_status == TX_STATUS_SUPR_FRAG)
1036 WL_NONE(("%s: AMPDU frag err\n",
1037 __func__));
1038 else
1039 WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
1040 }
1041 /* no need to retry for badch; will fail again */
1042 if (supr_status == TX_STATUS_SUPR_BADCH ||
1043 supr_status == TX_STATUS_SUPR_EXPTIME) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001044 retry = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001045 WLCNTINCR(wlc->pub->_cnt->txchanrej);
1046 } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
1047
1048 WLCNTINCR(wlc->pub->_cnt->txexptime);
1049
1050 /* TX underflow : try tuning pre-loading or ampdu size */
1051 } else if (supr_status == TX_STATUS_SUPR_FRAG) {
1052 /* if there were underflows, but pre-loading is not active,
1053 notify rate adaptation.
1054 */
1055 if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
1056 > 0) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001057 tx_error = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001058 }
1059 }
1060 } else if (txs->phyerr) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001061 update_rate = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001062 WLCNTINCR(wlc->pub->_cnt->txphyerr);
1063 WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc->pub->unit, txs->phyerr));
1064
1065#ifdef BCMDBG
1066 if (WL_ERROR_ON()) {
1067 prpkt("txpkt (AMPDU)", wlc->osh, p);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001068 wlc_print_txdesc((d11txh_t *) p->data);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001069 wlc_print_txstatus(txs);
1070 }
1071#endif /* BCMDBG */
1072 }
1073 }
1074
1075 /* loop through all pkts and retry if not acked */
1076 while (p) {
1077 tx_info = IEEE80211_SKB_CB(p);
1078 ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001079 txh = (d11txh_t *) p->data;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001080 mcl = ltoh16(txh->MacTxControlLow);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001081 plcp = (u8 *) (txh + 1);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001082 h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
1083 seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
1084
1085 if (tot_mpdu == 0) {
1086 mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
1087 mimoantsel = ltoh16(txh->ABI_MimoAntSel);
1088 }
1089
1090 index = TX_SEQ_TO_INDEX(seq);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001091 ack_recd = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001092 if (ba_recd) {
1093 bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
1094
1095 WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
1096 "bindex is %d set %d, index %d\n",
1097 __func__, tid, seq, start_seq, bindex,
1098 isset(bitmap, bindex), index));
1099
1100 /* if acked then clear bit and free packet */
1101 if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
1102 && isset(bitmap, bindex)) {
1103 ini->tx_in_transit--;
1104 ini->txretry[index] = 0;
1105
1106 /* ampdu_ack_len: number of acked aggregated frames */
1107 /* ampdu_ack_map: block ack bit map for the aggregation */
1108 /* ampdu_len: number of aggregated frames */
1109 rate_status(wlc, tx_info, txs, mcs);
1110 tx_info->flags |= IEEE80211_TX_STAT_ACK;
1111 tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
1112
1113 /* XXX TODO: Make these accurate. */
1114 tx_info->status.ampdu_ack_len =
1115 (txs->
1116 status & TX_STATUS_FRM_RTX_MASK) >>
1117 TX_STATUS_FRM_RTX_SHIFT;
1118 tx_info->status.ampdu_len =
1119 (txs->
1120 status & TX_STATUS_FRM_RTX_MASK) >>
1121 TX_STATUS_FRM_RTX_SHIFT;
1122
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001123 skb_pull(p, D11_PHY_HDR_LEN);
1124 skb_pull(p, D11_TXH_LEN);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001125
1126 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1127 p);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001128 ack_recd = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001129 suc_mpdu++;
1130 }
1131 }
1132 /* either retransmit or send bar if ack not recd */
1133 if (!ack_recd) {
1134 struct ieee80211_tx_rate *txrate =
1135 tx_info->status.rates;
1136 if (retry && (txrate[0].count < (int)retry_limit)) {
1137 ini->txretry[index]++;
1138 ini->tx_in_transit--;
1139 /* Use high prededence for retransmit to give some punch */
1140 /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
1141 wlc_txq_enq(wlc, scb, p,
1142 WLC_PRIO_TO_HI_PREC(tid));
1143 } else {
1144 /* Retry timeout */
1145 ini->tx_in_transit--;
1146 ieee80211_tx_info_clear_status(tx_info);
1147 tx_info->flags |=
1148 IEEE80211_TX_STAT_AMPDU_NO_BACK;
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001149 skb_pull(p, D11_PHY_HDR_LEN);
1150 skb_pull(p, D11_TXH_LEN);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001151 WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME, seq, ini->tx_in_transit));
1152 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
1153 p);
1154 }
1155 }
1156 tot_mpdu++;
1157
1158 /* break out if last packet of ampdu */
1159 if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
1160 TXC_AMPDU_LAST)
1161 break;
1162
1163 p = GETNEXTTXP(wlc, queue);
1164 if (p == NULL) {
1165 ASSERT(p);
1166 break;
1167 }
1168 }
1169 wlc_send_q(wlc, wlc->active_queue);
1170
1171 /* update rate state */
1172 if (WLANTSEL_ENAB(wlc))
1173 antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
1174
1175 wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
1176}
1177
1178static void
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001179ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001180 bool force)
1181{
1182 scb_ampdu_tid_ini_t *ini;
Jason Cooperca8c1e52010-09-14 09:45:33 -04001183 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1184 if (!ini)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001185 return;
1186
1187 WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
1188 ampdu->wlc->pub->unit, tid));
1189
1190 if (ini->tx_in_transit && !force)
1191 return;
1192
1193 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
1194 ASSERT(ini == &scb_ampdu->ini[ini->tid]);
1195
1196 /* free all buffered tx packets */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001197 pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001198}
1199
1200/* initialize the initiator code for tid */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001201static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
1202 scb_ampdu_t *scb_ampdu,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001203 u8 tid, bool override)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001204{
1205 scb_ampdu_tid_ini_t *ini;
1206
1207 ASSERT(scb_ampdu);
1208 ASSERT(scb_ampdu->scb);
1209 ASSERT(SCB_AMPDU(scb_ampdu->scb));
1210 ASSERT(tid < AMPDU_MAX_SCB_TID);
1211
1212 /* check for per-tid control of ampdu */
1213 if (!ampdu->ini_enable[tid]) {
1214 WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
1215 return NULL;
1216 }
1217
1218 ini = SCB_AMPDU_INI(scb_ampdu, tid);
1219 ini->tid = tid;
1220 ini->scb = scb_ampdu->scb;
1221 ini->magic = INI_MAGIC;
1222 WLCNTINCR(ampdu->cnt->txaddbareq);
1223
1224 return ini;
1225}
1226
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001227int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001228{
1229 wlc_info_t *wlc = ampdu->wlc;
1230
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001231 wlc->pub->_ampdu = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001232
1233 if (on) {
1234 if (!N_ENAB(wlc->pub)) {
1235 WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
1236 wlc->pub->unit));
1237 return BCME_UNSUPPORTED;
1238 }
1239 if (!wlc_ampdu_cap(ampdu)) {
1240 WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
1241 wlc->pub->unit));
1242 return BCME_UNSUPPORTED;
1243 }
1244 wlc->pub->_ampdu = on;
1245 }
1246
1247 return 0;
1248}
1249
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001250bool wlc_ampdu_cap(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001251{
1252 if (WLC_PHY_11N_CAP(ampdu->wlc->band))
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001253 return true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001254 else
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001255 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001256}
1257
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001258static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001259{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001260 u32 rate, mcs;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001261
1262 for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1263 /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1264 /* 20MHz, No SGI */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001265 rate = MCS_RATE(mcs, false, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001266 ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1267 /* 40 MHz, No SGI */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001268 rate = MCS_RATE(mcs, true, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001269 ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1270 /* 20MHz, SGI */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001271 rate = MCS_RATE(mcs, false, true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001272 ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1273 /* 40 MHz, SGI */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001274 rate = MCS_RATE(mcs, true, true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001275 ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1276 }
1277}
1278
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001279u8 BCMFASTPATH
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001280wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001281 ratespec_t rspec, int phylen)
1282{
1283 scb_ampdu_t *scb_ampdu;
1284 int bytes, cnt, tmp;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001285 u8 tx_density;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001286
1287 ASSERT(scb);
1288 ASSERT(SCB_AMPDU(scb));
1289
1290 scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
1291 ASSERT(scb_ampdu);
1292
1293 if (scb_ampdu->mpdu_density == 0)
1294 return 0;
1295
1296 /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
1297 density x is in 2^(x-4) usec
1298 ==> # of bytes needed for req density = rate/2^(17-x)
1299 ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
1300 */
1301
1302 tx_density = scb_ampdu->mpdu_density;
1303
1304 ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
1305 tmp = 1 << (17 - tx_density);
1306 bytes = CEIL(RSPEC2RATE(rspec), tmp);
1307
1308 if (bytes > phylen) {
1309 cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
1310 ASSERT(cnt <= 255);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001311 return (u8) cnt;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001312 } else
1313 return 0;
1314}
1315
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001316void wlc_ampdu_macaddr_upd(wlc_info_t *wlc)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001317{
1318 char template[T_RAM_ACCESS_SZ * 2];
1319
1320 /* driver needs to write the ta in the template; ta is at offset 16 */
Brett Rudley9249ede2010-11-30 20:09:49 -08001321 memset(template, 0, sizeof(template));
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001322 bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETHER_ADDR_LEN);
1323 wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
1324 template);
1325}
1326
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001327bool wlc_aggregatable(wlc_info_t *wlc, u8 tid)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001328{
Jason Cooper90ea2292010-09-14 09:45:32 -04001329 return wlc->ampdu->ini_enable[tid];
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001330}
1331
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001332void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001333{
1334 wlc_info_t *wlc = ampdu->wlc;
1335
1336 /* Extend ucode internal watchdog timer to match larger received frames */
1337 if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
1338 AMPDU_RX_FACTOR_64K) {
1339 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
1340 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
1341 } else {
1342 wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
1343 wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
1344 }
1345}