summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2026-08-01 14:53:27 +0000
committerFelix Fietkau <nbd@nbd.name>2026-08-01 15:22:41 +0000
commit2d16caccd278bdad4299a7093104f29ac73ebed2 (patch)
treee7c37e75eeb329169243e0c01ad9693af5482d32
parentdc930cf883c81799f11f4da33a3711cad983de24 (diff)
wifi: mt76: mt7996: handle UNI PS sync events
Enable MT_DRV_HW_PS_BUFFERING and handle MCU_UNI_EVENT_PS_SYNC with all three TLV formats (single client, multi-client packed entries, and bitmap). Link: https://patch.msgid.link/20260801145334.1166751-3-nbd@nbd.name Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c86
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h33
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mmio.c3
3 files changed, 121 insertions, 1 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 8aab91810135..cd7587ce5b8a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -827,6 +827,89 @@ mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
}
static void
+mt7996_mcu_ps_transition(struct mt7996_dev *dev, u16 wcid_idx, bool ps)
+{
+ struct mt76_wcid *wcid;
+
+ wcid = mt76_wcid_ptr(dev, wcid_idx);
+ if (!wcid || !wcid_to_sta(wcid))
+ return;
+
+ mt76_sta_ps_transition(&dev->mt76, wcid, ps);
+}
+
+static void
+mt7996_mcu_rx_ps_sync(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt7996_mcu_ps_sync_event *event = (void *)skb->data;
+ struct tlv *tlv;
+ int len;
+
+ if (skb->len < sizeof(*event))
+ return;
+
+ skb_pull(skb, sizeof(*event));
+
+ len = skb->len;
+ while (len >= sizeof(*tlv)) {
+ u16 tag, tag_len;
+
+ tlv = (struct tlv *)skb->data;
+ tag = le16_to_cpu(tlv->tag);
+ tag_len = le16_to_cpu(tlv->len);
+ /* a tag_len below the header size would not advance the buffer */
+ if (tag_len < sizeof(*tlv) || tag_len > len)
+ break;
+
+ switch (tag) {
+ case UNI_PS_CLIENT_INFO: {
+ struct mt7996_mcu_ps_client_info *info = (void *)tlv;
+
+ if (tag_len < sizeof(*info))
+ break;
+
+ mt7996_mcu_ps_transition(dev,
+ le16_to_cpu(info->wlan_idx),
+ info->ps_bit);
+ break;
+ }
+ case UNI_PS_MULTI_CLIENT_INFO: {
+ struct mt7996_mcu_ps_multi_client_info *info = (void *)tlv;
+ u16 cnt;
+ int i;
+
+ if (tag_len < sizeof(*info))
+ break;
+
+ cnt = min_t(u16, le16_to_cpu(info->sta_cnt),
+ (tag_len - sizeof(*info)) / sizeof(info->sta_ps_info[0]));
+ for (i = 0; i < cnt; i++) {
+ u16 entry = le16_to_cpu(info->sta_ps_info[i]);
+
+ mt7996_mcu_ps_transition(dev,
+ FIELD_GET(MT7996_PS_MULTI_WCID, entry),
+ !!(entry & MT7996_PS_MULTI_PS_BIT));
+ }
+ break;
+ }
+ case UNI_PS_MULTI_CLIENT_INFO_BITMAP: {
+ u8 *bitmap = tlv->data;
+ int bitmap_len = tag_len - sizeof(*tlv);
+ int i;
+
+ for (i = 0; i < bitmap_len * 8; i++)
+ mt7996_mcu_ps_transition(dev, i,
+ !!(bitmap[i / 8] & BIT(i % 8)));
+ break;
+ }
+ }
+
+ skb_pull(skb, tag_len);
+ len -= tag_len;
+ }
+}
+
+static void
mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
{
struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
@@ -847,6 +930,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
case MCU_UNI_EVENT_WED_RRO:
mt7996_mcu_wed_rro_event(dev, skb);
break;
+ case MCU_UNI_EVENT_PS_SYNC:
+ mt7996_mcu_rx_ps_sync(dev, skb);
+ break;
default:
break;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index 1487d33a8dbb..74b70fb6da3d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -293,6 +293,39 @@ enum {
UNI_WED_RRO_BA_SESSION_DELETE,
};
+struct mt7996_mcu_ps_sync_event {
+ struct mt7996_mcu_rxd rxd;
+
+ u8 bss_idx;
+ u8 __rsv[3];
+} __packed;
+
+struct mt7996_mcu_ps_client_info {
+ __le16 tag;
+ __le16 len;
+ u8 ps_bit;
+ u8 __rsv;
+ __le16 wlan_idx;
+ u8 buffer_size;
+ u8 __rsv2[3];
+} __packed;
+
+struct mt7996_mcu_ps_multi_client_info {
+ __le16 tag;
+ __le16 len;
+ __le16 sta_cnt;
+ __le16 sta_ps_info[];
+} __packed;
+
+#define MT7996_PS_MULTI_WCID GENMASK(10, 0)
+#define MT7996_PS_MULTI_PS_BIT BIT(15)
+
+enum {
+ UNI_PS_CLIENT_INFO = 0,
+ UNI_PS_MULTI_CLIENT_INFO = 1,
+ UNI_PS_MULTI_CLIENT_INFO_BITMAP = 2,
+};
+
struct mt7996_mcu_thermal_notify {
struct mt7996_mcu_rxd rxd;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
index ac81be5fe023..fbfbb96a742f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
@@ -838,7 +838,8 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
.link_data_size = sizeof(struct mt7996_vif_link),
.drv_flags = MT_DRV_TXWI_NO_FREE |
MT_DRV_AMSDU_OFFLOAD |
- MT_DRV_HW_MGMT_TXQ,
+ MT_DRV_HW_MGMT_TXQ |
+ MT_DRV_HW_PS_BUFFERING,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,