Commit d71ef286 authored by Felix Fietkau's avatar Felix Fietkau Committed by Kalle Valo

mt76: implement AP_LINK_PS

With software A-MPDU reordering in place, frames that notify mac80211 of
powersave changes are reordered as well, which can cause connection
stalls. Fix this by implementing powersave state processing in the
driver.

Fixes: aee5b8cf ("mt76: implement A-MPDU rx reordering in the driver code")
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent b954c862
...@@ -276,6 +276,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht, ...@@ -276,6 +276,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
ieee80211_hw_set(hw, TX_AMSDU); ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, TX_FRAG_LIST); ieee80211_hw_set(hw, TX_FRAG_LIST);
ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, MFP_CAPABLE);
ieee80211_hw_set(hw, AP_LINK_PS);
wiphy->flags |= WIPHY_FLAG_IBSS_RSN; wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
...@@ -470,6 +471,53 @@ mt76_check_ccmp_pn(struct sk_buff *skb) ...@@ -470,6 +471,53 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
return 0; return 0;
} }
static void
mt76_check_ps(struct mt76_dev *dev, struct sk_buff *skb)
{
struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_sta *sta;
struct mt76_wcid *wcid = status->wcid;
bool ps;
if (!wcid || !wcid->sta)
return;
sta = container_of((void *) wcid, struct ieee80211_sta, drv_priv);
if (!test_bit(MT_WCID_FLAG_CHECK_PS, &wcid->flags))
return;
if (ieee80211_is_pspoll(hdr->frame_control)) {
ieee80211_sta_pspoll(sta);
return;
}
if (ieee80211_has_morefrags(hdr->frame_control) ||
!(ieee80211_is_mgmt(hdr->frame_control) ||
ieee80211_is_data(hdr->frame_control)))
return;
ps = ieee80211_has_pm(hdr->frame_control);
if (ps && (ieee80211_is_data_qos(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)))
ieee80211_sta_uapsd_trigger(sta, status->tid);
if (!!test_bit(MT_WCID_FLAG_PS, &wcid->flags) == ps)
return;
if (ps) {
set_bit(MT_WCID_FLAG_PS, &wcid->flags);
mt76_stop_tx_queues(dev, sta, true);
} else {
clear_bit(MT_WCID_FLAG_PS, &wcid->flags);
}
ieee80211_sta_ps_transition(sta, ps);
dev->drv->sta_ps(dev, sta, ps);
}
void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
int queue) int queue)
{ {
...@@ -498,8 +546,10 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q) ...@@ -498,8 +546,10 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
__skb_queue_head_init(&frames); __skb_queue_head_init(&frames);
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
mt76_check_ps(dev, skb);
mt76_rx_aggr_reorder(skb, &frames); mt76_rx_aggr_reorder(skb, &frames);
}
mt76_rx_complete(dev, &frames, q); mt76_rx_complete(dev, &frames, q);
} }
...@@ -121,11 +121,18 @@ struct mt76_queue_ops { ...@@ -121,11 +121,18 @@ struct mt76_queue_ops {
void (*kick)(struct mt76_dev *dev, struct mt76_queue *q); void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
}; };
enum mt76_wcid_flags {
MT_WCID_FLAG_CHECK_PS,
MT_WCID_FLAG_PS,
};
struct mt76_wcid { struct mt76_wcid {
struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS]; struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS];
struct work_struct aggr_work; struct work_struct aggr_work;
unsigned long flags;
u8 idx; u8 idx;
u8 hw_key_idx; u8 hw_key_idx;
...@@ -206,6 +213,9 @@ struct mt76_driver_ops { ...@@ -206,6 +213,9 @@ struct mt76_driver_ops {
struct sk_buff *skb); struct sk_buff *skb);
void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q); void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q);
void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta,
bool ps);
}; };
struct mt76_channel_state { struct mt76_channel_state {
......
...@@ -218,6 +218,8 @@ void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); ...@@ -218,6 +218,8 @@ void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q);
void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb); struct sk_buff *skb);
void mt76x2_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps);
void mt76x2_update_channel(struct mt76_dev *mdev); void mt76x2_update_channel(struct mt76_dev *mdev);
s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev, s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev,
......
...@@ -630,6 +630,7 @@ struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev) ...@@ -630,6 +630,7 @@ struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev)
.tx_complete_skb = mt76x2_tx_complete_skb, .tx_complete_skb = mt76x2_tx_complete_skb,
.rx_skb = mt76x2_queue_rx_skb, .rx_skb = mt76x2_queue_rx_skb,
.rx_poll_complete = mt76x2_rx_poll_complete, .rx_poll_complete = mt76x2_rx_poll_complete,
.sta_ps = mt76x2_sta_ps,
}; };
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct mt76x2_dev *dev; struct mt76x2_dev *dev;
......
...@@ -282,6 +282,9 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -282,6 +282,9 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
for (i = 0; i < ARRAY_SIZE(sta->txq); i++) for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
mt76x2_txq_init(dev, sta->txq[i]); mt76x2_txq_init(dev, sta->txq[i]);
if (vif->type == NL80211_IFTYPE_AP)
set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);
rcu_assign_pointer(dev->wcid[idx], &msta->wcid); rcu_assign_pointer(dev->wcid[idx], &msta->wcid);
out: out:
...@@ -311,23 +314,14 @@ mt76x2_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -311,23 +314,14 @@ mt76x2_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return 0; return 0;
} }
static void void
mt76x2_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76x2_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
{ {
struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv; struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv;
struct mt76x2_dev *dev = hw->priv; struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76);
int idx = msta->wcid.idx; int idx = msta->wcid.idx;
switch (cmd) { mt76x2_mac_wcid_set_drop(dev, idx, ps);
case STA_NOTIFY_SLEEP:
mt76x2_mac_wcid_set_drop(dev, idx, true);
mt76_stop_tx_queues(&dev->mt76, sta, true);
break;
case STA_NOTIFY_AWAKE:
mt76x2_mac_wcid_set_drop(dev, idx, false);
break;
}
} }
static int static int
...@@ -549,6 +543,12 @@ static void mt76x2_set_coverage_class(struct ieee80211_hw *hw, ...@@ -549,6 +543,12 @@ static void mt76x2_set_coverage_class(struct ieee80211_hw *hw,
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
} }
static int
mt76x2_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
{
return 0;
}
const struct ieee80211_ops mt76x2_ops = { const struct ieee80211_ops mt76x2_ops = {
.tx = mt76x2_tx, .tx = mt76x2_tx,
.start = mt76x2_start, .start = mt76x2_start,
...@@ -560,7 +560,6 @@ const struct ieee80211_ops mt76x2_ops = { ...@@ -560,7 +560,6 @@ const struct ieee80211_ops mt76x2_ops = {
.bss_info_changed = mt76x2_bss_info_changed, .bss_info_changed = mt76x2_bss_info_changed,
.sta_add = mt76x2_sta_add, .sta_add = mt76x2_sta_add,
.sta_remove = mt76x2_sta_remove, .sta_remove = mt76x2_sta_remove,
.sta_notify = mt76x2_sta_notify,
.set_key = mt76x2_set_key, .set_key = mt76x2_set_key,
.conf_tx = mt76x2_conf_tx, .conf_tx = mt76x2_conf_tx,
.sw_scan_start = mt76x2_sw_scan, .sw_scan_start = mt76x2_sw_scan,
...@@ -573,5 +572,6 @@ const struct ieee80211_ops mt76x2_ops = { ...@@ -573,5 +572,6 @@ const struct ieee80211_ops mt76x2_ops = {
.release_buffered_frames = mt76_release_buffered_frames, .release_buffered_frames = mt76_release_buffered_frames,
.set_coverage_class = mt76x2_set_coverage_class, .set_coverage_class = mt76x2_set_coverage_class,
.get_survey = mt76_get_survey, .get_survey = mt76_get_survey,
.set_tim = mt76x2_set_tim,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment