From bdd0c9db95dad39710091d065921abbb9bd2e541 Mon Sep 17 00:00:00 2001
From: "sujuan.chen" <sujuan.chen@mediatek.com>
Date: Thu, 18 May 2023 15:01:47 +0800
Subject: [PATCH 2009/2010] wifi: mt76: mt7996: reset addr_elem when delete ba

The old addr element info may be used when the signature is not equel to
0xff, and sta will find error SDP cause the SDP/SDL=0 issue.

Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
---
 mt76.h            |   1 +
 mt76_connac_mcu.h |   1 +
 mt7996/init.c     |   3 ++
 mt7996/mac.c      | 122 ++++++++++++++++++++++++++++++++++++++++++++++
 mt7996/main.c     |   7 +++
 mt7996/mcu.c      |  66 ++++++++++++++++++++++++-
 mt7996/mcu.h      |  34 +++++++++++++
 mt7996/mt7996.h   |  32 ++++++++++++
 mt7996/regs.h     |   5 ++
 9 files changed, 270 insertions(+), 1 deletion(-)

diff --git a/mt76.h b/mt76.h
index e584469..2aa152b 100644
--- a/mt76.h
+++ b/mt76.h
@@ -467,6 +467,7 @@ struct mt76_rx_tid {
 	u16 nframes;
 
 	u8 num;
+	u8 partial_id;
 
 	u8 started:1, stopped:1, timer_pending:1;
 
diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
index a53fa13..d74fd2d 100644
--- a/mt76_connac_mcu.h
+++ b/mt76_connac_mcu.h
@@ -1023,6 +1023,7 @@ enum {
 	MCU_UNI_EVENT_THERMAL = 0x35,
 	MCU_UNI_EVENT_BF = 0x33,
 	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
+	MCU_UNI_EVENT_RRO = 0x57,
 };
 
 #define MCU_UNI_CMD_EVENT			BIT(1)
diff --git a/mt7996/init.c b/mt7996/init.c
index 7233e41..8aeff59 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -765,6 +765,9 @@ static int mt7996_rro_init(struct mt7996_dev *dev)
 	mt76_wr(dev, MT_RRO_HOST_INT_ENA,
 		MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA);
 
+	INIT_DELAYED_WORK(&dev->rro.rro_del_work, mt7996_rro_delete_sessions);
+	INIT_LIST_HEAD(&dev->rro.rro_poll_list);
+
 	/* rro ind cmd queue init */
 	return mt7996_dma_rro_init(dev);
 }
diff --git a/mt7996/mac.c b/mt7996/mac.c
index 3674411..4904e88 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -1969,6 +1969,121 @@ mt7996_rro_get_addr_elem(struct mt7996_dev *dev, u16 seid, u16 sn)
 	return addr + idx * sizeof(struct mt7996_rro_addr);
 }
 
+static bool mt7996_rro_reset_sessions(struct mt7996_dev *dev,
+				  u16 wcid, u8 partial_id)
+{
+	u32 sid = ((wcid & 0x7F) << 3) + partial_id;
+	u32 value[2];
+	struct mt7996_rro_ba_session *s;
+	struct  mt7996_rro_addr *elem;
+	int i;
+
+	mt76_wr(dev, MT_RRO_DBG_RD_CTRL, MT_RRO_DBG_RD_EXEC |
+		sid >> 1 | 0x200);
+
+	if (sid & 0x1) {
+		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
+		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(2));
+	} else {
+		value[0] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(0));
+		value[1] = mt76_rr(dev, MT_RRO_DBG_RDAT_DW(1));
+	}
+
+	s = (struct mt7996_rro_ba_session *)&value[0];
+	if (!s->cn && s->ack_sn == s->last_in_sn) {
+		for (i = 0; i < MT7996_RRO_WIN_SIZE_MAX; i++) {
+			elem = mt7996_rro_get_addr_elem(dev, sid, i);
+			elem->signature = 0xff;
+		}
+		return true;
+	}
+
+	return false;
+}
+
+void  mt7996_rro_delete_sessions(struct work_struct *work)
+{
+	struct mt7996_dev *dev;
+	struct mt7996_rro_ba_session_elem *e;
+	int elem_nums;
+	LIST_HEAD(rro_poll_list);
+
+	dev = (struct mt7996_dev *)container_of(work, struct mt7996_dev,
+					       rro.rro_del_work.work);
+	elem_nums = dev->rro.elem_nums;
+
+	spin_lock_bh(&dev->rro.rro_stbl_lock);
+	list_splice_init(&dev->rro.rro_poll_list, &rro_poll_list);
+	spin_unlock_bh(&dev->rro.rro_stbl_lock);
+
+	do {
+		spin_lock_bh(&dev->rro.rro_stbl_lock);
+		if (list_empty(&rro_poll_list)) {
+			spin_unlock_bh(&dev->rro.rro_stbl_lock);
+			break;
+		}
+
+		e = list_first_entry(&rro_poll_list,
+				     struct mt7996_rro_ba_session_elem,
+				     poll_list);
+		if (!e) {
+			spin_unlock_bh(&dev->rro.rro_stbl_lock);
+			break;
+		}
+		list_del_init(&e->poll_list);
+		spin_unlock_bh(&dev->rro.rro_stbl_lock);
+
+		if (mt7996_rro_reset_sessions(dev, e->wlan_idx,
+					      e->partial_id)) {
+			mt7996_mcu_reset_rro_sessions(dev, e->wlan_idx,
+						      e->tid, e->partial_id);
+			kfree(e);
+			dev->rro.elem_nums--;
+		} else {
+			spin_lock_bh(&dev->rro.rro_stbl_lock);
+			list_add_tail(&e->poll_list, &dev->rro.rro_poll_list);
+			spin_unlock_bh(&dev->rro.rro_stbl_lock);
+		}
+		elem_nums--;
+	} while (elem_nums);
+
+	if (list_empty(&rro_poll_list))
+		ieee80211_queue_delayed_work(mt76_hw(dev),
+					     &dev->rro.rro_del_work,
+					     MT7996_WATCHDOG_TIME);
+}
+
+int mt7996_rro_add_delete_elem(struct mt7996_dev *dev,
+			       struct mt7996_sta *msta, u8 tidno)
+{
+	struct mt76_rx_tid *tid = NULL;
+	struct mt76_wcid *wcid = &msta->wcid;
+	struct mt7996_rro_ba_session_elem *e;
+	u16 idx = msta->wcid.idx;
+
+	tid = rcu_dereference(wcid->aggr[tidno]);
+	if (!tid)
+		return 0;
+
+	e = kzalloc(sizeof(*e), GFP_ATOMIC);
+	if (!e)
+		return -ENOMEM;
+
+	e->wlan_idx = idx;
+	e->tid = tidno;
+	e->partial_id = tid->partial_id;
+
+	spin_lock_bh(&dev->rro.rro_stbl_lock);
+	list_add_tail(&e->poll_list, &dev->rro.rro_poll_list);
+	spin_unlock_bh(&dev->rro.rro_stbl_lock);
+	dev->rro.elem_nums++;
+
+	ieee80211_queue_delayed_work(mt76_hw(dev),
+				     &dev->rro.rro_del_work,
+				     MT7996_WATCHDOG_TIME);
+	return 0;
+}
+
 void mt7996_rx_token_put(struct mt7996_dev *dev)
 {
 	struct mt76_queue *q;
@@ -2504,6 +2619,9 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
 	if (phy3)
 		ieee80211_stop_queues(phy3->mt76->hw);
 
+	if (dev->rro_support)
+		cancel_delayed_work_sync(&dev->rro.rro_del_work);
+
 	cancel_delayed_work_sync(&dev->mphy.mac_work);
 	if (phy2)
 		cancel_delayed_work_sync(&phy2->mt76->mac_work);
@@ -2595,6 +2713,10 @@ void mt7996_mac_reset_work(struct work_struct *work)
 	set_bit(MT76_RESET, &dev->mphy.state);
 	set_bit(MT76_MCU_RESET, &dev->mphy.state);
 	wake_up(&dev->mt76.mcu.wait);
+
+	if (dev->rro_support)
+		cancel_delayed_work_sync(&dev->rro.rro_del_work);
+
 	cancel_delayed_work_sync(&dev->mphy.mac_work);
 	if (phy2) {
 		set_bit(MT76_RESET, &phy2->mt76->state);
diff --git a/mt7996/main.c b/mt7996/main.c
index fdd73d3..70e30e4 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -119,6 +119,9 @@ static void mt7996_stop(struct ieee80211_hw *hw)
 	struct mt7996_dev *dev = mt7996_hw_dev(hw);
 	struct mt7996_phy *phy = mt7996_hw_phy(hw);
 
+	if (dev->rro_support)
+		cancel_delayed_work_sync(&dev->rro.rro_del_work);
+
 	cancel_delayed_work_sync(&phy->mt76->mac_work);
 	cancel_delayed_work_sync(&dev->scs_work);
 
@@ -797,6 +800,10 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		ret = mt7996_mcu_add_rx_ba(dev, params, true);
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
+		if (dev->rro_support)  {
+			ret = mt7996_rro_add_delete_elem(dev, msta,
+							 params->tid);
+		}
 		mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
 		ret = mt7996_mcu_add_rx_ba(dev, params, false);
 		break;
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 4c7cc09..abb69a5 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -476,6 +476,41 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
 	phy->throttle_state = n->duty_percent;
 }
 
+static void mt7996_mcu_rx_rro(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+	struct mt7996_mcu_rro_event *event;
+
+	if (!dev->rro_support)
+		return;
+
+	event = (struct mt7996_mcu_rro_event*)skb->data;
+
+	switch (event->tag) {
+	case UNI_RRO_BA_SESSION_STATUS: {
+		struct mt7996_mcu_rro_ba *rro = (struct mt7996_mcu_rro_ba *)skb->data;
+		u16 idx = rro->wlan_id;
+		struct mt76_rx_tid *tid;
+		struct mt76_wcid *wcid;
+
+		wcid = rcu_dereference(dev->mt76.wcid[idx]);
+		if (!wcid || !wcid->sta)
+			return;
+
+		tid = rcu_dereference(wcid->aggr[rro->tid]);
+		if (!tid)
+			return;
+		tid->partial_id = rro->partial_id;
+
+		break;
+	}
+	default:
+		dev_info(dev->mt76.dev, "%s: unknown rro event tag %d\n",
+			 __func__, event->tag);
+		break;
+	}
+
+}
+
 static void
 mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
 {
@@ -523,7 +558,10 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
 	case MCU_UNI_EVENT_THERMAL:
 		mt7996_mcu_rx_thermal_notify(dev, skb);
 		break;
-#ifdef CONFIG_MTK_DEBUG
+	case MCU_UNI_EVENT_RRO:
+		mt7996_mcu_rx_rro(dev, skb);
+		break;
+#if defined CONFIG_NL80211_TESTMODE || defined CONFIG_MTK_DEBUG
 	case MCU_UNI_EVENT_BF:
 		mt7996_mcu_rx_bf_event(dev, skb);
 		break;
@@ -4530,6 +4568,32 @@ int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val)
 				 sizeof(req), true);
 }
 
+int mt7996_mcu_reset_rro_sessions(struct mt7996_dev *dev,
+					 u16 wcid, u8 tid, u8 pid)
+{
+	struct {
+		/* fixed field */
+		u8 __rsv[4];
+
+		__le16 tag;
+		__le16 len;
+		u16 wcid;
+		u8 tid;
+		u8 partial_id;
+		u8 pad[4];
+	} __packed req = {
+		.tag = cpu_to_le16(UNI_RRO_DEL_BA_SESSION),
+		.len = cpu_to_le16(sizeof(req) - 4),
+		.wcid = wcid,
+		.tid = tid,
+		.partial_id = pid,
+	};
+
+	return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(RRO),
+				 &req, sizeof(req), true);
+}
+
+
 int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
 {
 	struct mt7996_dev *dev = phy->dev;
diff --git a/mt7996/mcu.h b/mt7996/mcu.h
index ec074bc..10e3799 100644
--- a/mt7996/mcu.h
+++ b/mt7996/mcu.h
@@ -191,6 +191,38 @@ struct mt7996_mcu_thermal_notify {
 	u8 __rsv2[4];
 } __packed;
 
+struct mt7996_mcu_rro_event {
+	struct mt7996_mcu_rxd rxd;
+
+	u8 __rsv1[4];
+
+	__le16 tag;
+	__le16 len;
+} __packed;
+
+struct mt7996_mcu_rro_ba {
+	struct mt7996_mcu_rro_event event;
+
+	u16 wlan_id;
+	u8 tid;
+	u8 partial_id;
+	__le32 status;
+}__packed;
+
+enum  {
+	UNI_RRO_BA_SESSION_STATUS = 0,
+	UNI_RRO_BA_SESSION_TBL	= 1,
+	UNI_RRO_BA_SESSION_MAX_NUM
+};
+
+struct mt7996_mcu_rro_del_ba {
+	struct mt7996_mcu_rro_event event;
+
+	u8  wlan_idx;
+	u8  tid;
+	u8 __rsv2[2];
+};
+
 enum mt7996_chan_mib_offs {
 	UNI_MIB_OBSS_AIRTIME = 26,
 	UNI_MIB_NON_WIFI_TIME = 27,
@@ -718,6 +750,8 @@ enum {
 	UNI_RRO_GET_BA_SESSION_TABLE,
 	UNI_RRO_SET_BYPASS_MODE,
 	UNI_RRO_SET_TXFREE_PATH,
+	UNI_RRO_DEL_BA_SESSION,
+	UNI_RRO_SET_FLUSH_TIMEOUT
 };
 
 enum{
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index 3463eb9..fcf8d04 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -340,6 +340,28 @@ struct mt7996_rro_hif {
        u32 rsv5;
 };
 
+struct mt7996_rro_ba_session {
+	u32 ack_sn         :12;
+	u32 win_sz         :3;
+	u32 bn             :1;
+	u32 last_in_sn     :12;
+	u32 bc             :1;
+	u32 bd             :1;
+	u32 sat            :1;
+	u32 cn             :1;
+	u32 within_cnt     :12;
+	u32 to_sel         :3;
+	u32 rsv            :1;
+	u32 last_in_rxtime :12;
+};
+
+struct mt7996_rro_ba_session_elem {
+	struct list_head poll_list;
+	u16 wlan_idx;
+	u8 tid;
+	u8 partial_id;
+};
+
 struct mt7996_msdu_pg {
 	struct mt7996_rro_hif rxd[MT7996_MAX_HIF_RXD_IN_PG];
 	u32 next_pg_l;
@@ -369,6 +391,11 @@ struct mt7996_rro_cfg {
 	spinlock_t lock;
 	struct list_head pg_addr_cache;
 	struct list_head pg_hash_head[MT7996_RRO_MSDU_PG_HASH_SIZE];
+
+	struct delayed_work rro_del_work;
+	spinlock_t rro_stbl_lock;
+	struct list_head rro_poll_list;
+	u16 elem_nums;
 };
 
 struct mt7996_phy {
@@ -716,6 +743,8 @@ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
 int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set);
 int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
 int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val);
+int mt7996_mcu_reset_rro_sessions(struct mt7996_dev *dev,
+				     u16 wcid, u8 tid, u8 pid);
 int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
 int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
 int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
@@ -814,6 +843,9 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
 			  struct mt76_tx_info *tx_info);
 void mt7996_tx_token_put(struct mt7996_dev *dev);
 int mt7996_dma_rro_init(struct mt7996_dev *dev);
+void  mt7996_rro_delete_sessions(struct work_struct *work);
+int mt7996_rro_add_delete_elem(struct mt7996_dev *dev,
+			       struct mt7996_sta *msta, u8 tid);
 void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
 			 struct sk_buff *skb, u32 *info);
 void mt7996_rx_token_put(struct mt7996_dev *dev);
diff --git a/mt7996/regs.h b/mt7996/regs.h
index 20db058..312d570 100644
--- a/mt7996/regs.h
+++ b/mt7996/regs.h
@@ -72,6 +72,11 @@ enum base_rev {
 #define MT_RRO_ACK_SN_CTRL_SN_MASK		GENMASK(27, 16)
 #define MT_RRO_ACK_SN_CTRL_SESSION_MASK		GENMASK(11, 0)
 
+#define MT_RRO_DBG_RD_CTRL			MT_RRO_TOP(0xe0)
+#define MT_RRO_DBG_RD_ADDR			GENMASK(15, 0)
+#define MT_RRO_DBG_RD_EXEC			BIT(31)
+
+#define MT_RRO_DBG_RDAT_DW(_n)			MT_RRO_TOP(0xf0 + _n * 0x4)
 
 #define MT_MCU_INT_EVENT			0x2108
 #define MT_MCU_INT_EVENT_DMA_STOPPED		BIT(0)
-- 
2.18.0

