~ubuntu-branches/ubuntu/natty/linux-backports-modules-2.6.38/natty-updates

« back to all changes in this revision

Viewing changes to updates/compat-wireless-2.6.37/drivers/staging/brcm80211/sys/wlc_ampdu.c

  • Committer: Bazaar Package Importer
  • Author(s): Tim Gardner, Tim Gardner
  • Date: 2011-06-08 10:44:09 UTC
  • Revision ID: james.westby@ubuntu.com-20110608104409-fnl8carkdo15bwsz
Tags: 2.6.38-10.6
[ Tim Gardner ]

Shorten compat-wireless package name to cw to accomodate
CDROM file name length restrictions.

Show diffs side-by-side

added added

removed removed

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