From: Peter Chiu Because 4 address non-AMSDU packets do not have a bssid field, the hardware cannot get the bssid. Without the bssid, stations are not able to leave PS mode due to HW design. Wake up non-setup links when 4-address mode is established to prevent this issue. mt7992 and mt7990 handle this via the BSSID mapping band config instead, so restrict the command to mt7996. Signed-off-by: Peter Chiu Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + .../net/wireless/mediatek/mt76/mt7996/main.c | 8 ++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.c | 18 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 6 ++++++ .../net/wireless/mediatek/mt76/mt7996/mt7996.h | 2 ++ 5 files changed, 35 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 8198efc6c05d..51380849d24e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -867,6 +867,7 @@ enum { STA_REC_HDRT = 0x28, STA_REC_EML_OP = 0x29, STA_REC_HDR_TRANS = 0x2B, + STA_REC_PS_LEAVE = 0x45, STA_REC_MAX_NUM }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 57afe4e81666..b6717d4e4dda 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -2067,6 +2067,14 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, continue; mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + + /* HW cannot derive the BSSID from 4-address non-AMSDU frames, + * so PS exit is missed on links other than the setup link. + * Let the firmware wake those links instead. + */ + if (enabled && msta->deflink_id != link_id && + is_mt7996(&dev->mt76)) + mt7996_mcu_ps_leave(dev, link, msta_link); } mutex_unlock(&dev->mt76.mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 69676a605549..57117ce69cbb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -5184,6 +5184,24 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } +int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_add_tlv(skb, STA_REC_PS_LEAVE, + sizeof(struct sta_rec_ps_leave)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx, u16 rate_idx, bool beacon) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index b53a9e71c281..1487d33a8dbb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -680,6 +680,12 @@ struct sta_rec_hdr_trans { u8 mesh; } __packed; +struct sta_rec_ps_leave { + __le16 tag; + __le16 len; + u8 __rsv[4]; +} __packed; + struct sta_rec_mld_setup { __le16 tag; __le16 len; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index dcacc7d06e85..2a0cdb56f822 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -928,6 +928,8 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link); +int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode); int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev, struct ieee80211_vif *vif, -- 2.53.0