diff options
author | Yan-Hsuan Chuang <yhchuang@realtek.com> | 2019-10-02 14:35:20 +0800 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2019-10-04 16:44:55 +0300 |
commit | 3745d3e550d1e6c4301596ac05a5fe82c11301ce (patch) | |
tree | 0904b12c62c9df0ca6c642640f2d4e6b5f691090 /drivers/net/wireless/realtek/rtw88/tx.c | |
parent | 942e2a5d39a9706aa0115078a67659b4d071aece (diff) |
rtw88: add driver TX queue support
The mac80211 provides software TX queue for driver, as long as
driver has hooked ieee80211_ops::wake_tx_queue. Each time a
packet is queued onto the TX queue, that queue will be woken
up the inform driver to serve the queue.
Now driver only supports PCI interface ICs, there's no specific
traffic control for each queue, just schedule a tasklet, and
dump all of the packets at once to the DMA ring. Instead of TX
the packets whenever TX queue is woke, tasklet handler can have
more packets dumped to the device, takes advantage of burst
write with DMA engine.
And if the driver is going to support USB/SDIO ICs, the tasklet
can be more flexible for aggregating the packets, enhance the
efficiency of bandwidth usage.
Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Diffstat (limited to 'drivers/net/wireless/realtek/rtw88/tx.c')
-rw-r--r-- | drivers/net/wireless/realtek/rtw88/tx.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 25fa932d0208..1e19bdac1d45 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -367,3 +367,98 @@ void rtw_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev, pkt_info->qsel = TX_DESC_QSEL_MGMT; pkt_info->ls = true; } + +void rtw_tx(struct rtw_dev *rtwdev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct rtw_tx_pkt_info pkt_info = {0}; + + rtw_tx_pkt_info_update(rtwdev, &pkt_info, control, skb); + if (rtw_hci_tx(rtwdev, &pkt_info, skb)) + goto out; + + return; + +out: + ieee80211_free_txskb(rtwdev->hw, skb); +} + +static bool rtw_txq_dequeue(struct rtw_dev *rtwdev, + struct rtw_txq *rtwtxq) +{ + struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); + struct ieee80211_tx_control control; + struct sk_buff *skb; + + skb = ieee80211_tx_dequeue(rtwdev->hw, txq); + if (!skb) + return false; + + control.sta = txq->sta; + rtw_tx(rtwdev, &control, skb); + rtwtxq->last_push = jiffies; + + return true; +} + +static void rtw_txq_push(struct rtw_dev *rtwdev, + struct rtw_txq *rtwtxq, + unsigned long frames) +{ + int i; + + rcu_read_lock(); + + for (i = 0; i < frames; i++) + if (!rtw_txq_dequeue(rtwdev, rtwtxq)) + break; + + rcu_read_unlock(); +} + +void rtw_tx_tasklet(unsigned long data) +{ + struct rtw_dev *rtwdev = (void *)data; + struct rtw_txq *rtwtxq, *tmp; + + spin_lock_bh(&rtwdev->txq_lock); + + list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->txqs, list) { + struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); + unsigned long frame_cnt; + unsigned long byte_cnt; + + ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt); + rtw_txq_push(rtwdev, rtwtxq, frame_cnt); + + list_del_init(&rtwtxq->list); + } + + spin_unlock_bh(&rtwdev->txq_lock); +} + +void rtw_txq_init(struct rtw_dev *rtwdev, struct ieee80211_txq *txq) +{ + struct rtw_txq *rtwtxq; + + if (!txq) + return; + + rtwtxq = (struct rtw_txq *)txq->drv_priv; + INIT_LIST_HEAD(&rtwtxq->list); +} + +void rtw_txq_cleanup(struct rtw_dev *rtwdev, struct ieee80211_txq *txq) +{ + struct rtw_txq *rtwtxq; + + if (!txq) + return; + + rtwtxq = (struct rtw_txq *)txq->drv_priv; + spin_lock_bh(&rtwdev->txq_lock); + if (!list_empty(&rtwtxq->list)) + list_del_init(&rtwtxq->list); + spin_unlock_bh(&rtwdev->txq_lock); +} |