18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 118c2ecf20Sopenharmony_ci * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 138c2ecf20Sopenharmony_ci * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 148c2ecf20Sopenharmony_ci * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci#include <net/mac80211.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "rate.h" 198c2ecf20Sopenharmony_ci#include "scb.h" 208c2ecf20Sopenharmony_ci#include "phy/phy_hal.h" 218c2ecf20Sopenharmony_ci#include "antsel.h" 228c2ecf20Sopenharmony_ci#include "main.h" 238c2ecf20Sopenharmony_ci#include "ampdu.h" 248c2ecf20Sopenharmony_ci#include "debug.h" 258c2ecf20Sopenharmony_ci#include "brcms_trace_events.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* max number of mpdus in an ampdu */ 288c2ecf20Sopenharmony_ci#define AMPDU_MAX_MPDU 32 298c2ecf20Sopenharmony_ci/* max number of mpdus in an ampdu to a legacy */ 308c2ecf20Sopenharmony_ci#define AMPDU_NUM_MPDU_LEGACY 16 318c2ecf20Sopenharmony_ci/* max Tx ba window size (in pdu) */ 328c2ecf20Sopenharmony_ci#define AMPDU_TX_BA_MAX_WSIZE 64 338c2ecf20Sopenharmony_ci/* default Tx ba window size (in pdu) */ 348c2ecf20Sopenharmony_ci#define AMPDU_TX_BA_DEF_WSIZE 64 358c2ecf20Sopenharmony_ci/* default Rx ba window size (in pdu) */ 368c2ecf20Sopenharmony_ci#define AMPDU_RX_BA_DEF_WSIZE 64 378c2ecf20Sopenharmony_ci/* max Rx ba window size (in pdu) */ 388c2ecf20Sopenharmony_ci#define AMPDU_RX_BA_MAX_WSIZE 64 398c2ecf20Sopenharmony_ci/* max dur of tx ampdu (in msec) */ 408c2ecf20Sopenharmony_ci#define AMPDU_MAX_DUR 5 418c2ecf20Sopenharmony_ci/* default tx retry limit */ 428c2ecf20Sopenharmony_ci#define AMPDU_DEF_RETRY_LIMIT 5 438c2ecf20Sopenharmony_ci/* default tx retry limit at reg rate */ 448c2ecf20Sopenharmony_ci#define AMPDU_DEF_RR_RETRY_LIMIT 2 458c2ecf20Sopenharmony_ci/* default ffpld reserved bytes */ 468c2ecf20Sopenharmony_ci#define AMPDU_DEF_FFPLD_RSVD 2048 478c2ecf20Sopenharmony_ci/* # of inis to be freed on detach */ 488c2ecf20Sopenharmony_ci#define AMPDU_INI_FREE 10 498c2ecf20Sopenharmony_ci/* max # of mpdus released at a time */ 508c2ecf20Sopenharmony_ci#define AMPDU_SCB_MAX_RELEASE 20 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */ 538c2ecf20Sopenharmony_ci#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu 548c2ecf20Sopenharmony_ci * without underflows 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */ 578c2ecf20Sopenharmony_ci#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */ 588c2ecf20Sopenharmony_ci#define FFPLD_PLD_INCR 1000 /* increments in bytes */ 598c2ecf20Sopenharmony_ci#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we 608c2ecf20Sopenharmony_ci * accumulate between resets. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define AMPDU_DELIMITER_LEN 4 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* max allowed number of mpdus in an ampdu (2 streams) */ 668c2ecf20Sopenharmony_ci#define AMPDU_NUM_MPDU 16 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */ 718c2ecf20Sopenharmony_ci#define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\ 728c2ecf20Sopenharmony_ci AMPDU_DELIMITER_LEN + 3\ 738c2ecf20Sopenharmony_ci + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* modulo add/sub, bound = 2^k */ 768c2ecf20Sopenharmony_ci#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) 778c2ecf20Sopenharmony_ci#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* structure to hold tx fifo information and pre-loading state 808c2ecf20Sopenharmony_ci * counters specific to tx underflows of ampdus 818c2ecf20Sopenharmony_ci * some counters might be redundant with the ones in wlc or ampdu structures. 828c2ecf20Sopenharmony_ci * This allows to maintain a specific state independently of 838c2ecf20Sopenharmony_ci * how often and/or when the wlc counters are updated. 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * ampdu_pld_size: number of bytes to be pre-loaded 868c2ecf20Sopenharmony_ci * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu 878c2ecf20Sopenharmony_ci * prev_txfunfl: num of underflows last read from the HW macstats counter 888c2ecf20Sopenharmony_ci * accum_txfunfl: num of underflows since we modified pld params 898c2ecf20Sopenharmony_ci * accum_txampdu: num of tx ampdu since we modified pld params 908c2ecf20Sopenharmony_ci * prev_txampdu: previous reading of tx ampdu 918c2ecf20Sopenharmony_ci * dmaxferrate: estimated dma avg xfer rate in kbits/sec 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistruct brcms_fifo_info { 948c2ecf20Sopenharmony_ci u16 ampdu_pld_size; 958c2ecf20Sopenharmony_ci u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; 968c2ecf20Sopenharmony_ci u16 prev_txfunfl; 978c2ecf20Sopenharmony_ci u32 accum_txfunfl; 988c2ecf20Sopenharmony_ci u32 accum_txampdu; 998c2ecf20Sopenharmony_ci u32 prev_txampdu; 1008c2ecf20Sopenharmony_ci u32 dmaxferrate; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* AMPDU module specific state 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * wlc: pointer to main wlc structure 1068c2ecf20Sopenharmony_ci * scb_handle: scb cubby handle to retrieve data from scb 1078c2ecf20Sopenharmony_ci * ini_enable: per-tid initiator enable/disable of ampdu 1088c2ecf20Sopenharmony_ci * ba_tx_wsize: Tx ba window size (in pdu) 1098c2ecf20Sopenharmony_ci * ba_rx_wsize: Rx ba window size (in pdu) 1108c2ecf20Sopenharmony_ci * retry_limit: mpdu transmit retry limit 1118c2ecf20Sopenharmony_ci * rr_retry_limit: mpdu transmit retry limit at regular rate 1128c2ecf20Sopenharmony_ci * retry_limit_tid: per-tid mpdu transmit retry limit 1138c2ecf20Sopenharmony_ci * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate 1148c2ecf20Sopenharmony_ci * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec 1158c2ecf20Sopenharmony_ci * max_pdu: max pdus allowed in ampdu 1168c2ecf20Sopenharmony_ci * dur: max duration of an ampdu (in msec) 1178c2ecf20Sopenharmony_ci * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes 1188c2ecf20Sopenharmony_ci * ffpld_rsvd: number of bytes to reserve for preload 1198c2ecf20Sopenharmony_ci * max_txlen: max size of ampdu per mcs, bw and sgi 1208c2ecf20Sopenharmony_ci * mfbr: enable multiple fallback rate 1218c2ecf20Sopenharmony_ci * tx_max_funl: underflows should be kept such that 1228c2ecf20Sopenharmony_ci * (tx_max_funfl*underflows) < tx frames 1238c2ecf20Sopenharmony_ci * fifo_tb: table of fifo infos 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistruct ampdu_info { 1268c2ecf20Sopenharmony_ci struct brcms_c_info *wlc; 1278c2ecf20Sopenharmony_ci int scb_handle; 1288c2ecf20Sopenharmony_ci u8 ini_enable[AMPDU_MAX_SCB_TID]; 1298c2ecf20Sopenharmony_ci u8 ba_tx_wsize; 1308c2ecf20Sopenharmony_ci u8 ba_rx_wsize; 1318c2ecf20Sopenharmony_ci u8 retry_limit; 1328c2ecf20Sopenharmony_ci u8 rr_retry_limit; 1338c2ecf20Sopenharmony_ci u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; 1348c2ecf20Sopenharmony_ci u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID]; 1358c2ecf20Sopenharmony_ci u8 mpdu_density; 1368c2ecf20Sopenharmony_ci s8 max_pdu; 1378c2ecf20Sopenharmony_ci u8 dur; 1388c2ecf20Sopenharmony_ci u8 rx_factor; 1398c2ecf20Sopenharmony_ci u32 ffpld_rsvd; 1408c2ecf20Sopenharmony_ci u32 max_txlen[MCS_TABLE_SIZE][2][2]; 1418c2ecf20Sopenharmony_ci bool mfbr; 1428c2ecf20Sopenharmony_ci u32 tx_max_funl; 1438c2ecf20Sopenharmony_ci struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO]; 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* used for flushing ampdu packets */ 1478c2ecf20Sopenharmony_cistruct cb_del_ampdu_pars { 1488c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 1498c2ecf20Sopenharmony_ci u16 tid; 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci u32 rate, mcs; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) { 1578c2ecf20Sopenharmony_ci /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */ 1588c2ecf20Sopenharmony_ci /* 20MHz, No SGI */ 1598c2ecf20Sopenharmony_ci rate = mcs_2_rate(mcs, false, false); 1608c2ecf20Sopenharmony_ci ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3; 1618c2ecf20Sopenharmony_ci /* 40 MHz, No SGI */ 1628c2ecf20Sopenharmony_ci rate = mcs_2_rate(mcs, true, false); 1638c2ecf20Sopenharmony_ci ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3; 1648c2ecf20Sopenharmony_ci /* 20MHz, SGI */ 1658c2ecf20Sopenharmony_ci rate = mcs_2_rate(mcs, false, true); 1668c2ecf20Sopenharmony_ci ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3; 1678c2ecf20Sopenharmony_ci /* 40 MHz, SGI */ 1688c2ecf20Sopenharmony_ci rate = mcs_2_rate(mcs, true, true); 1698c2ecf20Sopenharmony_ci ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic bool brcms_c_ampdu_cap(struct ampdu_info *ampdu) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci if (BRCMS_PHY_11N_CAP(ampdu->wlc->band)) 1768c2ecf20Sopenharmony_ci return true; 1778c2ecf20Sopenharmony_ci else 1788c2ecf20Sopenharmony_ci return false; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct brcms_c_info *wlc = ampdu->wlc; 1848c2ecf20Sopenharmony_ci struct bcma_device *core = wlc->hw->d11core; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci wlc->pub->_ampdu = false; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (on) { 1898c2ecf20Sopenharmony_ci if (!(wlc->pub->_n_enab & SUPPORT_11N)) { 1908c2ecf20Sopenharmony_ci brcms_err(core, "wl%d: driver not nmode enabled\n", 1918c2ecf20Sopenharmony_ci wlc->pub->unit); 1928c2ecf20Sopenharmony_ci return -ENOTSUPP; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (!brcms_c_ampdu_cap(ampdu)) { 1958c2ecf20Sopenharmony_ci brcms_err(core, "wl%d: device not ampdu capable\n", 1968c2ecf20Sopenharmony_ci wlc->pub->unit); 1978c2ecf20Sopenharmony_ci return -ENOTSUPP; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci wlc->pub->_ampdu = on; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void brcms_c_ffpld_init(struct ampdu_info *ampdu) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int i, j; 2088c2ecf20Sopenharmony_ci struct brcms_fifo_info *fifo; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (j = 0; j < NUM_FFPLD_FIFO; j++) { 2118c2ecf20Sopenharmony_ci fifo = (ampdu->fifo_tb + j); 2128c2ecf20Sopenharmony_ci fifo->ampdu_pld_size = 0; 2138c2ecf20Sopenharmony_ci for (i = 0; i <= FFPLD_MAX_MCS; i++) 2148c2ecf20Sopenharmony_ci fifo->mcs2ampdu_table[i] = 255; 2158c2ecf20Sopenharmony_ci fifo->dmaxferrate = 0; 2168c2ecf20Sopenharmony_ci fifo->accum_txampdu = 0; 2178c2ecf20Sopenharmony_ci fifo->prev_txfunfl = 0; 2188c2ecf20Sopenharmony_ci fifo->accum_txfunfl = 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistruct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct ampdu_info *ampdu; 2268c2ecf20Sopenharmony_ci int i; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC); 2298c2ecf20Sopenharmony_ci if (!ampdu) 2308c2ecf20Sopenharmony_ci return NULL; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ampdu->wlc = wlc; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < AMPDU_MAX_SCB_TID; i++) 2358c2ecf20Sopenharmony_ci ampdu->ini_enable[i] = true; 2368c2ecf20Sopenharmony_ci /* Disable ampdu for VO by default */ 2378c2ecf20Sopenharmony_ci ampdu->ini_enable[PRIO_8021D_VO] = false; 2388c2ecf20Sopenharmony_ci ampdu->ini_enable[PRIO_8021D_NC] = false; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Disable ampdu for BK by default since not enough fifo space */ 2418c2ecf20Sopenharmony_ci ampdu->ini_enable[PRIO_8021D_NONE] = false; 2428c2ecf20Sopenharmony_ci ampdu->ini_enable[PRIO_8021D_BK] = false; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE; 2458c2ecf20Sopenharmony_ci ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE; 2468c2ecf20Sopenharmony_ci ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY; 2478c2ecf20Sopenharmony_ci ampdu->max_pdu = AUTO; 2488c2ecf20Sopenharmony_ci ampdu->dur = AMPDU_MAX_DUR; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD; 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * bump max ampdu rcv size to 64k for all 11n 2538c2ecf20Sopenharmony_ci * devices except 4321A0 and 4321A1 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2)) 2568c2ecf20Sopenharmony_ci ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K; 2578c2ecf20Sopenharmony_ci else 2588c2ecf20Sopenharmony_ci ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K; 2598c2ecf20Sopenharmony_ci ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT; 2608c2ecf20Sopenharmony_ci ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci for (i = 0; i < AMPDU_MAX_SCB_TID; i++) { 2638c2ecf20Sopenharmony_ci ampdu->retry_limit_tid[i] = ampdu->retry_limit; 2648c2ecf20Sopenharmony_ci ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur); 2688c2ecf20Sopenharmony_ci ampdu->mfbr = false; 2698c2ecf20Sopenharmony_ci /* try to set ampdu to the default value */ 2708c2ecf20Sopenharmony_ci brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL; 2738c2ecf20Sopenharmony_ci brcms_c_ffpld_init(ampdu); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return ampdu; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_civoid brcms_c_ampdu_detach(struct ampdu_info *ampdu) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci kfree(ampdu); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu, 2848c2ecf20Sopenharmony_ci struct scb *scb) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; 2878c2ecf20Sopenharmony_ci int i; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci scb_ampdu->max_pdu = AMPDU_NUM_MPDU; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* go back to legacy size if some preloading is occurring */ 2928c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FFPLD_FIFO; i++) { 2938c2ecf20Sopenharmony_ci if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR) 2948c2ecf20Sopenharmony_ci scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* apply user override */ 2988c2ecf20Sopenharmony_ci if (ampdu->max_pdu != AUTO) 2998c2ecf20Sopenharmony_ci scb_ampdu->max_pdu = (u8) ampdu->max_pdu; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, 3028c2ecf20Sopenharmony_ci AMPDU_SCB_MAX_RELEASE); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (scb_ampdu->max_rx_ampdu_bytes) 3058c2ecf20Sopenharmony_ci scb_ampdu->release = min_t(u8, scb_ampdu->release, 3068c2ecf20Sopenharmony_ci scb_ampdu->max_rx_ampdu_bytes / 1600); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci scb_ampdu->release = min(scb_ampdu->release, 3098c2ecf20Sopenharmony_ci ampdu->fifo_tb[TX_AC_BE_FIFO]. 3108c2ecf20Sopenharmony_ci mcs2ampdu_table[FFPLD_MAX_MCS]); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci brcms_c_scb_ampdu_update_config(ampdu, &du->wlc->pri_scb); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci u32 phy_rate, dma_rate, tmp; 3228c2ecf20Sopenharmony_ci u8 max_mpdu; 3238c2ecf20Sopenharmony_ci struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* recompute the dma rate */ 3268c2ecf20Sopenharmony_ci /* note : we divide/multiply by 100 to avoid integer overflows */ 3278c2ecf20Sopenharmony_ci max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], 3288c2ecf20Sopenharmony_ci AMPDU_NUM_MPDU_LEGACY); 3298c2ecf20Sopenharmony_ci phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); 3308c2ecf20Sopenharmony_ci dma_rate = 3318c2ecf20Sopenharmony_ci (((phy_rate / 100) * 3328c2ecf20Sopenharmony_ci (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) 3338c2ecf20Sopenharmony_ci / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; 3348c2ecf20Sopenharmony_ci fifo->dmaxferrate = dma_rate; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* fill up the mcs2ampdu table; do not recalc the last mcs */ 3378c2ecf20Sopenharmony_ci dma_rate = dma_rate >> 7; 3388c2ecf20Sopenharmony_ci for (i = 0; i < FFPLD_MAX_MCS; i++) { 3398c2ecf20Sopenharmony_ci /* shifting to keep it within integer range */ 3408c2ecf20Sopenharmony_ci phy_rate = mcs_2_rate(i, true, false) >> 7; 3418c2ecf20Sopenharmony_ci if (phy_rate > dma_rate) { 3428c2ecf20Sopenharmony_ci tmp = ((fifo->ampdu_pld_size * phy_rate) / 3438c2ecf20Sopenharmony_ci ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1; 3448c2ecf20Sopenharmony_ci tmp = min_t(u32, tmp, 255); 3458c2ecf20Sopenharmony_ci fifo->mcs2ampdu_table[i] = (u8) tmp; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* evaluate the dma transfer rate using the tx underflows as feedback. 3518c2ecf20Sopenharmony_ci * If necessary, increase tx fifo preloading. If not enough, 3528c2ecf20Sopenharmony_ci * decrease maximum ampdu size for each mcs till underflows stop 3538c2ecf20Sopenharmony_ci * Return 1 if pre-loading not active, -1 if not an underflow event, 3548c2ecf20Sopenharmony_ci * 0 if pre-loading module took care of the event. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_cistatic int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ampdu_info *ampdu = wlc->ampdu; 3598c2ecf20Sopenharmony_ci u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); 3608c2ecf20Sopenharmony_ci u32 txunfl_ratio; 3618c2ecf20Sopenharmony_ci u8 max_mpdu; 3628c2ecf20Sopenharmony_ci u32 current_ampdu_cnt = 0; 3638c2ecf20Sopenharmony_ci u16 max_pld_size; 3648c2ecf20Sopenharmony_ci u32 new_txunfl; 3658c2ecf20Sopenharmony_ci struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid); 3668c2ecf20Sopenharmony_ci uint xmtfifo_sz; 3678c2ecf20Sopenharmony_ci u16 cur_txunfl; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* return if we got here for a different reason than underflows */ 3708c2ecf20Sopenharmony_ci cur_txunfl = brcms_b_read_shm(wlc->hw, 3718c2ecf20Sopenharmony_ci M_UCODE_MACSTAT + 3728c2ecf20Sopenharmony_ci offsetof(struct macstat, txfunfl[fid])); 3738c2ecf20Sopenharmony_ci new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl); 3748c2ecf20Sopenharmony_ci if (new_txunfl == 0) { 3758c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, 3768c2ecf20Sopenharmony_ci "TX status FRAG set but no tx underflows\n"); 3778c2ecf20Sopenharmony_ci return -1; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci fifo->prev_txfunfl = cur_txunfl; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!ampdu->tx_max_funl) 3828c2ecf20Sopenharmony_ci return 1; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* check if fifo is big enough */ 3858c2ecf20Sopenharmony_ci if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz)) 3868c2ecf20Sopenharmony_ci return -1; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd) 3898c2ecf20Sopenharmony_ci return 1; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd; 3928c2ecf20Sopenharmony_ci fifo->accum_txfunfl += new_txunfl; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* we need to wait for at least 10 underflows */ 3958c2ecf20Sopenharmony_ci if (fifo->accum_txfunfl < 10) 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, "ampdu_count %d tx_underflows %d\n", 3998c2ecf20Sopenharmony_ci current_ampdu_cnt, fifo->accum_txfunfl); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci compute the current ratio of tx unfl per ampdu. 4038c2ecf20Sopenharmony_ci When the current ampdu count becomes too 4048c2ecf20Sopenharmony_ci big while the ratio remains small, we reset 4058c2ecf20Sopenharmony_ci the current count in order to not 4068c2ecf20Sopenharmony_ci introduce too big of a latency in detecting a 4078c2ecf20Sopenharmony_ci large amount of tx underflows later. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (txunfl_ratio > ampdu->tx_max_funl) { 4138c2ecf20Sopenharmony_ci if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) 4148c2ecf20Sopenharmony_ci fifo->accum_txfunfl = 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], 4198c2ecf20Sopenharmony_ci AMPDU_NUM_MPDU_LEGACY); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* In case max value max_pdu is already lower than 4228c2ecf20Sopenharmony_ci the fifo depth, there is nothing more we can do. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) { 4268c2ecf20Sopenharmony_ci fifo->accum_txfunfl = 0; 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (fifo->ampdu_pld_size < max_pld_size) { 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* increment by TX_FIFO_PLD_INC bytes */ 4338c2ecf20Sopenharmony_ci fifo->ampdu_pld_size += FFPLD_PLD_INCR; 4348c2ecf20Sopenharmony_ci if (fifo->ampdu_pld_size > max_pld_size) 4358c2ecf20Sopenharmony_ci fifo->ampdu_pld_size = max_pld_size; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* update scb release size */ 4388c2ecf20Sopenharmony_ci brcms_c_scb_ampdu_update_config_all(ampdu); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * compute a new dma xfer rate for max_mpdu @ max mcs. 4428c2ecf20Sopenharmony_ci * This is the minimum dma rate that can achieve no 4438c2ecf20Sopenharmony_ci * underflow condition for the current mpdu size. 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * note : we divide/multiply by 100 to avoid integer overflows 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci fifo->dmaxferrate = 4488c2ecf20Sopenharmony_ci (((phy_rate / 100) * 4498c2ecf20Sopenharmony_ci (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) 4508c2ecf20Sopenharmony_ci / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, 4538c2ecf20Sopenharmony_ci "DMA estimated transfer rate %d; " 4548c2ecf20Sopenharmony_ci "pre-load size %d\n", 4558c2ecf20Sopenharmony_ci fifo->dmaxferrate, fifo->ampdu_pld_size); 4568c2ecf20Sopenharmony_ci } else { 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* decrease ampdu size */ 4598c2ecf20Sopenharmony_ci if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) { 4608c2ecf20Sopenharmony_ci if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255) 4618c2ecf20Sopenharmony_ci fifo->mcs2ampdu_table[FFPLD_MAX_MCS] = 4628c2ecf20Sopenharmony_ci AMPDU_NUM_MPDU_LEGACY - 1; 4638c2ecf20Sopenharmony_ci else 4648c2ecf20Sopenharmony_ci fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* recompute the table */ 4678c2ecf20Sopenharmony_ci brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* update scb release size */ 4708c2ecf20Sopenharmony_ci brcms_c_scb_ampdu_update_config_all(ampdu); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci fifo->accum_txfunfl = 0; 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_civoid 4788c2ecf20Sopenharmony_cibrcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, 4798c2ecf20Sopenharmony_ci u8 ba_wsize, /* negotiated ba window size (in pdu) */ 4808c2ecf20Sopenharmony_ci uint max_rx_ampdu_bytes) /* from ht_cap in beacon */ 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct scb_ampdu *scb_ampdu; 4838c2ecf20Sopenharmony_ci struct scb_ampdu_tid_ini *ini; 4848c2ecf20Sopenharmony_ci struct ampdu_info *ampdu = wlc->ampdu; 4858c2ecf20Sopenharmony_ci struct scb *scb = &wlc->pri_scb; 4868c2ecf20Sopenharmony_ci scb_ampdu = &scb->scb_ampdu; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (!ampdu->ini_enable[tid]) { 4898c2ecf20Sopenharmony_ci brcms_err(wlc->hw->d11core, "%s: Rejecting tid %d\n", 4908c2ecf20Sopenharmony_ci __func__, tid); 4918c2ecf20Sopenharmony_ci return; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ini = &scb_ampdu->ini[tid]; 4958c2ecf20Sopenharmony_ci ini->tid = tid; 4968c2ecf20Sopenharmony_ci ini->scb = scb_ampdu->scb; 4978c2ecf20Sopenharmony_ci ini->ba_wsize = ba_wsize; 4988c2ecf20Sopenharmony_ci scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_civoid brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, 5028c2ecf20Sopenharmony_ci struct brcms_c_info *wlc) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci session->wlc = wlc; 5058c2ecf20Sopenharmony_ci skb_queue_head_init(&session->skb_list); 5068c2ecf20Sopenharmony_ci session->max_ampdu_len = 0; /* determined from first MPDU */ 5078c2ecf20Sopenharmony_ci session->max_ampdu_frames = 0; /* determined from first MPDU */ 5088c2ecf20Sopenharmony_ci session->ampdu_len = 0; 5098c2ecf20Sopenharmony_ci session->dma_len = 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/* 5138c2ecf20Sopenharmony_ci * Preps the given packet for AMPDU based on the session data. If the 5148c2ecf20Sopenharmony_ci * frame cannot be accomodated in the current session, -ENOSPC is 5158c2ecf20Sopenharmony_ci * returned. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ciint brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, 5188c2ecf20Sopenharmony_ci struct sk_buff *p) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct brcms_c_info *wlc = session->wlc; 5218c2ecf20Sopenharmony_ci struct ampdu_info *ampdu = wlc->ampdu; 5228c2ecf20Sopenharmony_ci struct scb *scb = &wlc->pri_scb; 5238c2ecf20Sopenharmony_ci struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; 5248c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); 5258c2ecf20Sopenharmony_ci struct ieee80211_tx_rate *txrate = tx_info->status.rates; 5268c2ecf20Sopenharmony_ci struct d11txh *txh = (struct d11txh *)p->data; 5278c2ecf20Sopenharmony_ci unsigned ampdu_frames; 5288c2ecf20Sopenharmony_ci u8 ndelim, tid; 5298c2ecf20Sopenharmony_ci u8 *plcp; 5308c2ecf20Sopenharmony_ci uint len; 5318c2ecf20Sopenharmony_ci u16 mcl; 5328c2ecf20Sopenharmony_ci bool fbr_iscck; 5338c2ecf20Sopenharmony_ci bool rr; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; 5368c2ecf20Sopenharmony_ci plcp = (u8 *)(txh + 1); 5378c2ecf20Sopenharmony_ci fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); 5388c2ecf20Sopenharmony_ci len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : 5398c2ecf20Sopenharmony_ci BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); 5408c2ecf20Sopenharmony_ci len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ampdu_frames = skb_queue_len(&session->skb_list); 5438c2ecf20Sopenharmony_ci if (ampdu_frames != 0) { 5448c2ecf20Sopenharmony_ci struct sk_buff *first; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (ampdu_frames + 1 > session->max_ampdu_frames || 5478c2ecf20Sopenharmony_ci session->ampdu_len + len > session->max_ampdu_len) 5488c2ecf20Sopenharmony_ci return -ENOSPC; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* 5518c2ecf20Sopenharmony_ci * We aren't really out of space if the new frame is of 5528c2ecf20Sopenharmony_ci * a different priority, but we want the same behaviour 5538c2ecf20Sopenharmony_ci * so return -ENOSPC anyway. 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * XXX: The old AMPDU code did this, but is it really 5568c2ecf20Sopenharmony_ci * necessary? 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ci first = skb_peek(&session->skb_list); 5598c2ecf20Sopenharmony_ci if (p->priority != first->priority) 5608c2ecf20Sopenharmony_ci return -ENOSPC; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* 5648c2ecf20Sopenharmony_ci * Now that we're sure this frame can be accomodated, update the 5658c2ecf20Sopenharmony_ci * session information. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci session->ampdu_len += len; 5688c2ecf20Sopenharmony_ci session->dma_len += p->len; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci tid = (u8)p->priority; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Handle retry limits */ 5738c2ecf20Sopenharmony_ci if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) { 5748c2ecf20Sopenharmony_ci txrate[0].count++; 5758c2ecf20Sopenharmony_ci rr = true; 5768c2ecf20Sopenharmony_ci } else { 5778c2ecf20Sopenharmony_ci txrate[1].count++; 5788c2ecf20Sopenharmony_ci rr = false; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (ampdu_frames == 0) { 5828c2ecf20Sopenharmony_ci u8 plcp0, plcp3, is40, sgi, mcs; 5838c2ecf20Sopenharmony_ci uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; 5848c2ecf20Sopenharmony_ci struct brcms_fifo_info *f = &du->fifo_tb[fifo]; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (rr) { 5878c2ecf20Sopenharmony_ci plcp0 = plcp[0]; 5888c2ecf20Sopenharmony_ci plcp3 = plcp[3]; 5898c2ecf20Sopenharmony_ci } else { 5908c2ecf20Sopenharmony_ci plcp0 = txh->FragPLCPFallback[0]; 5918c2ecf20Sopenharmony_ci plcp3 = txh->FragPLCPFallback[3]; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Limit AMPDU size based on MCS */ 5968c2ecf20Sopenharmony_ci is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; 5978c2ecf20Sopenharmony_ci sgi = plcp3_issgi(plcp3) ? 1 : 0; 5988c2ecf20Sopenharmony_ci mcs = plcp0 & ~MIMO_PLCP_40MHZ; 5998c2ecf20Sopenharmony_ci session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes, 6008c2ecf20Sopenharmony_ci ampdu->max_txlen[mcs][is40][sgi]); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci session->max_ampdu_frames = scb_ampdu->max_pdu; 6038c2ecf20Sopenharmony_ci if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { 6048c2ecf20Sopenharmony_ci session->max_ampdu_frames = 6058c2ecf20Sopenharmony_ci min_t(u16, f->mcs2ampdu_table[mcs], 6068c2ecf20Sopenharmony_ci session->max_ampdu_frames); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * Treat all frames as "middle" frames of AMPDU here. First and 6128c2ecf20Sopenharmony_ci * last frames must be fixed up after all MPDUs have been prepped. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_ci mcl = le16_to_cpu(txh->MacTxControlLow); 6158c2ecf20Sopenharmony_ci mcl &= ~TXC_AMPDU_MASK; 6168c2ecf20Sopenharmony_ci mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); 6178c2ecf20Sopenharmony_ci mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); 6188c2ecf20Sopenharmony_ci txh->MacTxControlLow = cpu_to_le16(mcl); 6198c2ecf20Sopenharmony_ci txh->PreloadSize = 0; /* always default to 0 */ 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci skb_queue_tail(&session->skb_list, p); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_civoid brcms_c_ampdu_finalize(struct brcms_ampdu_session *session) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct brcms_c_info *wlc = session->wlc; 6298c2ecf20Sopenharmony_ci struct ampdu_info *ampdu = wlc->ampdu; 6308c2ecf20Sopenharmony_ci struct sk_buff *first, *last; 6318c2ecf20Sopenharmony_ci struct d11txh *txh; 6328c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info; 6338c2ecf20Sopenharmony_ci struct ieee80211_tx_rate *txrate; 6348c2ecf20Sopenharmony_ci u8 ndelim; 6358c2ecf20Sopenharmony_ci u8 *plcp; 6368c2ecf20Sopenharmony_ci uint len; 6378c2ecf20Sopenharmony_ci uint fifo; 6388c2ecf20Sopenharmony_ci struct brcms_fifo_info *f; 6398c2ecf20Sopenharmony_ci u16 mcl; 6408c2ecf20Sopenharmony_ci bool fbr; 6418c2ecf20Sopenharmony_ci bool fbr_iscck; 6428c2ecf20Sopenharmony_ci struct ieee80211_rts *rts; 6438c2ecf20Sopenharmony_ci bool use_rts = false, use_cts = false; 6448c2ecf20Sopenharmony_ci u16 dma_len = session->dma_len; 6458c2ecf20Sopenharmony_ci u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; 6468c2ecf20Sopenharmony_ci u32 rspec = 0, rspec_fallback = 0; 6478c2ecf20Sopenharmony_ci u32 rts_rspec = 0, rts_rspec_fallback = 0; 6488c2ecf20Sopenharmony_ci u8 plcp0, is40, mcs; 6498c2ecf20Sopenharmony_ci u16 mch; 6508c2ecf20Sopenharmony_ci u8 preamble_type = BRCMS_GF_PREAMBLE; 6518c2ecf20Sopenharmony_ci u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; 6528c2ecf20Sopenharmony_ci u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; 6538c2ecf20Sopenharmony_ci u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (skb_queue_empty(&session->skb_list)) 6568c2ecf20Sopenharmony_ci return; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci first = skb_peek(&session->skb_list); 6598c2ecf20Sopenharmony_ci last = skb_peek_tail(&session->skb_list); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Need to fix up last MPDU first to adjust AMPDU length */ 6628c2ecf20Sopenharmony_ci txh = (struct d11txh *)last->data; 6638c2ecf20Sopenharmony_ci fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; 6648c2ecf20Sopenharmony_ci f = &du->fifo_tb[fifo]; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci mcl = le16_to_cpu(txh->MacTxControlLow); 6678c2ecf20Sopenharmony_ci mcl &= ~TXC_AMPDU_MASK; 6688c2ecf20Sopenharmony_ci mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); 6698c2ecf20Sopenharmony_ci txh->MacTxControlLow = cpu_to_le16(mcl); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* remove the null delimiter after last mpdu */ 6728c2ecf20Sopenharmony_ci ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; 6738c2ecf20Sopenharmony_ci txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; 6748c2ecf20Sopenharmony_ci session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* remove the pad len from last mpdu */ 6778c2ecf20Sopenharmony_ci fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); 6788c2ecf20Sopenharmony_ci len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : 6798c2ecf20Sopenharmony_ci BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); 6808c2ecf20Sopenharmony_ci session->ampdu_len -= roundup(len, 4) - len; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Now fix up the first MPDU */ 6838c2ecf20Sopenharmony_ci tx_info = IEEE80211_SKB_CB(first); 6848c2ecf20Sopenharmony_ci txrate = tx_info->status.rates; 6858c2ecf20Sopenharmony_ci txh = (struct d11txh *)first->data; 6868c2ecf20Sopenharmony_ci plcp = (u8 *)(txh + 1); 6878c2ecf20Sopenharmony_ci rts = (struct ieee80211_rts *)&txh->rts_frame; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci mcl = le16_to_cpu(txh->MacTxControlLow); 6908c2ecf20Sopenharmony_ci /* If only one MPDU leave it marked as last */ 6918c2ecf20Sopenharmony_ci if (first != last) { 6928c2ecf20Sopenharmony_ci mcl &= ~TXC_AMPDU_MASK; 6938c2ecf20Sopenharmony_ci mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci mcl |= TXC_STARTMSDU; 6968c2ecf20Sopenharmony_ci if (ieee80211_is_rts(rts->frame_control)) { 6978c2ecf20Sopenharmony_ci mcl |= TXC_SENDRTS; 6988c2ecf20Sopenharmony_ci use_rts = true; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci if (ieee80211_is_cts(rts->frame_control)) { 7018c2ecf20Sopenharmony_ci mcl |= TXC_SENDCTS; 7028c2ecf20Sopenharmony_ci use_cts = true; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci txh->MacTxControlLow = cpu_to_le16(mcl); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci fbr = txrate[1].count > 0; 7078c2ecf20Sopenharmony_ci if (!fbr) 7088c2ecf20Sopenharmony_ci plcp0 = plcp[0]; 7098c2ecf20Sopenharmony_ci else 7108c2ecf20Sopenharmony_ci plcp0 = txh->FragPLCPFallback[0]; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; 7138c2ecf20Sopenharmony_ci mcs = plcp0 & ~MIMO_PLCP_40MHZ; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (is40) { 7168c2ecf20Sopenharmony_ci if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi))) 7178c2ecf20Sopenharmony_ci mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP; 7188c2ecf20Sopenharmony_ci else 7198c2ecf20Sopenharmony_ci mimo_ctlchbw = PHY_TXC1_BW_20MHZ; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* rebuild the rspec and rspec_fallback */ 7238c2ecf20Sopenharmony_ci rspec = RSPEC_MIMORATE; 7248c2ecf20Sopenharmony_ci rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; 7258c2ecf20Sopenharmony_ci if (plcp[0] & MIMO_PLCP_40MHZ) 7268c2ecf20Sopenharmony_ci rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); 7298c2ecf20Sopenharmony_ci if (fbr_iscck) { 7308c2ecf20Sopenharmony_ci rspec_fallback = 7318c2ecf20Sopenharmony_ci cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0])); 7328c2ecf20Sopenharmony_ci } else { 7338c2ecf20Sopenharmony_ci rspec_fallback = RSPEC_MIMORATE; 7348c2ecf20Sopenharmony_ci rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; 7358c2ecf20Sopenharmony_ci if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) 7368c2ecf20Sopenharmony_ci rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (use_rts || use_cts) { 7408c2ecf20Sopenharmony_ci rts_rspec = 7418c2ecf20Sopenharmony_ci brcms_c_rspec_to_rts_rspec(wlc, rspec, 7428c2ecf20Sopenharmony_ci false, mimo_ctlchbw); 7438c2ecf20Sopenharmony_ci rts_rspec_fallback = 7448c2ecf20Sopenharmony_ci brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback, 7458c2ecf20Sopenharmony_ci false, mimo_ctlchbw); 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len); 7498c2ecf20Sopenharmony_ci /* mark plcp to indicate ampdu */ 7508c2ecf20Sopenharmony_ci BRCMS_SET_MIMO_PLCP_AMPDU(plcp); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* reset the mixed mode header durations */ 7538c2ecf20Sopenharmony_ci if (txh->MModeLen) { 7548c2ecf20Sopenharmony_ci u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec, 7558c2ecf20Sopenharmony_ci session->ampdu_len); 7568c2ecf20Sopenharmony_ci txh->MModeLen = cpu_to_le16(mmodelen); 7578c2ecf20Sopenharmony_ci preamble_type = BRCMS_MM_PREAMBLE; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci if (txh->MModeFbrLen) { 7608c2ecf20Sopenharmony_ci u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback, 7618c2ecf20Sopenharmony_ci session->ampdu_len); 7628c2ecf20Sopenharmony_ci txh->MModeFbrLen = cpu_to_le16(mmfbrlen); 7638c2ecf20Sopenharmony_ci fbr_preamble_type = BRCMS_MM_PREAMBLE; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* set the preload length */ 7678c2ecf20Sopenharmony_ci if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { 7688c2ecf20Sopenharmony_ci dma_len = min(dma_len, f->ampdu_pld_size); 7698c2ecf20Sopenharmony_ci txh->PreloadSize = cpu_to_le16(dma_len); 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci txh->PreloadSize = 0; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci mch = le16_to_cpu(txh->MacTxControlHigh); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* update RTS dur fields */ 7778c2ecf20Sopenharmony_ci if (use_rts || use_cts) { 7788c2ecf20Sopenharmony_ci u16 durid; 7798c2ecf20Sopenharmony_ci if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == 7808c2ecf20Sopenharmony_ci TXC_PREAMBLE_RTS_MAIN_SHORT) 7818c2ecf20Sopenharmony_ci rts_preamble_type = BRCMS_SHORT_PREAMBLE; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == 7848c2ecf20Sopenharmony_ci TXC_PREAMBLE_RTS_FB_SHORT) 7858c2ecf20Sopenharmony_ci rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, 7888c2ecf20Sopenharmony_ci rspec, rts_preamble_type, 7898c2ecf20Sopenharmony_ci preamble_type, 7908c2ecf20Sopenharmony_ci session->ampdu_len, true); 7918c2ecf20Sopenharmony_ci rts->duration = cpu_to_le16(durid); 7928c2ecf20Sopenharmony_ci durid = brcms_c_compute_rtscts_dur(wlc, use_cts, 7938c2ecf20Sopenharmony_ci rts_rspec_fallback, 7948c2ecf20Sopenharmony_ci rspec_fallback, 7958c2ecf20Sopenharmony_ci rts_fbr_preamble_type, 7968c2ecf20Sopenharmony_ci fbr_preamble_type, 7978c2ecf20Sopenharmony_ci session->ampdu_len, true); 7988c2ecf20Sopenharmony_ci txh->RTSDurFallback = cpu_to_le16(durid); 7998c2ecf20Sopenharmony_ci /* set TxFesTimeNormal */ 8008c2ecf20Sopenharmony_ci txh->TxFesTimeNormal = rts->duration; 8018c2ecf20Sopenharmony_ci /* set fallback rate version of TxFesTimeNormal */ 8028c2ecf20Sopenharmony_ci txh->TxFesTimeFallback = txh->RTSDurFallback; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* set flag and plcp for fallback rate */ 8068c2ecf20Sopenharmony_ci if (fbr) { 8078c2ecf20Sopenharmony_ci mch |= TXC_AMPDU_FBR; 8088c2ecf20Sopenharmony_ci txh->MacTxControlHigh = cpu_to_le16(mch); 8098c2ecf20Sopenharmony_ci BRCMS_SET_MIMO_PLCP_AMPDU(plcp); 8108c2ecf20Sopenharmony_ci BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, "wl%d: count %d ampdu_len %d\n", 8148c2ecf20Sopenharmony_ci wlc->pub->unit, skb_queue_len(&session->skb_list), 8158c2ecf20Sopenharmony_ci session->ampdu_len); 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic void 8198c2ecf20Sopenharmony_cibrcms_c_ampdu_rate_status(struct brcms_c_info *wlc, 8208c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info, 8218c2ecf20Sopenharmony_ci struct tx_status *txs, u8 mcs) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct ieee80211_tx_rate *txrate = tx_info->status.rates; 8248c2ecf20Sopenharmony_ci int i; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* clear the rest of the rates */ 8278c2ecf20Sopenharmony_ci for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { 8288c2ecf20Sopenharmony_ci txrate[i].idx = -1; 8298c2ecf20Sopenharmony_ci txrate[i].count = 0; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void 8348c2ecf20Sopenharmony_cibrcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, 8358c2ecf20Sopenharmony_ci struct sk_buff *p, struct tx_status *txs, 8368c2ecf20Sopenharmony_ci u32 s1, u32 s2) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct scb_ampdu *scb_ampdu; 8398c2ecf20Sopenharmony_ci struct brcms_c_info *wlc = ampdu->wlc; 8408c2ecf20Sopenharmony_ci struct scb_ampdu_tid_ini *ini; 8418c2ecf20Sopenharmony_ci u8 bitmap[8], queue, tid; 8428c2ecf20Sopenharmony_ci struct d11txh *txh; 8438c2ecf20Sopenharmony_ci u8 *plcp; 8448c2ecf20Sopenharmony_ci struct ieee80211_hdr *h; 8458c2ecf20Sopenharmony_ci u16 seq, start_seq = 0, bindex, index, mcl; 8468c2ecf20Sopenharmony_ci u8 mcs = 0; 8478c2ecf20Sopenharmony_ci bool ba_recd = false, ack_recd = false; 8488c2ecf20Sopenharmony_ci u8 suc_mpdu = 0, tot_mpdu = 0; 8498c2ecf20Sopenharmony_ci uint supr_status; 8508c2ecf20Sopenharmony_ci bool retry = true; 8518c2ecf20Sopenharmony_ci u16 mimoantsel = 0; 8528c2ecf20Sopenharmony_ci u8 retry_limit; 8538c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci#ifdef DEBUG 8568c2ecf20Sopenharmony_ci u8 hole[AMPDU_MAX_MPDU]; 8578c2ecf20Sopenharmony_ci memset(hole, 0, sizeof(hole)); 8588c2ecf20Sopenharmony_ci#endif 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci scb_ampdu = &scb->scb_ampdu; 8618c2ecf20Sopenharmony_ci tid = (u8) (p->priority); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci ini = &scb_ampdu->ini[tid]; 8648c2ecf20Sopenharmony_ci retry_limit = ampdu->retry_limit_tid[tid]; 8658c2ecf20Sopenharmony_ci memset(bitmap, 0, sizeof(bitmap)); 8668c2ecf20Sopenharmony_ci queue = txs->frameid & TXFID_QUEUE_MASK; 8678c2ecf20Sopenharmony_ci supr_status = txs->status & TX_STATUS_SUPR_MASK; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (txs->status & TX_STATUS_ACK_RCV) { 8708c2ecf20Sopenharmony_ci WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE)); 8718c2ecf20Sopenharmony_ci start_seq = txs->sequence >> SEQNUM_SHIFT; 8728c2ecf20Sopenharmony_ci bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >> 8738c2ecf20Sopenharmony_ci TX_STATUS_BA_BMAP03_SHIFT; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci WARN_ON(s1 & TX_STATUS_INTERMEDIATE); 8768c2ecf20Sopenharmony_ci WARN_ON(!(s1 & TX_STATUS_AMPDU)); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci bitmap[0] |= 8798c2ecf20Sopenharmony_ci (s1 & TX_STATUS_BA_BMAP47_MASK) << 8808c2ecf20Sopenharmony_ci TX_STATUS_BA_BMAP47_SHIFT; 8818c2ecf20Sopenharmony_ci bitmap[1] = (s1 >> 8) & 0xff; 8828c2ecf20Sopenharmony_ci bitmap[2] = (s1 >> 16) & 0xff; 8838c2ecf20Sopenharmony_ci bitmap[3] = (s1 >> 24) & 0xff; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci bitmap[4] = s2 & 0xff; 8868c2ecf20Sopenharmony_ci bitmap[5] = (s2 >> 8) & 0xff; 8878c2ecf20Sopenharmony_ci bitmap[6] = (s2 >> 16) & 0xff; 8888c2ecf20Sopenharmony_ci bitmap[7] = (s2 >> 24) & 0xff; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci ba_recd = true; 8918c2ecf20Sopenharmony_ci } else { 8928c2ecf20Sopenharmony_ci if (supr_status) { 8938c2ecf20Sopenharmony_ci if (supr_status == TX_STATUS_SUPR_BADCH) { 8948c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, 8958c2ecf20Sopenharmony_ci "%s: Pkt tx suppressed, illegal channel possibly %d\n", 8968c2ecf20Sopenharmony_ci __func__, CHSPEC_CHANNEL( 8978c2ecf20Sopenharmony_ci wlc->default_bss->chanspec)); 8988c2ecf20Sopenharmony_ci } else { 8998c2ecf20Sopenharmony_ci if (supr_status != TX_STATUS_SUPR_FRAG) 9008c2ecf20Sopenharmony_ci brcms_err(wlc->hw->d11core, 9018c2ecf20Sopenharmony_ci "%s: supr_status 0x%x\n", 9028c2ecf20Sopenharmony_ci __func__, supr_status); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci /* no need to retry for badch; will fail again */ 9058c2ecf20Sopenharmony_ci if (supr_status == TX_STATUS_SUPR_BADCH || 9068c2ecf20Sopenharmony_ci supr_status == TX_STATUS_SUPR_EXPTIME) { 9078c2ecf20Sopenharmony_ci retry = false; 9088c2ecf20Sopenharmony_ci } else if (supr_status == TX_STATUS_SUPR_EXPTIME) { 9098c2ecf20Sopenharmony_ci /* TX underflow: 9108c2ecf20Sopenharmony_ci * try tuning pre-loading or ampdu size 9118c2ecf20Sopenharmony_ci */ 9128c2ecf20Sopenharmony_ci } else if (supr_status == TX_STATUS_SUPR_FRAG) { 9138c2ecf20Sopenharmony_ci /* 9148c2ecf20Sopenharmony_ci * if there were underflows, but pre-loading 9158c2ecf20Sopenharmony_ci * is not active, notify rate adaptation. 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_ci brcms_c_ffpld_check_txfunfl(wlc, queue); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci } else if (txs->phyerr) { 9208c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, 9218c2ecf20Sopenharmony_ci "%s: ampdu tx phy error (0x%x)\n", 9228c2ecf20Sopenharmony_ci __func__, txs->phyerr); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* loop through all pkts and retry if not acked */ 9278c2ecf20Sopenharmony_ci while (p) { 9288c2ecf20Sopenharmony_ci tx_info = IEEE80211_SKB_CB(p); 9298c2ecf20Sopenharmony_ci txh = (struct d11txh *) p->data; 9308c2ecf20Sopenharmony_ci mcl = le16_to_cpu(txh->MacTxControlLow); 9318c2ecf20Sopenharmony_ci plcp = (u8 *) (txh + 1); 9328c2ecf20Sopenharmony_ci h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); 9338c2ecf20Sopenharmony_ci seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (tot_mpdu == 0) { 9388c2ecf20Sopenharmony_ci mcs = plcp[0] & MIMO_PLCP_MCS_MASK; 9398c2ecf20Sopenharmony_ci mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel); 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci index = TX_SEQ_TO_INDEX(seq); 9438c2ecf20Sopenharmony_ci ack_recd = false; 9448c2ecf20Sopenharmony_ci if (ba_recd) { 9458c2ecf20Sopenharmony_ci bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX); 9468c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, 9478c2ecf20Sopenharmony_ci "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n", 9488c2ecf20Sopenharmony_ci tid, seq, start_seq, bindex, 9498c2ecf20Sopenharmony_ci isset(bitmap, bindex), index); 9508c2ecf20Sopenharmony_ci /* if acked then clear bit and free packet */ 9518c2ecf20Sopenharmony_ci if ((bindex < AMPDU_TX_BA_MAX_WSIZE) 9528c2ecf20Sopenharmony_ci && isset(bitmap, bindex)) { 9538c2ecf20Sopenharmony_ci ini->txretry[index] = 0; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * ampdu_ack_len: 9578c2ecf20Sopenharmony_ci * number of acked aggregated frames 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ci /* ampdu_len: number of aggregated frames */ 9608c2ecf20Sopenharmony_ci brcms_c_ampdu_rate_status(wlc, tx_info, txs, 9618c2ecf20Sopenharmony_ci mcs); 9628c2ecf20Sopenharmony_ci tx_info->flags |= IEEE80211_TX_STAT_ACK; 9638c2ecf20Sopenharmony_ci tx_info->flags |= IEEE80211_TX_STAT_AMPDU; 9648c2ecf20Sopenharmony_ci tx_info->status.ampdu_ack_len = 9658c2ecf20Sopenharmony_ci tx_info->status.ampdu_len = 1; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci skb_pull(p, D11_PHY_HDR_LEN); 9688c2ecf20Sopenharmony_ci skb_pull(p, D11_TXH_LEN); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, 9718c2ecf20Sopenharmony_ci p); 9728c2ecf20Sopenharmony_ci ack_recd = true; 9738c2ecf20Sopenharmony_ci suc_mpdu++; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci /* either retransmit or send bar if ack not recd */ 9778c2ecf20Sopenharmony_ci if (!ack_recd) { 9788c2ecf20Sopenharmony_ci if (retry && (ini->txretry[index] < (int)retry_limit)) { 9798c2ecf20Sopenharmony_ci int ret; 9808c2ecf20Sopenharmony_ci ini->txretry[index]++; 9818c2ecf20Sopenharmony_ci ret = brcms_c_txfifo(wlc, queue, p); 9828c2ecf20Sopenharmony_ci /* 9838c2ecf20Sopenharmony_ci * We shouldn't be out of space in the DMA 9848c2ecf20Sopenharmony_ci * ring here since we're reinserting a frame 9858c2ecf20Sopenharmony_ci * that was just pulled out. 9868c2ecf20Sopenharmony_ci */ 9878c2ecf20Sopenharmony_ci WARN_ONCE(ret, "queue %d out of txds\n", queue); 9888c2ecf20Sopenharmony_ci } else { 9898c2ecf20Sopenharmony_ci /* Retry timeout */ 9908c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(tx_info); 9918c2ecf20Sopenharmony_ci tx_info->status.ampdu_ack_len = 0; 9928c2ecf20Sopenharmony_ci tx_info->status.ampdu_len = 1; 9938c2ecf20Sopenharmony_ci tx_info->flags |= 9948c2ecf20Sopenharmony_ci IEEE80211_TX_STAT_AMPDU_NO_BACK; 9958c2ecf20Sopenharmony_ci skb_pull(p, D11_PHY_HDR_LEN); 9968c2ecf20Sopenharmony_ci skb_pull(p, D11_TXH_LEN); 9978c2ecf20Sopenharmony_ci brcms_dbg_ht(wlc->hw->d11core, 9988c2ecf20Sopenharmony_ci "BA Timeout, seq %d\n", 9998c2ecf20Sopenharmony_ci seq); 10008c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, 10018c2ecf20Sopenharmony_ci p); 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci tot_mpdu++; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* break out if last packet of ampdu */ 10078c2ecf20Sopenharmony_ci if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == 10088c2ecf20Sopenharmony_ci TXC_AMPDU_LAST) 10098c2ecf20Sopenharmony_ci break; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* update rate state */ 10158c2ecf20Sopenharmony_ci brcms_c_antsel_antsel2id(wlc->asi, mimoantsel); 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_civoid 10198c2ecf20Sopenharmony_cibrcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, 10208c2ecf20Sopenharmony_ci struct sk_buff *p, struct tx_status *txs) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct brcms_c_info *wlc = ampdu->wlc; 10238c2ecf20Sopenharmony_ci u32 s1 = 0, s2 = 0; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* BMAC_NOTE: For the split driver, second level txstatus comes later 10268c2ecf20Sopenharmony_ci * So if the ACK was received then wait for the second level else just 10278c2ecf20Sopenharmony_ci * call the first one 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_ci if (txs->status & TX_STATUS_ACK_RCV) { 10308c2ecf20Sopenharmony_ci u8 status_delay = 0; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* wait till the next 8 bytes of txstatus is available */ 10338c2ecf20Sopenharmony_ci s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus)); 10348c2ecf20Sopenharmony_ci while ((s1 & TXS_V) == 0) { 10358c2ecf20Sopenharmony_ci udelay(1); 10368c2ecf20Sopenharmony_ci status_delay++; 10378c2ecf20Sopenharmony_ci if (status_delay > 10) 10388c2ecf20Sopenharmony_ci return; /* error condition */ 10398c2ecf20Sopenharmony_ci s1 = bcma_read32(wlc->hw->d11core, 10408c2ecf20Sopenharmony_ci D11REGOFFS(frmtxstatus)); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci s2 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus2)); 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (scb) { 10478c2ecf20Sopenharmony_ci brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2); 10488c2ecf20Sopenharmony_ci } else { 10498c2ecf20Sopenharmony_ci /* loop through all pkts and free */ 10508c2ecf20Sopenharmony_ci u8 queue = txs->frameid & TXFID_QUEUE_MASK; 10518c2ecf20Sopenharmony_ci struct d11txh *txh; 10528c2ecf20Sopenharmony_ci u16 mcl; 10538c2ecf20Sopenharmony_ci while (p) { 10548c2ecf20Sopenharmony_ci txh = (struct d11txh *) p->data; 10558c2ecf20Sopenharmony_ci trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, 10568c2ecf20Sopenharmony_ci sizeof(*txh)); 10578c2ecf20Sopenharmony_ci mcl = le16_to_cpu(txh->MacTxControlLow); 10588c2ecf20Sopenharmony_ci brcmu_pkt_buf_free_skb(p); 10598c2ecf20Sopenharmony_ci /* break out if last packet of ampdu */ 10608c2ecf20Sopenharmony_ci if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == 10618c2ecf20Sopenharmony_ci TXC_AMPDU_LAST) 10628c2ecf20Sopenharmony_ci break; 10638c2ecf20Sopenharmony_ci p = dma_getnexttxp(wlc->hw->di[queue], 10648c2ecf20Sopenharmony_ci DMA_RANGE_TRANSMITTED); 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_civoid brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci char template[T_RAM_ACCESS_SZ * 2]; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* driver needs to write the ta in the template; ta is at offset 16 */ 10748c2ecf20Sopenharmony_ci memset(template, 0, sizeof(template)); 10758c2ecf20Sopenharmony_ci memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN); 10768c2ecf20Sopenharmony_ci brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16), 10778c2ecf20Sopenharmony_ci (T_RAM_ACCESS_SZ * 2), 10788c2ecf20Sopenharmony_ci template); 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cibool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci return wlc->ampdu->ini_enable[tid]; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_civoid brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci struct brcms_c_info *wlc = ampdu->wlc; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* 10918c2ecf20Sopenharmony_ci * Extend ucode internal watchdog timer to 10928c2ecf20Sopenharmony_ci * match larger received frames 10938c2ecf20Sopenharmony_ci */ 10948c2ecf20Sopenharmony_ci if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) == 10958c2ecf20Sopenharmony_ci IEEE80211_HT_MAX_AMPDU_64K) { 10968c2ecf20Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX); 10978c2ecf20Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX); 10988c2ecf20Sopenharmony_ci } else { 10998c2ecf20Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF); 11008c2ecf20Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF); 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci/* 11058c2ecf20Sopenharmony_ci * callback function that helps invalidating ampdu packets in a DMA queue 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_cistatic void dma_cb_fn_ampdu(void *txi, void *arg_a) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct ieee80211_sta *sta = arg_a; 11108c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && 11138c2ecf20Sopenharmony_ci (tx_info->rate_driver_data[0] == sta || sta == NULL)) 11148c2ecf20Sopenharmony_ci tx_info->rate_driver_data[0] = NULL; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci/* 11188c2ecf20Sopenharmony_ci * When a remote party is no longer available for ampdu communication, any 11198c2ecf20Sopenharmony_ci * pending tx ampdu packets in the driver have to be flushed. 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_civoid brcms_c_ampdu_flush(struct brcms_c_info *wlc, 11228c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u16 tid) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu); 11258c2ecf20Sopenharmony_ci} 1126