18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "mt76.h" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_cistatic int 98c2ecf20Sopenharmony_cimt76_txq_get_qid(struct ieee80211_txq *txq) 108c2ecf20Sopenharmony_ci{ 118c2ecf20Sopenharmony_ci if (!txq->sta) 128c2ecf20Sopenharmony_ci return MT_TXQ_BE; 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci return txq->ac; 158c2ecf20Sopenharmony_ci} 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_civoid 188c2ecf20Sopenharmony_cimt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 218c2ecf20Sopenharmony_ci struct ieee80211_txq *txq; 228c2ecf20Sopenharmony_ci struct mt76_txq *mtxq; 238c2ecf20Sopenharmony_ci u8 tid; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (!sta || !ieee80211_is_data_qos(hdr->frame_control) || 268c2ecf20Sopenharmony_ci !ieee80211_is_data_present(hdr->frame_control)) 278c2ecf20Sopenharmony_ci return; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; 308c2ecf20Sopenharmony_ci txq = sta->txq[tid]; 318c2ecf20Sopenharmony_ci mtxq = (struct mt76_txq *)txq->drv_priv; 328c2ecf20Sopenharmony_ci if (!mtxq->aggr) 338c2ecf20Sopenharmony_ci return; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci mtxq->agg_ssn = le16_to_cpu(hdr->seq_ctrl) + 0x10; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_check_agg_ssn); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_civoid 408c2ecf20Sopenharmony_cimt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list) 418c2ecf20Sopenharmony_ci __acquires(&dev->status_list.lock) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci __skb_queue_head_init(list); 448c2ecf20Sopenharmony_ci spin_lock_bh(&dev->status_list.lock); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_status_lock); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_civoid 498c2ecf20Sopenharmony_cimt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list) 508c2ecf20Sopenharmony_ci __releases(&dev->status_list.lock) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct ieee80211_hw *hw; 538c2ecf20Sopenharmony_ci struct sk_buff *skb; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci spin_unlock_bh(&dev->status_list.lock); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(list)) != NULL) { 588c2ecf20Sopenharmony_ci hw = mt76_tx_status_get_hw(dev, skb); 598c2ecf20Sopenharmony_ci ieee80211_tx_status(hw, skb); 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_status_unlock); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void 668c2ecf20Sopenharmony_ci__mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags, 678c2ecf20Sopenharmony_ci struct sk_buff_head *list) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 708c2ecf20Sopenharmony_ci struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); 718c2ecf20Sopenharmony_ci u8 done = MT_TX_CB_DMA_DONE | MT_TX_CB_TXS_DONE; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci flags |= cb->flags; 748c2ecf20Sopenharmony_ci cb->flags = flags; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if ((flags & done) != done) 778c2ecf20Sopenharmony_ci return; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci __skb_unlink(skb, &dev->status_list); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Tx status can be unreliable. if it fails, mark the frame as ACKed */ 828c2ecf20Sopenharmony_ci if (flags & MT_TX_CB_TXS_FAILED) { 838c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(info); 848c2ecf20Sopenharmony_ci info->status.rates[0].idx = -1; 858c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci __skb_queue_tail(list, skb); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_civoid 928c2ecf20Sopenharmony_cimt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, 938c2ecf20Sopenharmony_ci struct sk_buff_head *list) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_DONE, list); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_status_skb_done); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciint 1008c2ecf20Sopenharmony_cimt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid, 1018c2ecf20Sopenharmony_ci struct sk_buff *skb) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 1048c2ecf20Sopenharmony_ci struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); 1058c2ecf20Sopenharmony_ci int pid; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!wcid) 1088c2ecf20Sopenharmony_ci return MT_PACKET_ID_NO_ACK; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_NO_ACK) 1118c2ecf20Sopenharmony_ci return MT_PACKET_ID_NO_ACK; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!(info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS | 1148c2ecf20Sopenharmony_ci IEEE80211_TX_CTL_RATE_CTRL_PROBE))) 1158c2ecf20Sopenharmony_ci return MT_PACKET_ID_NO_SKB; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_lock_bh(&dev->status_list.lock); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci memset(cb, 0, sizeof(*cb)); 1208c2ecf20Sopenharmony_ci wcid->packet_id = (wcid->packet_id + 1) & MT_PACKET_ID_MASK; 1218c2ecf20Sopenharmony_ci if (wcid->packet_id == MT_PACKET_ID_NO_ACK || 1228c2ecf20Sopenharmony_ci wcid->packet_id == MT_PACKET_ID_NO_SKB) 1238c2ecf20Sopenharmony_ci wcid->packet_id = MT_PACKET_ID_FIRST; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci pid = wcid->packet_id; 1268c2ecf20Sopenharmony_ci cb->wcid = wcid->idx; 1278c2ecf20Sopenharmony_ci cb->pktid = pid; 1288c2ecf20Sopenharmony_ci cb->jiffies = jiffies; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci __skb_queue_tail(&dev->status_list, skb); 1318c2ecf20Sopenharmony_ci spin_unlock_bh(&dev->status_list.lock); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return pid; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_status_skb_add); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistruct sk_buff * 1388c2ecf20Sopenharmony_cimt76_tx_status_skb_get(struct mt76_dev *dev, struct mt76_wcid *wcid, int pktid, 1398c2ecf20Sopenharmony_ci struct sk_buff_head *list) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct sk_buff *skb, *tmp; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci skb_queue_walk_safe(&dev->status_list, skb, tmp) { 1448c2ecf20Sopenharmony_ci struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (wcid && cb->wcid != wcid->idx) 1478c2ecf20Sopenharmony_ci continue; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (cb->pktid == pktid) 1508c2ecf20Sopenharmony_ci return skb; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (pktid >= 0 && !time_after(jiffies, cb->jiffies + 1538c2ecf20Sopenharmony_ci MT_TX_STATUS_SKB_TIMEOUT)) 1548c2ecf20Sopenharmony_ci continue; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_FAILED | 1578c2ecf20Sopenharmony_ci MT_TX_CB_TXS_DONE, list); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return NULL; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_status_skb_get); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_civoid 1658c2ecf20Sopenharmony_cimt76_tx_status_check(struct mt76_dev *dev, struct mt76_wcid *wcid, bool flush) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct sk_buff_head list; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci mt76_tx_status_lock(dev, &list); 1708c2ecf20Sopenharmony_ci mt76_tx_status_skb_get(dev, wcid, flush ? -1 : 0, &list); 1718c2ecf20Sopenharmony_ci mt76_tx_status_unlock(dev, &list); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_status_check); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void 1768c2ecf20Sopenharmony_cimt76_tx_check_non_aql(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 1798c2ecf20Sopenharmony_ci struct mt76_wcid *wcid; 1808c2ecf20Sopenharmony_ci int pending; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (info->tx_time_est) 1838c2ecf20Sopenharmony_ci return; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (wcid_idx >= ARRAY_SIZE(dev->wcid)) 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci rcu_read_lock(); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci wcid = rcu_dereference(dev->wcid[wcid_idx]); 1918c2ecf20Sopenharmony_ci if (wcid) { 1928c2ecf20Sopenharmony_ci pending = atomic_dec_return(&wcid->non_aql_packets); 1938c2ecf20Sopenharmony_ci if (pending < 0) 1948c2ecf20Sopenharmony_ci atomic_cmpxchg(&wcid->non_aql_packets, pending, 0); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rcu_read_unlock(); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_civoid mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct ieee80211_hw *hw; 2038c2ecf20Sopenharmony_ci struct sk_buff_head list; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE 2068c2ecf20Sopenharmony_ci if (skb == dev->test.tx_skb) { 2078c2ecf20Sopenharmony_ci dev->test.tx_done++; 2088c2ecf20Sopenharmony_ci if (dev->test.tx_queued == dev->test.tx_done) 2098c2ecf20Sopenharmony_ci wake_up(&dev->tx_wait); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci mt76_tx_check_non_aql(dev, wcid_idx, skb); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!skb->prev) { 2168c2ecf20Sopenharmony_ci hw = mt76_tx_status_get_hw(dev, skb); 2178c2ecf20Sopenharmony_ci ieee80211_free_txskb(hw, skb); 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci mt76_tx_status_lock(dev, &list); 2228c2ecf20Sopenharmony_ci __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_DMA_DONE, &list); 2238c2ecf20Sopenharmony_ci mt76_tx_status_unlock(dev, &list); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx_complete_skb); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int 2288c2ecf20Sopenharmony_ci__mt76_tx_queue_skb(struct mt76_dev *dev, int qid, struct sk_buff *skb, 2298c2ecf20Sopenharmony_ci struct mt76_wcid *wcid, struct ieee80211_sta *sta, 2308c2ecf20Sopenharmony_ci bool *stop) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 2338c2ecf20Sopenharmony_ci struct mt76_queue *q; 2348c2ecf20Sopenharmony_ci bool non_aql; 2358c2ecf20Sopenharmony_ci int pending; 2368c2ecf20Sopenharmony_ci int idx; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci non_aql = !info->tx_time_est; 2398c2ecf20Sopenharmony_ci idx = dev->queue_ops->tx_queue_skb(dev, qid, skb, wcid, sta); 2408c2ecf20Sopenharmony_ci if (idx < 0 || !sta || !non_aql) 2418c2ecf20Sopenharmony_ci return idx; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci wcid = (struct mt76_wcid *)sta->drv_priv; 2448c2ecf20Sopenharmony_ci q = dev->q_tx[qid]; 2458c2ecf20Sopenharmony_ci q->entry[idx].wcid = wcid->idx; 2468c2ecf20Sopenharmony_ci pending = atomic_inc_return(&wcid->non_aql_packets); 2478c2ecf20Sopenharmony_ci if (stop && pending >= MT_MAX_NON_AQL_PKT) 2488c2ecf20Sopenharmony_ci *stop = true; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return idx; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_civoid 2548c2ecf20Sopenharmony_cimt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta, 2558c2ecf20Sopenharmony_ci struct mt76_wcid *wcid, struct sk_buff *skb) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct mt76_dev *dev = phy->dev; 2588c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 2598c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 2608c2ecf20Sopenharmony_ci struct mt76_queue *q; 2618c2ecf20Sopenharmony_ci int qid = skb_get_queue_mapping(skb); 2628c2ecf20Sopenharmony_ci bool ext_phy = phy != &dev->phy; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (mt76_testmode_enabled(dev)) { 2658c2ecf20Sopenharmony_ci ieee80211_free_txskb(phy->hw, skb); 2668c2ecf20Sopenharmony_ci return; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (WARN_ON(qid >= MT_TXQ_PSD)) { 2708c2ecf20Sopenharmony_ci qid = MT_TXQ_BE; 2718c2ecf20Sopenharmony_ci skb_set_queue_mapping(skb, qid); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) && 2758c2ecf20Sopenharmony_ci !ieee80211_is_data(hdr->frame_control) && 2768c2ecf20Sopenharmony_ci !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) { 2778c2ecf20Sopenharmony_ci qid = MT_TXQ_PSD; 2788c2ecf20Sopenharmony_ci skb_set_queue_mapping(skb, qid); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (wcid && !(wcid->tx_info & MT_WCID_TX_INFO_SET)) 2828c2ecf20Sopenharmony_ci ieee80211_get_tx_rates(info->control.vif, sta, skb, 2838c2ecf20Sopenharmony_ci info->control.rates, 1); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (ext_phy) 2868c2ecf20Sopenharmony_ci info->hw_queue |= MT_TX_HW_QUEUE_EXT_PHY; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci q = dev->q_tx[qid]; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci spin_lock_bh(&q->lock); 2918c2ecf20Sopenharmony_ci __mt76_tx_queue_skb(dev, qid, skb, wcid, sta, NULL); 2928c2ecf20Sopenharmony_ci dev->queue_ops->kick(dev, q); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (q->queued > q->ndesc - 8 && !q->stopped) { 2958c2ecf20Sopenharmony_ci ieee80211_stop_queue(phy->hw, skb_get_queue_mapping(skb)); 2968c2ecf20Sopenharmony_ci q->stopped = true; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci spin_unlock_bh(&q->lock); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_tx); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic struct sk_buff * 3048c2ecf20Sopenharmony_cimt76_txq_dequeue(struct mt76_phy *phy, struct mt76_txq *mtxq) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct ieee80211_txq *txq = mtxq_to_txq(mtxq); 3078c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 3088c2ecf20Sopenharmony_ci bool ext_phy = phy != &phy->dev->phy; 3098c2ecf20Sopenharmony_ci struct sk_buff *skb; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci skb = ieee80211_tx_dequeue(phy->hw, txq); 3128c2ecf20Sopenharmony_ci if (!skb) 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 3168c2ecf20Sopenharmony_ci if (ext_phy) 3178c2ecf20Sopenharmony_ci info->hw_queue |= MT_TX_HW_QUEUE_EXT_PHY; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return skb; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void 3238c2ecf20Sopenharmony_cimt76_queue_ps_skb(struct mt76_dev *dev, struct ieee80211_sta *sta, 3248c2ecf20Sopenharmony_ci struct sk_buff *skb, bool last) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; 3278c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE; 3308c2ecf20Sopenharmony_ci if (last) 3318c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STATUS_EOSP | 3328c2ecf20Sopenharmony_ci IEEE80211_TX_CTL_REQ_TX_STATUS; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci mt76_skb_set_moredata(skb, !last); 3358c2ecf20Sopenharmony_ci __mt76_tx_queue_skb(dev, MT_TXQ_PSD, skb, wcid, sta, NULL); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_civoid 3398c2ecf20Sopenharmony_cimt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, 3408c2ecf20Sopenharmony_ci u16 tids, int nframes, 3418c2ecf20Sopenharmony_ci enum ieee80211_frame_release_type reason, 3428c2ecf20Sopenharmony_ci bool more_data) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct mt76_phy *phy = hw->priv; 3458c2ecf20Sopenharmony_ci struct mt76_dev *dev = phy->dev; 3468c2ecf20Sopenharmony_ci struct sk_buff *last_skb = NULL; 3478c2ecf20Sopenharmony_ci struct mt76_queue *hwq = dev->q_tx[MT_TXQ_PSD]; 3488c2ecf20Sopenharmony_ci int i; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci spin_lock_bh(&hwq->lock); 3518c2ecf20Sopenharmony_ci for (i = 0; tids && nframes; i++, tids >>= 1) { 3528c2ecf20Sopenharmony_ci struct ieee80211_txq *txq = sta->txq[i]; 3538c2ecf20Sopenharmony_ci struct mt76_txq *mtxq = (struct mt76_txq *)txq->drv_priv; 3548c2ecf20Sopenharmony_ci struct sk_buff *skb; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!(tids & 1)) 3578c2ecf20Sopenharmony_ci continue; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci do { 3608c2ecf20Sopenharmony_ci skb = mt76_txq_dequeue(phy, mtxq); 3618c2ecf20Sopenharmony_ci if (!skb) 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci nframes--; 3658c2ecf20Sopenharmony_ci if (last_skb) 3668c2ecf20Sopenharmony_ci mt76_queue_ps_skb(dev, sta, last_skb, false); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci last_skb = skb; 3698c2ecf20Sopenharmony_ci } while (nframes); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (last_skb) { 3738c2ecf20Sopenharmony_ci mt76_queue_ps_skb(dev, sta, last_skb, true); 3748c2ecf20Sopenharmony_ci dev->queue_ops->kick(dev, hwq); 3758c2ecf20Sopenharmony_ci } else { 3768c2ecf20Sopenharmony_ci ieee80211_sta_eosp(sta); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci spin_unlock_bh(&hwq->lock); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_release_buffered_frames); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int 3848c2ecf20Sopenharmony_cimt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, 3858c2ecf20Sopenharmony_ci struct mt76_txq *mtxq) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct mt76_dev *dev = phy->dev; 3888c2ecf20Sopenharmony_ci struct ieee80211_txq *txq = mtxq_to_txq(mtxq); 3898c2ecf20Sopenharmony_ci enum mt76_txq_id qid = mt76_txq_get_qid(txq); 3908c2ecf20Sopenharmony_ci struct mt76_wcid *wcid = mtxq->wcid; 3918c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 3928c2ecf20Sopenharmony_ci struct sk_buff *skb; 3938c2ecf20Sopenharmony_ci int n_frames = 1; 3948c2ecf20Sopenharmony_ci bool stop = false; 3958c2ecf20Sopenharmony_ci int idx; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci skb = mt76_txq_dequeue(phy, mtxq); 4048c2ecf20Sopenharmony_ci if (!skb) 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 4088c2ecf20Sopenharmony_ci if (!(wcid->tx_info & MT_WCID_TX_INFO_SET)) 4098c2ecf20Sopenharmony_ci ieee80211_get_tx_rates(txq->vif, txq->sta, skb, 4108c2ecf20Sopenharmony_ci info->control.rates, 1); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci idx = __mt76_tx_queue_skb(dev, qid, skb, wcid, txq->sta, &stop); 4138c2ecf20Sopenharmony_ci if (idx < 0) 4148c2ecf20Sopenharmony_ci return idx; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci do { 4178c2ecf20Sopenharmony_ci if (test_bit(MT76_STATE_PM, &phy->state) || 4188c2ecf20Sopenharmony_ci test_bit(MT76_RESET, &phy->state)) 4198c2ecf20Sopenharmony_ci return -EBUSY; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (stop) 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (q->queued + MT_TXQ_FREE_THR >= q->ndesc) 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci skb = mt76_txq_dequeue(phy, mtxq); 4288c2ecf20Sopenharmony_ci if (!skb) 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 4328c2ecf20Sopenharmony_ci if (!(wcid->tx_info & MT_WCID_TX_INFO_SET)) 4338c2ecf20Sopenharmony_ci ieee80211_get_tx_rates(txq->vif, txq->sta, skb, 4348c2ecf20Sopenharmony_ci info->control.rates, 1); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci idx = __mt76_tx_queue_skb(dev, qid, skb, wcid, txq->sta, &stop); 4378c2ecf20Sopenharmony_ci if (idx < 0) 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci n_frames++; 4418c2ecf20Sopenharmony_ci } while (1); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci dev->queue_ops->kick(dev, q); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return n_frames; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int 4498c2ecf20Sopenharmony_cimt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct mt76_dev *dev = phy->dev; 4528c2ecf20Sopenharmony_ci struct mt76_queue *q = dev->q_tx[qid]; 4538c2ecf20Sopenharmony_ci struct ieee80211_txq *txq; 4548c2ecf20Sopenharmony_ci struct mt76_txq *mtxq; 4558c2ecf20Sopenharmony_ci struct mt76_wcid *wcid; 4568c2ecf20Sopenharmony_ci int ret = 0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci spin_lock_bh(&q->lock); 4598c2ecf20Sopenharmony_ci while (1) { 4608c2ecf20Sopenharmony_ci if (test_bit(MT76_STATE_PM, &phy->state) || 4618c2ecf20Sopenharmony_ci test_bit(MT76_RESET, &phy->state)) { 4628c2ecf20Sopenharmony_ci ret = -EBUSY; 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (q->queued + MT_TXQ_FREE_THR >= q->ndesc) 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci txq = ieee80211_next_txq(phy->hw, qid); 4708c2ecf20Sopenharmony_ci if (!txq) 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci mtxq = (struct mt76_txq *)txq->drv_priv; 4748c2ecf20Sopenharmony_ci wcid = mtxq->wcid; 4758c2ecf20Sopenharmony_ci if (wcid && test_bit(MT_WCID_FLAG_PS, &wcid->flags)) 4768c2ecf20Sopenharmony_ci continue; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (mtxq->send_bar && mtxq->aggr) { 4798c2ecf20Sopenharmony_ci struct ieee80211_txq *txq = mtxq_to_txq(mtxq); 4808c2ecf20Sopenharmony_ci struct ieee80211_sta *sta = txq->sta; 4818c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = txq->vif; 4828c2ecf20Sopenharmony_ci u16 agg_ssn = mtxq->agg_ssn; 4838c2ecf20Sopenharmony_ci u8 tid = txq->tid; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci mtxq->send_bar = false; 4868c2ecf20Sopenharmony_ci spin_unlock_bh(&q->lock); 4878c2ecf20Sopenharmony_ci ieee80211_send_bar(vif, sta->addr, tid, agg_ssn); 4888c2ecf20Sopenharmony_ci spin_lock_bh(&q->lock); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ret += mt76_txq_send_burst(phy, q, mtxq); 4928c2ecf20Sopenharmony_ci ieee80211_return_txq(phy->hw, txq, false); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci spin_unlock_bh(&q->lock); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return ret; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_civoid mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci int len; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (qid >= 4) 5048c2ecf20Sopenharmony_ci return; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci rcu_read_lock(); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci do { 5098c2ecf20Sopenharmony_ci ieee80211_txq_schedule_start(phy->hw, qid); 5108c2ecf20Sopenharmony_ci len = mt76_txq_schedule_list(phy, qid); 5118c2ecf20Sopenharmony_ci ieee80211_txq_schedule_end(phy->hw, qid); 5128c2ecf20Sopenharmony_ci } while (len > 0); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci rcu_read_unlock(); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_txq_schedule); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_civoid mt76_txq_schedule_all(struct mt76_phy *phy) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int i; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci for (i = 0; i <= MT_TXQ_BK; i++) 5238c2ecf20Sopenharmony_ci mt76_txq_schedule(phy, i); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_txq_schedule_all); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_civoid mt76_tx_worker(struct mt76_worker *w) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct mt76_dev *dev = container_of(w, struct mt76_dev, tx_worker); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci mt76_txq_schedule_all(&dev->phy); 5328c2ecf20Sopenharmony_ci if (dev->phy2) 5338c2ecf20Sopenharmony_ci mt76_txq_schedule_all(dev->phy2); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE 5368c2ecf20Sopenharmony_ci if (dev->test.tx_pending) 5378c2ecf20Sopenharmony_ci mt76_testmode_tx_pending(dev); 5388c2ecf20Sopenharmony_ci#endif 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_civoid mt76_stop_tx_queues(struct mt76_dev *dev, struct ieee80211_sta *sta, 5428c2ecf20Sopenharmony_ci bool send_bar) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci int i; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { 5478c2ecf20Sopenharmony_ci struct ieee80211_txq *txq = sta->txq[i]; 5488c2ecf20Sopenharmony_ci struct mt76_queue *hwq; 5498c2ecf20Sopenharmony_ci struct mt76_txq *mtxq; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!txq) 5528c2ecf20Sopenharmony_ci continue; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci hwq = dev->q_tx[mt76_txq_get_qid(txq)]; 5558c2ecf20Sopenharmony_ci mtxq = (struct mt76_txq *)txq->drv_priv; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci spin_lock_bh(&hwq->lock); 5588c2ecf20Sopenharmony_ci mtxq->send_bar = mtxq->aggr && send_bar; 5598c2ecf20Sopenharmony_ci spin_unlock_bh(&hwq->lock); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_stop_tx_queues); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_civoid mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct mt76_phy *phy = hw->priv; 5678c2ecf20Sopenharmony_ci struct mt76_dev *dev = phy->dev; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!test_bit(MT76_STATE_RUNNING, &phy->state)) 5708c2ecf20Sopenharmony_ci return; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci mt76_worker_schedule(&dev->tx_worker); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_wake_tx_queue); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ciu8 mt76_ac_to_hwq(u8 ac) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci static const u8 wmm_queue_map[] = { 5798c2ecf20Sopenharmony_ci [IEEE80211_AC_BE] = 0, 5808c2ecf20Sopenharmony_ci [IEEE80211_AC_BK] = 1, 5818c2ecf20Sopenharmony_ci [IEEE80211_AC_VI] = 2, 5828c2ecf20Sopenharmony_ci [IEEE80211_AC_VO] = 3, 5838c2ecf20Sopenharmony_ci }; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (WARN_ON(ac >= IEEE80211_NUM_ACS)) 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return wmm_queue_map[ac]; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_ac_to_hwq); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ciint mt76_skb_adjust_pad(struct sk_buff *skb, int pad) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct sk_buff *iter, *last = skb; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* First packet of a A-MSDU burst keeps track of the whole burst 5978c2ecf20Sopenharmony_ci * length, need to update length of it and the last packet. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci skb_walk_frags(skb, iter) { 6008c2ecf20Sopenharmony_ci last = iter; 6018c2ecf20Sopenharmony_ci if (!iter->next) { 6028c2ecf20Sopenharmony_ci skb->data_len += pad; 6038c2ecf20Sopenharmony_ci skb->len += pad; 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (skb_pad(last, pad)) 6098c2ecf20Sopenharmony_ci return -ENOMEM; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci __skb_put(last, pad); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_skb_adjust_pad); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_civoid mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q, 6188c2ecf20Sopenharmony_ci struct mt76_queue_entry *e) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (e->skb) 6218c2ecf20Sopenharmony_ci dev->drv->tx_complete_skb(dev, e); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci spin_lock_bh(&q->lock); 6248c2ecf20Sopenharmony_ci q->tail = (q->tail + 1) % q->ndesc; 6258c2ecf20Sopenharmony_ci q->queued--; 6268c2ecf20Sopenharmony_ci spin_unlock_bh(&q->lock); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_queue_tx_complete); 629