[rdkb][common][bsp][Refactor and sync wifi from openwrt]

[Description]
ced8594f [MAC80211][WiFi6][Misc][Fix the MT76 WiFi6 Makefile]
7221999e [MAC80211][WiFi7][Misc][Correct the MAC80211 WiFi7 Makefile.]
9d87794a [MAC80211][WiFi7][Misc][Correct the MT76 WiFi7 Makefile.]
ff24e1b2 [openwrt-24][Mac80211][Fix patch conflict with upstream openwrt]
3a6c13e2 [mac80211][misc][fix patch fail due to openwrt update]
05763faa [MAC80211][WiFi7][misc][fix patch failed of wifi-scripts]
f34fd014 [mac80211][misc][fix patch fail due to openwrt update]
f6796660 [openwrt-24][Release][Fix build fail of Wi-Fi7 MT76]
7076d96c [MAC80211][WiFi7][Misc][Fix release build fail because of mt76 version upgradation]
1f748b17 [mac80211][misc][fix patch fail due to openwrt update]
95ba6722 [mac80211][misc][fix patch fail due to openwrt update]
17680d7f [MAC80211][WiFi7][misc][Rename eeprom of eFEM variants]
b97cefa1 [MAC80211][WiFi7][app][Add Griffin support for atenl/iwpriv]
6de718a4 [MAC80211][WiFi7][misc][fix wifi-scripts patch failed]
9f1ace86 [MAC80211][WiFi7][misc][fix hostapd Makefile patch]
e4d0d28e [MAC80211][Misc][Add MT7990 Firmware OpenWrt config]
f3a8a8f7 [MAC80211][Release][Fix build fail of Wi-Fi6 MT76]
dabe8eae [openwrt-24][common][bsp][Fix line ending]
6d438a9d [openwrt-24][common][bsp][Use zstd to compress rootfs debug symbols for unified autobuild]
c268e47e [openwrt][common][bsp][Change SMC ID of wdt nonrst reg of reset-boot-count to 0x570]
c6819fbc [openwrt-24][Release][Update release note for Filogic 880 alpha release]
6897b4de [openwrt-24][common][bsp][Adjust unified autobuild for internal build detection]
fb9b9762 [MAC80211][WiFi6/7][app][Add ext eeprom write back cmd support]
d42b42a3 [openwrt-24][common][bsp][Add kernel6.6 Filogic880 BE19000/BE14000]
3806f047 [MAC80211][misc][Add Bpi-R4 support]
ddbda753 [MAC80211][WiFi7][Misc][Fix build fail because of mt76 version upgradation]
90959b08 [MAC80211][WiFi6][mt76][Rebase mt76 pathes]
728a3362 [MAC80211][WiFi6][mt76][Refactor Qos Map]
b46277b5 [MAC80211][WiFi6][mt76][Fix add ba issue on tid not equal to zero]
c084ee8b [MAC80211][WiFi7][mt76][split mt76 Makefile patch]
bbaec094 [MAC80211][Release][Update Filogic 830/820/630 firmware]
5ce2eece [MAC80211][wifi6][MT76][Fix build fail]
5ac1121f [MAC80211][wifi6][MT76][Fix mt76 version to 2024-07-13]
485f92b1 [MAC80211][WiFi7][misc][synchronize PP bitmap when association]
84db8818 [MAC80211][WiFi6/7][app][Add ATETXNSS in iwpriv wrapper]
cc5a4605 [MAC80211][WiFi7][mt76][fix patch failed of Makefile]

[Release-log]

Change-Id: I06704c04c4b5571af4ffd189d636c1fc9f0567fd
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-mt76-mt7996-Add-connac3-csi-feature.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-mt76-mt7996-Add-connac3-csi-feature.patch
new file mode 100644
index 0000000..e7146ca
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0106-mtk-mt76-mt7996-Add-connac3-csi-feature.patch
@@ -0,0 +1,1134 @@
+From c16fd657d9955992ab12c22ac068c7f9d0035ed3 Mon Sep 17 00:00:00 2001
+From: mtk20656 <chank.chen@mediatek.com>
+Date: Sat, 20 Jan 2024 12:03:24 +0800
+Subject: [PATCH 106/223] mtk: mt76: mt7996: Add connac3 csi feature.
+
+1. format align to wifi6.
+2. add bw320 support.
+3. add active mode.
+
+Fix csi bug with single wiphy design.
+
+Change-Id: If37ac6de4781c3673671707ee3ee243dda8163f8
+Change-Id: I7761e2883bb08939917432df075062fb78de3bee
+Signed-off-by: mtk20656 <chank.chen@mediatek.com>
+---
+ mt76_connac_mcu.h |   2 +
+ mt7996/init.c     |  22 +++
+ mt7996/main.c     |   3 +
+ mt7996/mcu.c      | 465 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h      | 105 +++++++++++
+ mt7996/mt7996.h   |  55 ++++++
+ mt7996/vendor.c   | 247 ++++++++++++++++++++++++
+ mt7996/vendor.h   |  52 ++++++
+ 8 files changed, 951 insertions(+)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index f6b472ef..11c147cd 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1066,6 +1066,7 @@ enum {
+ 	MCU_UNI_EVENT_THERMAL = 0x35,
+ 	MCU_UNI_EVENT_NIC_CAPAB = 0x43,
+ 	MCU_UNI_EVENT_TESTMODE_CTRL = 0x46,
++	MCU_UNI_EVENT_CSI_REPORT = 0x4A,
+ 	MCU_UNI_EVENT_WED_RRO = 0x57,
+ 	MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ 	MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
+@@ -1303,6 +1304,7 @@ enum {
+ 	MCU_UNI_CMD_TESTMODE_TRX_PARAM = 0x42,
+ 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+ 	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
++	MCU_UNI_CMD_CSI_CTRL = 0x4A,
+ 	MCU_UNI_CMD_THERMAL_CAL = 0x4c,
+ 	MCU_UNI_CMD_RRO = 0x57,
+ 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
+diff --git a/mt7996/init.c b/mt7996/init.c
+index f26687df..211ad40c 100644
+--- a/mt7996/init.c
++++ b/mt7996/init.c
+@@ -807,6 +807,24 @@ error:
+ 	return ret;
+ }
+ 
++#ifdef CONFIG_MTK_VENDOR
++static int mt7996_unregister_csi(struct mt7996_phy *phy)
++{
++	struct csi_data *c, *tmp_c;
++
++	spin_lock_bh(&phy->csi.lock);
++	phy->csi.enable = 0;
++
++	list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
++		list_del(&c->node);
++		kfree(c);
++	}
++	spin_unlock_bh(&phy->csi.lock);
++
++	return 0;
++}
++#endif
++
+ static void
+ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ {
+@@ -818,6 +836,10 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+ 	/* TODO: FIXME: temp for single wiphy support */
+ 	phy->mt76->hw = phy->mt76->ori_hw;
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_unregister_csi(phy);
++#endif
++
+ 	mt7996_unregister_thermal(phy);
+ 
+ 	mphy = phy->dev->mt76.phys[band];
+diff --git a/mt7996/main.c b/mt7996/main.c
+index b7096901..32a37936 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -1306,6 +1306,9 @@ void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ 	unsigned long rem = sta->valid_links ?: BIT(0);
+ 
++#ifdef CONFIG_MTK_VENDOR
++	mt7996_mcu_set_csi(&dev->phy, 2, 8, 1, 0, sta->addr);
++#endif
+ 	mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ }
+ 
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 8034c8ab..56bb32a0 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -658,6 +658,263 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	}
+ }
+ 
++static int
++csi_integrate_segment_data(struct mt7996_phy *phy, struct csi_data *csi)
++{
++	struct csi_data *csi_temp = NULL;
++
++	if (csi->segment_num == 0 && csi->remain_last == 0)
++		return CSI_CHAIN_COMPLETE;
++	else if (csi->segment_num == 0 && csi->remain_last == 1) {
++		memcpy(&phy->csi.buffered_csi,
++		       csi, sizeof(struct csi_data));
++
++		return CSI_CHAIN_SEGMENT_FIRST;
++	} else if (csi->segment_num != 0) {
++		csi_temp = &phy->csi.buffered_csi;
++		if (csi->chain_info != csi_temp->chain_info ||
++		csi->segment_num != (csi_temp->segment_num + 1))
++			return CSI_CHAIN_SEGMENT_ERR;
++
++		memcpy(&csi_temp->data_i[csi_temp->data_num],
++		       csi->data_i, csi->data_num * sizeof(s16));
++
++		memcpy(&csi_temp->data_q[csi_temp->data_num],
++		       csi->data_q, csi->data_num * sizeof(s16));
++
++		csi_temp->data_num += csi->data_num;
++		csi_temp->segment_num = csi->segment_num;
++		csi_temp->remain_last = csi->remain_last;
++
++		if (csi->remain_last == 0)
++			return CSI_CHAIN_SEGMENT_LAST;
++		else if (csi->remain_last == 1)
++			return CSI_CHAIN_SEGMENT_MIDDLE;
++	}
++
++	return CSI_CHAIN_ERR;
++}
++
++static int
++mt7996_mcu_csi_report_data(struct mt7996_phy *phy, u8 *tlv_buf, u32 len)
++{
++	int ret, i;
++	struct csi_data *current_csi;
++	struct csi_data *target_csi;
++	struct csi_tlv *tlv_data;
++	u8 *buf_tmp;
++	u32 rx_info, tx_rx_idx;
++	u32 buf_len_last, offset;
++
++	buf_tmp = tlv_buf;
++	buf_len_last = len;
++	offset = sizeof(((struct csi_tlv *)0)->basic);
++
++	current_csi = kzalloc(sizeof(*current_csi), GFP_KERNEL);
++	if (!current_csi)
++		return -ENOMEM;
++
++	while (buf_len_last >= offset) {
++		u32 tag, len;
++		s16 *data_tmp = NULL;
++
++		tlv_data = (struct csi_tlv *)buf_tmp;
++		tag = le32_to_cpu(tlv_data->basic.tag);
++		len = le32_to_cpu(tlv_data->basic.len);
++
++		switch (tag) {
++		case CSI_EVENT_FW_VER:
++			current_csi->fw_ver = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_CBW:
++			current_csi->ch_bw = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_RSSI:
++			current_csi->rssi = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_SNR:
++			current_csi->snr = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_BAND:
++			current_csi->band = le32_to_cpu(tlv_data->info);
++
++			if (current_csi->band != phy->mt76->band_idx) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			break;
++		case CSI_EVENT_CSI_NUM:
++			current_csi->data_num = le32_to_cpu(tlv_data->info);
++
++			if (current_csi->data_num > CSI_BW80_DATA_COUNT) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			break;
++		case CSI_EVENT_CSI_I_DATA:
++			if (len != sizeof(s16) * current_csi->data_num) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			data_tmp = tlv_data->data;
++			for (i = 0; i < current_csi->data_num; i++)
++				current_csi->data_i[i] = le16_to_cpu(*(data_tmp + i));
++			break;
++		case CSI_EVENT_CSI_Q_DATA:
++			if (len != sizeof(s16) * current_csi->data_num) {
++				kfree(current_csi);
++				return -EINVAL;
++			}
++
++			data_tmp = tlv_data->data;
++			for (i = 0; i < current_csi->data_num; i++)
++				current_csi->data_q[i] = le16_to_cpu(*(data_tmp + i));
++			break;
++		case CSI_EVENT_DBW:
++			current_csi->data_bw = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_CH_IDX:
++			current_csi->pri_ch_idx = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_TA:
++			memcpy(current_csi->ta, tlv_data->mac, ETH_ALEN);
++			break;
++		case CSI_EVENT_EXTRA_INFO:
++			current_csi->ext_info = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_RX_MODE:
++			rx_info = le32_to_cpu(tlv_data->info);
++			current_csi->rx_mode = u32_get_bits(rx_info, GENMASK(15, 0));
++			current_csi->rx_rate = u32_get_bits(rx_info, GENMASK(31, 16));
++			break;
++		case CSI_EVENT_H_IDX:
++			current_csi->chain_info = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_TX_RX_IDX:
++			tx_rx_idx = le32_to_cpu(tlv_data->info);
++			current_csi->tx_idx = u32_get_bits(tx_rx_idx, GENMASK(31, 16));
++			current_csi->rx_idx = u32_get_bits(tx_rx_idx, GENMASK(15, 0));
++			break;
++		case CSI_EVENT_TS:
++			current_csi->ts = le32_to_cpu(tlv_data->info);
++
++			if (phy->csi.interval &&
++				current_csi->ts < phy->csi.last_record + phy->csi.interval) {
++				kfree(current_csi);
++				return 0;
++			}
++
++			break;
++		case CSI_EVENT_PKT_SN:
++			current_csi->pkt_sn = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_BW_SEG:
++			current_csi->segment_num = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_REMAIN_LAST:
++			current_csi->remain_last = le32_to_cpu(tlv_data->info);
++			break;
++		case CSI_EVENT_TR_STREAM:
++			current_csi->tr_stream = le32_to_cpu(tlv_data->info);
++			break;
++		default:
++			break;
++		};
++
++		buf_len_last -= (offset + len);
++
++		if (buf_len_last >= offset)
++			buf_tmp += (offset + len);
++	}
++
++	/* integret the bw80 segment */
++	if (current_csi->ch_bw >= CSI_BW80) {
++		ret = csi_integrate_segment_data(phy, current_csi);
++
++		switch (ret) {
++		case CSI_CHAIN_ERR:
++		case CSI_CHAIN_SEGMENT_ERR:
++			kfree(current_csi);
++			return -EINVAL;
++			break;
++		case CSI_CHAIN_SEGMENT_FIRST:
++		case CSI_CHAIN_SEGMENT_MIDDLE:
++			kfree(current_csi);
++			return 0;
++			break;
++		case CSI_CHAIN_COMPLETE:
++			target_csi = current_csi;
++			break;
++		case CSI_CHAIN_SEGMENT_LAST:
++			target_csi = current_csi;
++			memcpy(target_csi, &phy->csi.buffered_csi, sizeof(struct csi_data));
++			memset(&phy->csi.buffered_csi, 0, sizeof(struct csi_data));
++			break;
++		default:
++			break;
++		}
++	} else {
++		target_csi = current_csi;
++	}
++
++	/* put the csi data into list */
++	INIT_LIST_HEAD(&target_csi->node);
++	spin_lock_bh(&phy->csi.lock);
++
++	if (!phy->csi.enable) {
++		kfree(target_csi);
++		goto out;
++	}
++
++	list_add_tail(&target_csi->node, &phy->csi.list);
++	phy->csi.count++;
++
++	if (phy->csi.count > CSI_MAX_BUF_NUM) {
++		struct csi_data *old;
++
++		old = list_first_entry(&phy->csi.list,
++				       struct csi_data, node);
++
++		list_del(&old->node);
++		kfree(old);
++		phy->csi.count--;
++	}
++
++	if (target_csi->chain_info & BIT(15)) /* last chain */
++		phy->csi.last_record = target_csi->ts;
++
++out:
++	spin_unlock_bh(&phy->csi.lock);
++	return 0;
++}
++
++void
++mt7996_mcu_csi_report_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++	struct mt7996_mcu_csi_event *event;
++	struct mt76_phy *mphy;
++	struct mt7996_phy *phy;
++
++	event = (struct mt7996_mcu_csi_event *)skb->data;
++
++	mphy = dev->mt76.phys[event->band_idx];
++	if (!mphy)
++		return;
++
++	phy = mphy->priv;
++
++	switch (le16_to_cpu(event->tag)) {
++	case UNI_EVENT_CSI_DATA:
++		mt7996_mcu_csi_report_data(phy, event->tlv_buf, le16_to_cpu(event->len) - 4);
++		break;
++	default:
++		break;
++	}
++}
++
+ static void
+ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -901,6 +1158,11 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ 	case MCU_UNI_EVENT_BF:
+ 		mt7996_mcu_rx_bf_event(dev, skb);
+ 		break;
++#endif
++#ifdef CONFIG_MTK_VENDOR
++	case MCU_UNI_EVENT_CSI_REPORT:
++		mt7996_mcu_csi_report_event(dev, skb);
++		break;
+ #endif
+ 	default:
+ 		break;
+@@ -6006,4 +6268,207 @@ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+ 
+ 	mt7996_mcu_add_beacon(hw, &vif->bss_conf, &mvif->deflink, val);
+ }
++
++static int mt7996_mcu_set_csi_enable(struct mt7996_phy *phy, u16 tag)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_frame_type(struct mt7996_phy *phy, u16 tag, u8 type_idx, u32 type)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 frame_type_idx;
++		u8 frame_type;
++		u8 rsv2[2];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.frame_type_idx = type_idx,
++		.frame_type = type,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_chain_filter(struct mt7996_phy *phy, u16 tag, u8 func, u32 value)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 function;
++		u8 chain_value;
++		u8 rsv2[2];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.function = func,
++		.chain_value = value,
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_sta_filter(struct mt7996_phy *phy, u16 tag, u32 op, u8 *sta_mac)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		u8 operation;
++		u8 rsv2[1];
++		u8 mac[6];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.operation = op,
++	};
++
++	memcpy(req.mac, sta_mac, ETH_ALEN);
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++static int mt7996_mcu_set_csi_active_mode(struct mt7996_phy *phy, u16 tag,
++					  u32 interval, u8 frame_idx, u8 subframe_idx, u32 bitmap)
++{
++	struct {
++		u8 band;
++		u8 rsv1[3];
++
++		__le16 tag;
++		__le16 len;
++		__le16 interval; /* uint: ms */
++		u8 frame_type_idx;
++		u8 subframe_type_idx;
++		__le32 bitmap; /* sta wcid bitmap */
++		u8 rsv2[4];
++	} __packed req = {
++		.band = phy->mt76->band_idx,
++		.tag = cpu_to_le16(tag),
++		.len = cpu_to_le16(sizeof(req) - 4),
++		.interval = cpu_to_le16(interval),
++		.frame_type_idx = frame_idx,
++		.subframe_type_idx = subframe_idx,
++		.bitmap = cpu_to_le32(bitmap),
++	};
++
++	return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(CSI_CTRL), &req,
++				sizeof(req), false);
++}
++
++void mt7996_csi_wcid_bitmap_update(void *data, struct ieee80211_sta *sta)
++{
++	struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++	struct mt7996_phy *phy = msta->vif->deflink.phy;
++	struct csi_bitmap_info_update *sta_info = (struct csi_bitmap_info_update *)data;
++	u16 wcid = 0;
++
++#define CSI_ACTIVE_MODE_ADD 1
++#define CSI_ACTIVE_MODE_REMOVE 0
++
++	if (!memcmp(sta_info->addr, sta->addr, ETH_ALEN)) {
++		wcid = msta->deflink.wcid.idx;
++
++		/* active mode: only support station with wcid less than 32 */
++		if (wcid > 32)
++			return;
++
++		if (sta_info->action == CSI_ACTIVE_MODE_ADD)
++			phy->csi.active_bitmap |= BIT(wcid);
++		else if (sta_info->action == CSI_ACTIVE_MODE_REMOVE)
++			phy->csi.active_bitmap &= ~(BIT(wcid));
++	}
++}
++
++int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
++			u8 cfg, u8 v1, u32 v2, u8 *mac_addr)
++{
++	switch (mode) {
++	case CSI_CONTROL_MODE_STOP:
++		return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_STOP);
++	case CSI_CONTROL_MODE_START:
++		return mt7996_mcu_set_csi_enable(phy, UNI_CMD_CSI_START);
++	case CSI_CONTROL_MODE_SET:
++		switch (cfg) {
++		case CSI_CONFIG_FRAME_TYPE:
++			if (v2 > 255)
++				return -EINVAL;
++
++			return mt7996_mcu_set_csi_frame_type(phy,
++					UNI_CMD_CSI_SET_FRAME_TYPE, v1, v2);
++		case CSI_CONFIG_CHAIN_FILTER:
++			if (v2 > 255)
++				return -EINVAL;
++
++			return mt7996_mcu_set_csi_chain_filter(phy,
++					UNI_CMD_CSI_SET_CHAIN_FILTER, v1, v2);
++		case CSI_CONFIG_STA_FILTER:
++			if (!is_valid_ether_addr(mac_addr))
++				return -EINVAL;
++
++			if (v2 > 255)
++				return -EINVAL;
++
++			return mt7996_mcu_set_csi_sta_filter(phy,
++					UNI_CMD_CSI_SET_STA_FILTER, v2, mac_addr);
++		case CSI_CONFIG_ACTIVE_MODE:
++			if (is_valid_ether_addr(mac_addr)) {
++				struct csi_bitmap_info_update sta_info;
++
++				if (v2 > 255)
++					return -EINVAL;
++
++				memcpy(sta_info.addr, mac_addr, ETH_ALEN);
++				sta_info.action = v2;
++
++				ieee80211_iterate_stations_atomic(phy->mt76->hw,
++								mt7996_csi_wcid_bitmap_update, &sta_info);
++				return 0;
++			} else {
++				u8 frame_type = v1 & 0x3;
++				u8 frame_subtype = (v1 & 0x3c) >> 2;
++
++					/* active mode: max interval is 3000ms */
++					if (v2 > 3000)
++						return -EINVAL;
++
++				return mt7996_mcu_set_csi_active_mode(phy, UNI_CMD_CSI_SET_ACTIVE_MODE,
++						v2, frame_type, frame_subtype, phy->csi.active_bitmap);
++			}
++		default:
++			return -EINVAL;
++		}
++	default:
++		return -EINVAL;
++	}
++}
+ #endif
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index f9f04680..42e9f525 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -1153,4 +1153,109 @@ struct fixed_rate_table_ctrl {
+ 	u8 _rsv2;
+ } __packed;
+ 
++#ifdef CONFIG_MTK_VENDOR
++struct mt7996_mcu_csi_event {
++	struct mt7996_mcu_rxd rxd;
++
++	u8 band_idx;
++	u8 _rsv[3];
++
++	__le16 tag;
++	__le16 len;
++	u8 tlv_buf[0];
++};
++
++enum UNI_EVENT_CSI_TAG_T {
++	UNI_EVENT_CSI_DATA = 0,
++	UNI_EVENT_CSI_MAX_NUM
++};
++
++struct csi_tlv {
++	struct {
++		__le32 tag;
++		__le32 len;
++	} basic;
++	union {
++		u8 mac[ETH_ALEN];
++		__le32 info;
++		s16 data[0];
++	};
++} __packed;
++
++struct csi_bitmap_info_update {
++	u8 action;
++	u8 addr[ETH_ALEN];
++};
++
++#define CSI_MAX_BUF_NUM	3000
++
++enum CSI_EVENT_TLV_TAG {
++	CSI_EVENT_FW_VER,
++	CSI_EVENT_CBW,
++	CSI_EVENT_RSSI,
++	CSI_EVENT_SNR,
++	CSI_EVENT_BAND,
++	CSI_EVENT_CSI_NUM,
++	CSI_EVENT_CSI_I_DATA,
++	CSI_EVENT_CSI_Q_DATA,
++	CSI_EVENT_DBW,
++	CSI_EVENT_CH_IDX,
++	CSI_EVENT_TA,
++	CSI_EVENT_EXTRA_INFO,
++	CSI_EVENT_RX_MODE,
++	CSI_EVENT_RSVD1,
++	CSI_EVENT_RSVD2,
++	CSI_EVENT_RSVD3,
++	CSI_EVENT_RSVD4,
++	CSI_EVENT_H_IDX,
++	CSI_EVENT_TX_RX_IDX,
++	CSI_EVENT_TS,
++	CSI_EVENT_PKT_SN,
++	CSI_EVENT_BW_SEG,
++	CSI_EVENT_REMAIN_LAST,
++	CSI_EVENT_TR_STREAM,
++	CSI_EVENT_TLV_TAG_NUM,
++};
++
++enum CSI_CHAIN_TYPE {
++	CSI_CHAIN_ERR,
++	CSI_CHAIN_COMPLETE,
++	CSI_CHAIN_SEGMENT_FIRST,
++	CSI_CHAIN_SEGMENT_MIDDLE,
++	CSI_CHAIN_SEGMENT_LAST,
++	CSI_CHAIN_SEGMENT_ERR,
++};
++
++enum CSI_CONTROL_MODE_T {
++	CSI_CONTROL_MODE_STOP,
++	CSI_CONTROL_MODE_START,
++	CSI_CONTROL_MODE_SET,
++	CSI_CONTROL_MODE_NUM
++};
++
++enum CSI_CONFIG_ITEM_T {
++	CSI_CONFIG_RSVD1,
++	CSI_CONFIG_WF,
++	CSI_CONFIG_RSVD2,
++	CSI_CONFIG_FRAME_TYPE,
++	CSI_CONFIG_TX_PATH,
++	CSI_CONFIG_OUTPUT_FORMAT,
++	CSI_CONFIG_INFO,
++	CSI_CONFIG_CHAIN_FILTER,
++	CSI_CONFIG_STA_FILTER,
++	CSI_CONFIG_ACTIVE_MODE,
++	CSI_CONFIG_ITEM_NUM
++};
++
++/* CSI config Tag */
++enum UNI_CMD_CSI_TAG_T {
++	UNI_CMD_CSI_STOP = 0,
++	UNI_CMD_CSI_START = 1,
++	UNI_CMD_CSI_SET_FRAME_TYPE = 2,
++	UNI_CMD_CSI_SET_CHAIN_FILTER = 3,
++	UNI_CMD_CSI_SET_STA_FILTER = 4,
++	UNI_CMD_CSI_SET_ACTIVE_MODE = 5,
++};
++#endif
++
+ #endif
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 2a638a4a..eb38427b 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -446,6 +446,47 @@ struct mt7996_air_monitor_ctrl {
+ 	struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
+ 	struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
+ };
++
++enum {
++	CSI_BW20,
++	CSI_BW40,
++	CSI_BW80,
++	CSI_BW160,
++	CSI_BW320
++};
++
++#define CSI_BW20_DATA_COUNT	64
++#define CSI_BW40_DATA_COUNT	128
++#define CSI_BW80_DATA_COUNT	256
++#define CSI_BW160_DATA_COUNT	512
++#define CSI_BW320_DATA_COUNT	1024
++
++struct csi_data {
++	u8 fw_ver;
++	u8 ch_bw;
++	u16 data_num;
++	s16 data_i[CSI_BW320_DATA_COUNT];
++	s16 data_q[CSI_BW320_DATA_COUNT];
++	u8 band;
++	s8 rssi;
++	u8 snr;
++	u32 ts;
++	u8 data_bw;
++	u8 pri_ch_idx;
++	u8 ta[ETH_ALEN];
++	u32 ext_info;
++	u16 rx_mode;
++	u16 rx_rate;
++	u32 chain_info;
++	u16 tx_idx;
++	u16 rx_idx;
++	u32 segment_num;
++	u8 remain_last;
++	u16 pkt_sn;
++	u8 tr_stream;
++
++	struct list_head node;
++};
+ #endif
+ 
+ struct mt7996_rro_ba_session {
+@@ -542,6 +583,18 @@ struct mt7996_phy {
+ 	u8 rts_bw_sig;
+ 	spinlock_t amnt_lock;
+ 	struct mt7996_air_monitor_ctrl amnt_ctrl;
++
++	struct {
++		struct list_head list;
++		spinlock_t lock;
++		u32 count;
++		bool enable;
++
++		struct csi_data buffered_csi;
++		u32 active_bitmap;
++		u32 interval;
++		u32 last_record;
++	} csi;
+ #endif
+ #ifdef CONFIG_MTK_DEBUG
+ 	bool sr_enable:1;
+@@ -1180,6 +1233,8 @@ void mt7996_mcu_set_mimo(struct mt7996_phy *phy);
+ int mt7996_set_muru_cfg(struct mt7996_phy *phy, u8 action, u8 val);
+ int mt7996_mcu_set_muru_cfg(struct mt7996_phy *phy, void *data);
+ void mt7996_set_beacon_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
++int mt7996_mcu_set_csi(struct mt7996_phy *phy, u8 mode,
++		       u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
+ #endif
+ 
+ int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+diff --git a/mt7996/vendor.c b/mt7996/vendor.c
+index 64ef5515..84b50ab2 100644
+--- a/mt7996/vendor.c
++++ b/mt7996/vendor.c
+@@ -119,6 +119,19 @@ beacon_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BEACON_CTRL] = {
+ 	[MTK_VENDOR_ATTR_BEACON_CTRL_MODE] = { .type = NLA_U8 },
+ };
+ 
++static const struct nla_policy
++csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++	[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG] = {.type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
++	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++};
++
+ struct mt7996_amnt_data {
+ 	u8 idx;
+ 	u8 addr[ETH_ALEN];
+@@ -1000,7 +1013,226 @@ static int mt7996_vendor_beacon_ctrl(struct wiphy *wiphy,
+ 
+ 	return 0;
+ }
++static int mt7996_vendor_csi_ctrl(struct wiphy *wiphy,
++				  struct wireless_dev *wdev,
++				  const void *data,
++				  int data_len)
++{
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
++	u8 band_idx = 0;
++	int err;
++
++	err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
++			csi_ctrl_policy, NULL);
++	if (err)
++		return err;
++
++	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX])
++		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX]);
++
++	if (!mt7996_band_valid(dev, band_idx))
++		goto error;
++
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		goto error;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++	if (!phy)
++		goto error;
++
++	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG]) {
++		u8 mode = 0, type = 0, v1 = 0;
++		u32 v2 = 0;
++		u8 mac_addr[ETH_ALEN] = {};
++		struct nlattr *cur;
++		int rem;
++
++		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_CFG], rem) {
++			switch (nla_type(cur)) {
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE:
++				mode = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE:
++				type = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1:
++				v1 = nla_get_u8(cur);
++				break;
++			case MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2:
++				v2 = nla_get_u32(cur);
++				break;
++			default:
++				return -EINVAL;
++			};
++		}
++
++		if (tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR]) {
++			u8 idx = 0;
++
++			nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR], rem) {
++				mac_addr[idx++] = nla_get_u8(cur);
++			}
++		}
++
++		err = mt7996_mcu_set_csi(phy, mode, type, v1, v2, mac_addr);
++		if (err < 0)
++			return err;
++
++		spin_lock_bh(&phy->csi.lock);
++
++		phy->csi.enable = !!mode;
++
++		/* clean up old csi stats */
++		if ((mode == CSI_CONTROL_MODE_STOP || mode == CSI_CONTROL_MODE_SET)
++			&& !list_empty(&phy->csi.list)) {
++			struct csi_data *c, *tmp_c;
++
++			list_for_each_entry_safe(c, tmp_c, &phy->csi.list, node) {
++				list_del(&c->node);
++				kfree(c);
++				phy->csi.count--;
++			}
++		} else if (mode == CSI_CONTROL_MODE_START) {
++			phy->csi.last_record = 0;
++		}
++
++		spin_unlock_bh(&phy->csi.lock);
++
++		if (mode == CSI_CONTROL_MODE_SET && type == CSI_CONFIG_STA_FILTER && v1 == 2)
++			phy->csi.interval = v2;
++	}
++
++	return 0;
++
++error:
++	dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
++	return -EINVAL;
++}
++
++static int
++mt7996_vendor_csi_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++			    struct sk_buff *skb, const void *data, int data_len,
++			    unsigned long *storage)
++{
++#define RESERVED_SET	BIT(31)
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct mt7996_dev *dev = mt7996_hw_dev(hw);
++	struct mt7996_phy *phy;
++	struct mt76_phy *mphy;
++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {0};
++	u8 band_idx = 0;
++	int err = 0;
++
++	if (*storage & RESERVED_SET) {
++		if ((*storage & GENMASK(15, 0)) == 0)
++			return -ENOENT;
++		(*storage)--;
++	}
++
++	if (data) {
++		err = nla_parse(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX, data, data_len,
++				csi_ctrl_policy, NULL);
++		if (err)
++			return err;
++	}
++
++	if (tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX])
++		band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX]);
++
++	if (!mt7996_band_valid(dev, band_idx))
++		return -EINVAL;
+ 
++	mphy = dev->mt76.phys[band_idx];
++	if (!mphy)
++		return -EINVAL;
++
++	phy = (struct mt7996_phy *)mphy->priv;
++	if (!phy)
++		return -EINVAL;
++
++	if (!(*storage & RESERVED_SET) && tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]) {
++		*storage = nla_get_u16(tb[MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM]);
++		*storage |= RESERVED_SET;
++	}
++
++	spin_lock_bh(&phy->csi.lock);
++
++	if (!list_empty(&phy->csi.list)) {
++		struct csi_data *csi;
++		void *a, *b;
++		int i;
++
++		csi = list_first_entry(&phy->csi.list, struct csi_data, node);
++
++		a = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_CTRL_DATA);
++		if (!a)
++			goto out;
++
++		if (nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_VER, 1) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_RSSI, csi->rssi) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_SNR, csi->snr) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_BW, csi->data_bw) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_CH_IDX, csi->pri_ch_idx) ||
++		    nla_put_u8(skb, MTK_VENDOR_ATTR_CSI_DATA_MODE, csi->rx_mode))
++			goto out;
++
++		if (nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_TX_ANT, csi->tx_idx) ||
++		    nla_put_u16(skb, MTK_VENDOR_ATTR_CSI_DATA_RX_ANT, csi->rx_idx))
++			goto out;
++
++		if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_INFO, csi->ext_info) ||
++		    nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO, csi->chain_info) ||
++		    nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_TS, csi->ts))
++			goto out;
++
++		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_TA);
++		if (!b)
++			goto out;
++
++		for (i = 0; i < ARRAY_SIZE(csi->ta); i++)
++			if (nla_put_u8(skb, i, csi->ta[i]))
++				goto out;
++		nla_nest_end(skb, b);
++
++		if (nla_put_u32(skb, MTK_VENDOR_ATTR_CSI_DATA_NUM, csi->data_num))
++			goto out;
++
++		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_I);
++		if (!b)
++			goto out;
++
++		for (i = 0; i < csi->data_num; i++)
++			if (nla_put_u16(skb, i, csi->data_i[i]))
++				goto out;
++		nla_nest_end(skb, b);
++
++		b = nla_nest_start(skb, MTK_VENDOR_ATTR_CSI_DATA_Q);
++		if (!b)
++			goto out;
++
++		for (i = 0; i < csi->data_num; i++)
++			if (nla_put_u16(skb, i, csi->data_q[i]))
++				goto out;
++		nla_nest_end(skb, b);
++
++		nla_nest_end(skb, a);
++
++		list_del(&csi->node);
++		kfree(csi);
++		phy->csi.count--;
++
++		err = phy->csi.count;
++	}
++out:
++	spin_unlock_bh(&phy->csi.lock);
++
++	return err;
++}
+ 
+ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 	{
+@@ -1129,6 +1361,18 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ 		.policy = beacon_ctrl_policy,
+ 		.maxattr = MTK_VENDOR_ATTR_BEACON_CTRL_MAX,
+ 	},
++	{
++		.info = {
++			.vendor_id = MTK_NL80211_VENDOR_ID,
++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL,
++		},
++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
++			 WIPHY_VENDOR_CMD_NEED_RUNNING,
++		.doit = mt7996_vendor_csi_ctrl,
++		.dumpit = mt7996_vendor_csi_ctrl_dump,
++		.policy = csi_ctrl_policy,
++		.maxattr = MTK_VENDOR_ATTR_CSI_CTRL_MAX,
++	},
+ };
+ 
+ void mt7996_vendor_register(struct mt7996_phy *phy)
+@@ -1136,6 +1380,9 @@ void mt7996_vendor_register(struct mt7996_phy *phy)
+ 	phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands;
+ 	phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands);
+ 
++	INIT_LIST_HEAD(&phy->csi.list);
++	spin_lock_init(&phy->csi.lock);
++
+ 	spin_lock_init(&phy->amnt_lock);
+ }
+ #endif
+diff --git a/mt7996/vendor.h b/mt7996/vendor.h
+index 32346775..834b3d08 100644
+--- a/mt7996/vendor.h
++++ b/mt7996/vendor.h
+@@ -7,6 +7,7 @@
+ 
+ enum mtk_nl80211_vendor_subcmds {
+ 	MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
++	MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0xc2,
+ 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
+ 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
+ 	MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+@@ -240,6 +241,57 @@ enum mtk_vendor_attr_beacon_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1
+ };
+ 
++enum mtk_vendor_attr_csi_ctrl {
++	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1,
++	MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_DATA,
++
++	MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_CTRL,
++	MTK_VENDOR_ATTR_CSI_CTRL_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_CTRL - 1
++};
++
++enum mtk_vendor_attr_csi_data {
++	MTK_VENDOR_ATTR_CSI_DATA_UNSPEC,
++	MTK_VENDOR_ATTR_CSI_DATA_PAD,
++
++	MTK_VENDOR_ATTR_CSI_DATA_VER,
++	MTK_VENDOR_ATTR_CSI_DATA_TS,
++	MTK_VENDOR_ATTR_CSI_DATA_RSSI,
++	MTK_VENDOR_ATTR_CSI_DATA_SNR,
++	MTK_VENDOR_ATTR_CSI_DATA_BW,
++	MTK_VENDOR_ATTR_CSI_DATA_CH_IDX,
++	MTK_VENDOR_ATTR_CSI_DATA_TA,
++	MTK_VENDOR_ATTR_CSI_DATA_NUM,
++	MTK_VENDOR_ATTR_CSI_DATA_I,
++	MTK_VENDOR_ATTR_CSI_DATA_Q,
++	MTK_VENDOR_ATTR_CSI_DATA_INFO,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD1,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD2,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD3,
++	MTK_VENDOR_ATTR_CSI_DATA_RSVD4,
++	MTK_VENDOR_ATTR_CSI_DATA_TX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_RX_ANT,
++	MTK_VENDOR_ATTR_CSI_DATA_MODE,
++	MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_CSI_DATA,
++	MTK_VENDOR_ATTR_CSI_DATA_MAX =
++		NUM_MTK_VENDOR_ATTRS_CSI_DATA - 1
++};
+ #endif
+ 
+ #endif
+-- 
+2.45.2
+