diff --git a/main.c b/main.c index 8bcf83cd..dc581567 100644 --- a/main.c +++ b/main.c @@ -452,6 +452,7 @@ const struct ieee80211_ops mt76_ops = { .get_txpower = mt76_get_txpower, .wake_tx_queue = mt76_wake_tx_queue, .sta_rate_tbl_update = mt76_sta_rate_tbl_update, + .release_buffered_frames = mt76_release_buffered_frames, }; void mt76_rx(struct mt76_dev *dev, struct sk_buff *skb) diff --git a/mt76.h b/mt76.h index 205a9bb7..c7330e6f 100644 --- a/mt76.h +++ b/mt76.h @@ -339,4 +339,10 @@ void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq); void mt76_txq_remove(struct mt76_dev *dev, struct ieee80211_txq *txq); void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq); +void mt76_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data); + #endif diff --git a/tx.c b/tx.c index 4603337d..ac398c9a 100644 --- a/tx.c +++ b/tx.c @@ -202,6 +202,62 @@ mt76_txq_dequeue(struct mt76_dev *dev, struct mt76_txq *mtxq, bool ps) return ieee80211_tx_dequeue(dev->hw, txq); } +static void +mt76_queue_ps_skb(struct mt76_dev *dev, struct ieee80211_sta *sta, + struct sk_buff *skb, bool last) +{ + struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt76_queue *hwq = &dev->q_tx[MT_TXQ_PSD]; + struct mt76_wcid *wcid = &msta->wcid; + + info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE; + if (last) + info->flags |= IEEE80211_TX_STATUS_EOSP; + + mt76_skb_set_moredata(skb, !last); + mt76_tx_queue_skb(dev, hwq, skb, wcid, sta); +} + +void +mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct mt76_dev *dev = hw->priv; + struct sk_buff *last_skb = NULL; + struct mt76_queue *hwq = &dev->q_tx[MT_TXQ_PSD]; + int i; + + for (i = 0; tids && nframes; i++, tids >>= 1) { + struct ieee80211_txq *txq = sta->txq[i]; + struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv; + struct sk_buff *skb; + + if (!(tids & 1)) + continue; + + do { + skb = mt76_txq_dequeue(dev, mtxq, true); + if (!skb) + break; + + nframes--; + if (last_skb) { + mt76_queue_ps_skb(dev, sta, last_skb, false); + last_skb = skb; + } + } while (nframes); + } + + if (!last_skb) + return; + + mt76_queue_ps_skb(dev, sta, last_skb, true); + mt76_kick_queue(dev, hwq); +} + static int mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq, struct mt76_txq *mtxq, bool *empty)