mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
The PS queue filed redirected frames under the TID taken from the TXD, while mac80211 was told about the TID taken from the QoS header, or TID 0 for anything that is not a QoS data frame. When the two disagree, mt7603_release_buffered_frames() skips the frame because it does not match the requested TIDs, so it stays in the PS queue until the station wakes up. Buffered MMPDUs hit this whenever they were sent on a non-zero TID. Use one TID for both. Link: https://patch.msgid.link/[email protected] Signed-off-by: Felix Fietkau <[email protected]>
261 lines
6.1 KiB
C
261 lines
6.1 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
|
|
#include "mt7603.h"
|
|
#include "mac.h"
|
|
#include "../dma.h"
|
|
|
|
static const u8 wmm_queue_map[] = {
|
|
[IEEE80211_AC_BK] = 0,
|
|
[IEEE80211_AC_BE] = 1,
|
|
[IEEE80211_AC_VI] = 2,
|
|
[IEEE80211_AC_VO] = 3,
|
|
};
|
|
|
|
static void
|
|
mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb)
|
|
{
|
|
static const u8 tid_to_ac[8] = {
|
|
IEEE80211_AC_BE,
|
|
IEEE80211_AC_BK,
|
|
IEEE80211_AC_BK,
|
|
IEEE80211_AC_BE,
|
|
IEEE80211_AC_VI,
|
|
IEEE80211_AC_VI,
|
|
IEEE80211_AC_VO,
|
|
IEEE80211_AC_VO
|
|
};
|
|
__le32 *txd = (__le32 *)skb->data;
|
|
struct ieee80211_hdr *hdr;
|
|
struct ieee80211_sta *sta;
|
|
struct mt7603_sta *msta;
|
|
struct mt76_wcid *wcid;
|
|
u8 qid, tid = 0, hwq = 0;
|
|
void *priv;
|
|
int idx;
|
|
u32 val;
|
|
|
|
if (skb->len < MT_TXD_SIZE + sizeof(struct ieee80211_hdr))
|
|
goto free;
|
|
|
|
val = le32_to_cpu(txd[1]);
|
|
idx = FIELD_GET(MT_TXD1_WLAN_IDX, val);
|
|
|
|
if (idx >= MT7603_WTBL_STA - 1)
|
|
goto free;
|
|
|
|
wcid = mt76_wcid_ptr(dev, idx);
|
|
if (!wcid)
|
|
goto free;
|
|
|
|
priv = msta = container_of(wcid, struct mt7603_sta, wcid);
|
|
|
|
sta = container_of(priv, struct ieee80211_sta, drv_priv);
|
|
hdr = (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE];
|
|
|
|
hwq = wmm_queue_map[IEEE80211_AC_BE];
|
|
if (ieee80211_is_data_qos(hdr->frame_control)) {
|
|
tid = *ieee80211_get_qos_ctl(hdr) &
|
|
IEEE80211_QOS_CTL_TAG1D_MASK;
|
|
qid = tid_to_ac[tid];
|
|
hwq = wmm_queue_map[qid];
|
|
skb_set_queue_mapping(skb, qid);
|
|
} else if (ieee80211_is_data(hdr->frame_control)) {
|
|
skb_set_queue_mapping(skb, IEEE80211_AC_BE);
|
|
hwq = wmm_queue_map[IEEE80211_AC_BE];
|
|
} else {
|
|
skb_pull(skb, MT_TXD_SIZE);
|
|
if (!ieee80211_is_bufferable_mmpdu(skb))
|
|
goto free;
|
|
skb_push(skb, MT_TXD_SIZE);
|
|
skb_set_queue_mapping(skb, MT_TXQ_PSD);
|
|
hwq = MT_TX_HW_QUEUE_MGMT;
|
|
}
|
|
|
|
skb->priority = tid;
|
|
ieee80211_sta_set_buffered(sta, tid, true);
|
|
|
|
val = le32_to_cpu(txd[0]);
|
|
val &= ~(MT_TXD0_P_IDX | MT_TXD0_Q_IDX);
|
|
val |= FIELD_PREP(MT_TXD0_Q_IDX, hwq);
|
|
txd[0] = cpu_to_le32(val);
|
|
|
|
spin_lock_bh(&dev->ps_lock);
|
|
__skb_queue_tail(&msta->psq, skb);
|
|
if (skb_queue_len(&msta->psq) >= 64) {
|
|
skb = __skb_dequeue(&msta->psq);
|
|
dev_kfree_skb(skb);
|
|
}
|
|
spin_unlock_bh(&dev->ps_lock);
|
|
return;
|
|
|
|
free:
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
|
|
struct sk_buff *skb, u32 *info)
|
|
{
|
|
struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);
|
|
__le32 *rxd = (__le32 *)skb->data;
|
|
__le32 *end = (__le32 *)&skb->data[skb->len];
|
|
enum rx_pkt_type type;
|
|
|
|
type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
|
|
|
|
if (q == MT_RXQ_MCU) {
|
|
if (type == PKT_TYPE_RX_EVENT)
|
|
mt76_mcu_rx_event(&dev->mt76, skb);
|
|
else
|
|
mt7603_rx_loopback_skb(dev, skb);
|
|
return;
|
|
}
|
|
|
|
switch (type) {
|
|
case PKT_TYPE_TXS:
|
|
for (rxd++; rxd + 5 <= end; rxd += 5)
|
|
mt7603_mac_add_txs(dev, rxd);
|
|
dev_kfree_skb(skb);
|
|
break;
|
|
case PKT_TYPE_RX_EVENT:
|
|
mt76_mcu_rx_event(&dev->mt76, skb);
|
|
return;
|
|
case PKT_TYPE_NORMAL:
|
|
if (mt7603_mac_fill_rx(dev, skb) == 0) {
|
|
mt76_rx(&dev->mt76, q, skb);
|
|
return;
|
|
}
|
|
fallthrough;
|
|
default:
|
|
dev_kfree_skb(skb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
mt7603_init_rx_queue(struct mt7603_dev *dev, struct mt76_queue *q,
|
|
int idx, int n_desc, int bufsize)
|
|
{
|
|
int err;
|
|
|
|
err = mt76_queue_alloc(dev, q, idx, n_desc, bufsize,
|
|
MT_RX_RING_BASE);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
mt7603_irq_enable(dev, MT_INT_RX_DONE(idx));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mt7603_poll_tx(struct napi_struct *napi, int budget)
|
|
{
|
|
struct mt7603_dev *dev;
|
|
int i;
|
|
|
|
dev = container_of(napi, struct mt7603_dev, mt76.tx_napi);
|
|
dev->tx_dma_check = 0;
|
|
|
|
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false);
|
|
for (i = MT_TXQ_PSD; i >= 0; i--)
|
|
mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false);
|
|
|
|
if (napi_complete_done(napi, 0))
|
|
mt7603_irq_enable(dev, MT_INT_TX_DONE_ALL);
|
|
|
|
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false);
|
|
for (i = MT_TXQ_PSD; i >= 0; i--)
|
|
mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false);
|
|
|
|
mt7603_mac_sta_poll(dev);
|
|
|
|
mt76_worker_schedule(&dev->mt76.tx_worker);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7603_dma_init(struct mt7603_dev *dev)
|
|
{
|
|
int ret;
|
|
int i;
|
|
|
|
mt76_dma_attach(&dev->mt76);
|
|
|
|
mt76_clear(dev, MT_WPDMA_GLO_CFG,
|
|
MT_WPDMA_GLO_CFG_TX_DMA_EN |
|
|
MT_WPDMA_GLO_CFG_RX_DMA_EN |
|
|
MT_WPDMA_GLO_CFG_DMA_BURST_SIZE |
|
|
MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
|
|
|
|
mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
|
|
mt7603_pse_client_reset(dev);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) {
|
|
ret = mt76_init_tx_queue(&dev->mphy, i, wmm_queue_map[i],
|
|
MT7603_TX_RING_SIZE, MT_TX_RING_BASE,
|
|
NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT_TX_HW_QUEUE_MGMT,
|
|
MT7603_PSD_RING_SIZE, MT_TX_RING_BASE, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT_TX_HW_QUEUE_MCU,
|
|
MT_MCU_RING_SIZE, MT_TX_RING_BASE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_BEACON, MT_TX_HW_QUEUE_BCN,
|
|
MT_MCU_RING_SIZE, MT_TX_RING_BASE, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_CAB, MT_TX_HW_QUEUE_BMC,
|
|
MT_MCU_RING_SIZE, MT_TX_RING_BASE, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mt7603_irq_enable(dev,
|
|
MT_INT_TX_DONE(IEEE80211_AC_VO) |
|
|
MT_INT_TX_DONE(IEEE80211_AC_VI) |
|
|
MT_INT_TX_DONE(IEEE80211_AC_BE) |
|
|
MT_INT_TX_DONE(IEEE80211_AC_BK) |
|
|
MT_INT_TX_DONE(MT_TX_HW_QUEUE_MGMT) |
|
|
MT_INT_TX_DONE(MT_TX_HW_QUEUE_MCU) |
|
|
MT_INT_TX_DONE(MT_TX_HW_QUEUE_BCN) |
|
|
MT_INT_TX_DONE(MT_TX_HW_QUEUE_BMC));
|
|
|
|
ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1,
|
|
MT7603_MCU_RX_RING_SIZE, MT_RX_BUF_SIZE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0,
|
|
MT7603_RX_RING_SIZE, MT_RX_BUF_SIZE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mt76_wr(dev, MT_DELAY_INT_CFG, 0);
|
|
ret = mt76_init_queues(dev, mt76_dma_rx_poll);
|
|
if (ret)
|
|
return ret;
|
|
|
|
netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
|
|
mt7603_poll_tx);
|
|
napi_enable(&dev->mt76.tx_napi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mt7603_dma_cleanup(struct mt7603_dev *dev)
|
|
{
|
|
mt76_clear(dev, MT_WPDMA_GLO_CFG,
|
|
MT_WPDMA_GLO_CFG_TX_DMA_EN |
|
|
MT_WPDMA_GLO_CFG_RX_DMA_EN |
|
|
MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
|
|
|
|
mt76_dma_cleanup(&dev->mt76);
|
|
}
|