/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ #include <net/mac80211.h>
/* max number of mpdus in an ampdu */ #define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu to a legacy */ #define AMPDU_NUM_MPDU_LEGACY 16 /* max Tx ba window size (in pdu) */ #define AMPDU_TX_BA_MAX_WSIZE 64 /* default Tx ba window size (in pdu) */ #define AMPDU_TX_BA_DEF_WSIZE 64 /* default Rx ba window size (in pdu) */ #define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */ #define AMPDU_RX_BA_MAX_WSIZE 64 /* max dur of tx ampdu (in msec) */ #define AMPDU_MAX_DUR 5 /* default tx retry limit */ #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit at reg rate */ #define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default ffpld reserved bytes */ #define AMPDU_DEF_FFPLD_RSVD 2048 /* # of inis to be freed on detach */ #define AMPDU_INI_FREE 10 /* max # of mpdus released at a time */ #define AMPDU_SCB_MAX_RELEASE 20
#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */ #define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu * without underflows
*/ #define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */ #define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */ #define FFPLD_PLD_INCR 1000 /* increments in bytes */ #define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we * accumulate between resets.
*/
#define AMPDU_DELIMITER_LEN 4
/* max allowed number of mpdus in an ampdu (2 streams) */ #define AMPDU_NUM_MPDU 16
/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */ #define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\
AMPDU_DELIMITER_LEN + 3\
+ DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
/* structure to hold tx fifo information and pre-loading state * counters specific to tx underflows of ampdus * some counters might be redundant with the ones in wlc or ampdu structures. * This allows to maintain a specific state independently of * how often and/or when the wlc counters are updated. * * ampdu_pld_size: number of bytes to be pre-loaded * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu * prev_txfunfl: num of underflows last read from the HW macstats counter * accum_txfunfl: num of underflows since we modified pld params * accum_txampdu: num of tx ampdu since we modified pld params * prev_txampdu: previous reading of tx ampdu * dmaxferrate: estimated dma avg xfer rate in kbits/sec
*/ struct brcms_fifo_info {
u16 ampdu_pld_size;
u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1];
u16 prev_txfunfl;
u32 accum_txfunfl;
u32 accum_txampdu;
u32 prev_txampdu;
u32 dmaxferrate;
};
/* AMPDU module specific state * * wlc: pointer to main wlc structure * scb_handle: scb cubby handle to retrieve data from scb * ini_enable: per-tid initiator enable/disable of ampdu * ba_tx_wsize: Tx ba window size (in pdu) * ba_rx_wsize: Rx ba window size (in pdu) * retry_limit: mpdu transmit retry limit * rr_retry_limit: mpdu transmit retry limit at regular rate * retry_limit_tid: per-tid mpdu transmit retry limit * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec * max_pdu: max pdus allowed in ampdu * dur: max duration of an ampdu (in msec) * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes * ffpld_rsvd: number of bytes to reserve for preload * max_txlen: max size of ampdu per mcs, bw and sgi * mfbr: enable multiple fallback rate * tx_max_funl: underflows should be kept such that * (tx_max_funfl*underflows) < tx frames * fifo_tb: table of fifo infos
*/ struct ampdu_info { struct brcms_c_info *wlc; int scb_handle;
u8 ini_enable[AMPDU_MAX_SCB_TID];
u8 ba_tx_wsize;
u8 ba_rx_wsize;
u8 retry_limit;
u8 rr_retry_limit;
u8 retry_limit_tid[AMPDU_MAX_SCB_TID];
u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
u8 mpdu_density;
s8 max_pdu;
u8 dur;
u8 rx_factor;
u32 ffpld_rsvd;
u32 max_txlen[MCS_TABLE_SIZE][2][2]; bool mfbr;
u32 tx_max_funl; struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO];
};
ampdu = kzalloc(sizeof(*ampdu), GFP_ATOMIC); if (!ampdu) return NULL;
ampdu->wlc = wlc;
for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
ampdu->ini_enable[i] = true; /* Disable ampdu for VO by default */
ampdu->ini_enable[PRIO_8021D_VO] = false;
ampdu->ini_enable[PRIO_8021D_NC] = false;
/* Disable ampdu for BK by default since not enough fifo space */
ampdu->ini_enable[PRIO_8021D_NONE] = false;
ampdu->ini_enable[PRIO_8021D_BK] = false;
ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD; /* * bump max ampdu rcv size to 64k for all 11n * devices except 4321A0 and 4321A1
*/ if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K; else
ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K;
ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
ampdu->retry_limit_tid[i] = ampdu->retry_limit;
ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
}
brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur);
ampdu->mfbr = false; /* try to set ampdu to the default value */
brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu);
/* go back to legacy size if some preloading is occurring */ for (i = 0; i < NUM_FFPLD_FIFO; i++) { if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
}
/* apply user override */ if (ampdu->max_pdu != AUTO)
scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
/* fill up the mcs2ampdu table; do not recalc the last mcs */
dma_rate = dma_rate >> 7; for (i = 0; i < FFPLD_MAX_MCS; i++) { /* shifting to keep it within integer range */
phy_rate = mcs_2_rate(i, true, false) >> 7; if (phy_rate > dma_rate) {
tmp = ((fifo->ampdu_pld_size * phy_rate) /
((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
tmp = min_t(u32, tmp, 255);
fifo->mcs2ampdu_table[i] = (u8) tmp;
}
}
}
/* evaluate the dma transfer rate using the tx underflows as feedback. * If necessary, increase tx fifo preloading. If not enough, * decrease maximum ampdu size for each mcs till underflows stop * Return 1 if pre-loading not active, -1 if not an underflow event, * 0 if pre-loading module took care of the event.
*/ staticint brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid)
{ struct ampdu_info *ampdu = wlc->ampdu;
u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
u8 max_mpdu;
u16 max_pld_size;
u32 new_txunfl; struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid);
uint xmtfifo_sz;
u16 cur_txunfl;
/* return if we got here for a different reason than underflows */
cur_txunfl = brcms_b_read_shm(wlc->hw,
M_UCODE_MACSTAT +
offsetof(struct macstat, txfunfl[fid]));
new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl); if (new_txunfl == 0) {
brcms_dbg_ht(wlc->hw->d11core, "TX status FRAG set but no tx underflows\n"); return -1;
}
fifo->prev_txfunfl = cur_txunfl;
if (!ampdu->tx_max_funl) return 1;
/* check if fifo is big enough */ if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz)) return -1;
if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd) return 1;
/* * compute a new dma xfer rate for max_mpdu @ max mcs. * This is the minimum dma rate that can achieve no * underflow condition for the current mpdu size. * * note : we divide/multiply by 100 to avoid integer overflows
*/
fifo->dmaxferrate =
(((phy_rate / 100) *
(max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
/ (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
if (ampdu_frames + 1 > session->max_ampdu_frames ||
session->ampdu_len + len > session->max_ampdu_len) return -ENOSPC;
/* * We aren't really out of space if the new frame is of * a different priority, but we want the same behaviour * so return -ENOSPC anyway. * * XXX: The old AMPDU code did this, but is it really * necessary?
*/
first = skb_peek(&session->skb_list); if (p->priority != first->priority) return -ENOSPC;
}
/* * Now that we're sure this frame can be accommodated, update the * session information.
*/
session->ampdu_len += len;
session->dma_len += p->len;
/* * Treat all frames as "middle" frames of AMPDU here. First and * last frames must be fixed up after all MPDUs have been prepped.
*/
mcl = le16_to_cpu(txh->MacTxControlLow);
mcl &= ~TXC_AMPDU_MASK;
mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
txh->MacTxControlLow = cpu_to_le16(mcl);
txh->PreloadSize = 0; /* always default to 0 */
first = skb_peek(&session->skb_list);
last = skb_peek_tail(&session->skb_list);
/* Need to fix up last MPDU first to adjust AMPDU length */
txh = (struct d11txh *)last->data;
fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
f = &du->fifo_tb[fifo];
/* remove the null delimiter after last mpdu */
ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
/* remove the pad len from last mpdu */
fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
session->ampdu_len -= roundup(len, 4) - len;
/* Now fix up the first MPDU */
tx_info = IEEE80211_SKB_CB(first);
txrate = tx_info->status.rates;
txh = (struct d11txh *)first->data;
plcp = (u8 *)(txh + 1);
rts = (struct ieee80211_rts *)&txh->rts_frame;
mcl = le16_to_cpu(txh->MacTxControlLow); /* If only one MPDU leave it marked as last */ if (first != last) {
mcl &= ~TXC_AMPDU_MASK;
mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
}
mcl |= TXC_STARTMSDU; if (ieee80211_is_rts(rts->frame_control)) {
mcl |= TXC_SENDRTS;
use_rts = true;
} if (ieee80211_is_cts(rts->frame_control)) {
mcl |= TXC_SENDCTS;
use_cts = true;
}
txh->MacTxControlLow = cpu_to_le16(mcl);
/* set flag and plcp for fallback rate */ if (fbr) {
mch |= TXC_AMPDU_FBR;
txh->MacTxControlHigh = cpu_to_le16(mch);
BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
}
ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
p);
ack_recd = true;
}
} /* either retransmit or send bar if ack not recd */ if (!ack_recd) { if (retry && (ini->txretry[index] < (int)retry_limit)) { int ret;
ini->txretry[index]++;
ret = brcms_c_txfifo(wlc, queue, p); /* * We shouldn't be out of space in the DMA * ring here since we're reinserting a frame * that was just pulled out.
*/
WARN_ONCE(ret, "queue %d out of txds\n", queue);
} else { /* Retry timeout */
ieee80211_tx_info_clear_status(tx_info);
tx_info->status.ampdu_ack_len = 0;
tx_info->status.ampdu_len = 1;
tx_info->flags |=
IEEE80211_TX_STAT_AMPDU_NO_BACK;
skb_pull(p, D11_PHY_HDR_LEN);
skb_pull(p, D11_TXH_LEN);
brcms_dbg_ht(wlc->hw->d11core, "BA Timeout, seq %d\n",
seq);
ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
p);
}
}
tot_mpdu++;
/* break out if last packet of ampdu */ if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
TXC_AMPDU_LAST) break;
p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
}
/* update rate state */
brcms_c_antsel_antsel2id(wlc->asi, mimoantsel);
}
/* BMAC_NOTE: For the split driver, second level txstatus comes later * So if the ACK was received then wait for the second level else just * call the first one
*/ if (txs->status & TX_STATUS_ACK_RCV) {
u8 status_delay = 0;
/* wait till the next 8 bytes of txstatus is available */
s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus)); while ((s1 & TXS_V) == 0) {
udelay(1);
status_delay++; if (status_delay > 10) return; /* error condition */
s1 = bcma_read32(wlc->hw->d11core,
D11REGOFFS(frmtxstatus));
}
/* driver needs to write the ta in the template; ta is at offset 16 */
memset(template, 0, sizeof(template));
memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16),
(T_RAM_ACCESS_SZ * 2), template);
}
/* * Extend ucode internal watchdog timer to * match larger received frames
*/ if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) ==
IEEE80211_HT_MAX_AMPDU_64K) {
brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
} else {
brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
}
}
/* * callback function that helps invalidating ampdu packets in a DMA queue
*/ staticvoid dma_cb_fn_ampdu(void *txi, void *arg_a)
{ struct ieee80211_sta *sta = arg_a; struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi;
if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
(tx_info->rate_driver_data[0] == sta || sta == NULL))
tx_info->rate_driver_data[0] = NULL;
}
/* * When a remote party is no longer available for ampdu communication, any * pending tx ampdu packets in the driver have to be flushed.
*/ void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, u16 tid)
{
brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.