[][MAC80211][mt76][certification]

[Description]
Add support for Wi-Fi certification.
Please apply the [409] patch before applying this patch.
Note: [409] patch: 409-master-mt76-add-support-for-muru_onoff-via-debugfs.patch
(1006-mt7915-muru-onoff-switch-via-debugfs.patch)

[Release-log]
N/A

Change-Id: I9e6824487b1e638691843d7f8f91969205d052fa
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5410336
diff --git a/openwrt_patches-21.02/410-master-mt76-certification.patch b/openwrt_patches-21.02/410-master-mt76-certification.patch
new file mode 100644
index 0000000..4096032
--- /dev/null
+++ b/openwrt_patches-21.02/410-master-mt76-certification.patch
@@ -0,0 +1,1159 @@
+diff --git a/package/kernel/mt76/patches/1007-mt7915-cert-patches.patch b/package/kernel/mt76/patches/1007-mt7915-cert-patches.patch
+new file mode 100644
+index 0000000..7e88db2
+--- /dev/null
++++ b/package/kernel/mt76/patches/1007-mt7915-cert-patches.patch
+@@ -0,0 +1,1150 @@
++diff --git a/mt7915/init.c b/mt7915/init.c
++index 62a6d53b..c09432a3 100644
++--- a/mt7915/init.c
+++++ b/mt7915/init.c
++@@ -354,12 +354,17 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
++ 	if (!phy->dev->dbdc_support)
++ 		wiphy->txq_memory_limit = 32 << 20; /* 32 MiB */
++ 
++-	if (phy->mt76->cap.has_2ghz)
+++	if (phy->mt76->cap.has_2ghz) {
+++		phy->mt76->sband_2g.sband.ht_cap.ampdu_density =
+++			IEEE80211_HT_MPDU_DENSITY_4;
++ 		phy->mt76->sband_2g.sband.ht_cap.cap |=
++ 			IEEE80211_HT_CAP_LDPC_CODING |
++ 			IEEE80211_HT_CAP_MAX_AMSDU;
+++	}
++ 
++ 	if (phy->mt76->cap.has_5ghz) {
+++		phy->mt76->sband_5g.sband.ht_cap.ampdu_density =
+++			IEEE80211_HT_MPDU_DENSITY_4;
++ 		phy->mt76->sband_5g.sband.ht_cap.cap |=
++ 			IEEE80211_HT_CAP_LDPC_CODING |
++ 			IEEE80211_HT_CAP_MAX_AMSDU;
++diff --git a/mt7915/mac.c b/mt7915/mac.c
++index 084a597e..e97d46c7 100644
++--- a/mt7915/mac.c
+++++ b/mt7915/mac.c
++@@ -7,6 +7,7 @@
++ #include "../dma.h"
++ #include "mac.h"
++ #include "mcu.h"
+++#include "vendor.h"
++ 
++ #define to_rssi(field, rxv)	((FIELD_GET(field, rxv) - 220) / 2)
++ 
++@@ -2165,7 +2166,22 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy)
++ 		dev->mt76.aggr_stats[aggr1++] += val >> 16;
++ 	}
++ }
+++#ifdef CONFIG_MTK_VENDOR
+++void mt7915_capi_sta_rc_work(void *data, struct ieee80211_sta *sta)
+++{
+++	struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+++	struct mt7915_dev *dev = msta->vif->phy->dev;
+++	struct ieee80211_hw *hw = msta->vif->phy->mt76->hw;
+++	u32 *changed = data;
++ 
+++	spin_lock_bh(&dev->sta_poll_lock);
+++	msta->changed |= *changed;
+++	if (list_empty(&msta->rc_list)) {
+++		list_add_tail(&msta->rc_list, &dev->sta_rc_list);
+++	}
+++	spin_unlock_bh(&dev->sta_poll_lock);
+++}
+++#endif
++ void mt7915_mac_sta_rc_work(struct work_struct *work)
++ {
++ 	struct mt7915_dev *dev = container_of(work, struct mt7915_dev, rc_work);
++@@ -2187,7 +2203,13 @@ void mt7915_mac_sta_rc_work(struct work_struct *work)
++ 
++ 		sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
++ 		vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
++-
+++#ifdef CONFIG_MTK_VENDOR
+++		if (changed & CAPI_RFEATURE_CHANGED) {
+++			mt7915_mcu_set_rfeature_starec(&changed, dev, vif, sta);
+++			spin_lock_bh(&dev->sta_poll_lock);
+++			continue;
+++		}
+++#endif
++ 		if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
++ 			       IEEE80211_RC_NSS_CHANGED |
++ 			       IEEE80211_RC_BW_CHANGED))
++diff --git a/mt7915/main.c b/mt7915/main.c
++index 6633b2a1..049a68f2 100644
++--- a/mt7915/main.c
+++++ b/mt7915/main.c
++@@ -664,6 +664,9 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++ 	struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
++ 	struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
++ 	struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+++#ifdef CONFIG_MTK_VENDOR
+++	struct mt7915_phy *phy;
+++#endif
++ 	int ret, idx;
++ 
++ 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7915_WTBL_STA);
++@@ -689,7 +692,17 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++ #ifdef CONFIG_MTK_VENDOR
++ 	mt7915_vendor_amnt_sta_remove(mvif->phy, sta);
++ #endif
++-	return mt7915_mcu_add_rate_ctrl(dev, vif, sta, false);
+++	ret = mt7915_mcu_add_rate_ctrl(dev, vif, sta, false);
+++	if (ret)
+++		return ret;
+++
+++#ifdef CONFIG_MTK_VENDOR
+++	if (dev->dbg.muru_onoff & MUMIMO_DL_CERT) {
+++		phy = mvif->band_idx ? mt7915_ext_phy(dev) : &dev->phy;
+++		mt7915_mcu_set_mimo(phy, 0);
+++	}
+++#endif
+++	return 0;
++ }
++ 
++ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
++diff --git a/mt7915/mcu.c b/mt7915/mcu.c
++index dae38e63..ecac30cf 100644
++--- a/mt7915/mcu.c
+++++ b/mt7915/mcu.c
++@@ -4344,8 +4344,358 @@ int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev,
++ 	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TWT_AGRT_UPDATE),
++ 				 &req, sizeof(req), true);
++ }
++-
++ #ifdef CONFIG_MTK_VENDOR
+++void mt7915_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+++{
+++	u8 mode, val;
+++	struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+++	struct mt7915_dev *dev =  mvif->phy->dev;
+++
+++	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+++	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+++
+++	switch (mode) {
+++	case RATE_PARAM_FIXED_OFDMA:
+++		dev->dbg.muru_onoff = val;
+++		break;
+++	case RATE_PARAM_FIXED_MIMO:
+++		if (val == 0)
+++			dev->dbg.muru_onoff = FIELD_PREP(MUMIMO_DL_CERT, 1);
+++		break;
+++	}
+++}
+++
+++void mt7915_mcu_set_rfeature_starec(void *data, struct mt7915_dev *dev,
+++		       struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+++{
+++	struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+++	struct mt7915_vif *mvif = msta->vif;
+++	struct sta_rec_ra_fixed *ra;
+++	struct sk_buff *skb;
+++	struct tlv *tlv;
+++	u8 mode, val;
+++	int len = sizeof(struct sta_req_hdr) + sizeof(*ra);
+++
+++	mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+++	val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+++
+++	skb = mt7915_mcu_alloc_sta_req(dev, mvif, msta, len);
+++	if (IS_ERR(skb))
+++		return PTR_ERR(skb);
+++
+++	tlv = mt7915_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra));
+++	ra = (struct sta_rec_ra_fixed *)tlv;
+++
+++	switch (mode) {
+++	case RATE_PARAM_FIXED_GI:
+++		ra->field = cpu_to_le32(RATE_PARAM_FIXED_GI);
+++		ra->phy.sgi = val * 85;
+++		break;
+++	case RATE_PARAM_FIXED_HE_LTF:
+++		ra->field = cpu_to_le32(RATE_PARAM_FIXED_HE_LTF);
+++		ra->phy.he_ltf = val * 85;
+++		break;
+++	case RATE_PARAM_FIXED_MCS:
+++		ra->field = cpu_to_le32(RATE_PARAM_FIXED_MCS);
+++		ra->phy.mcs = val;
+++		break;
+++	}
+++
+++	mt76_mcu_skb_send_msg(&dev->mt76, skb,
+++			      MCU_EXT_CMD(STA_REC_UPDATE), true);
+++}
+++
+++int mt7915_mcu_set_mu_prot_frame_th(struct mt7915_phy *phy, u32 val)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		__le32 threshold;
+++	} __packed req = {
+++		.cmd = cpu_to_le32(MURU_SET_PROT_FRAME_THR),
+++		.threshold = val,
+++	};
+++
+++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
+++			sizeof(req), false);
+++}
+++
+++int mt7915_mcu_set_mu_edca(struct mt7915_phy *phy, u8 val)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		u8 override;
+++	} __packed req = {
+++		.cmd = cpu_to_le32(MURU_SET_CERT_MU_EDCA_OVERRIDE),
+++		.override = val,
+++	};
+++
+++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
+++			sizeof(req), false);
+++}
+++
+++int mt7915_mcu_set_muru_cfg(struct mt7915_phy *phy, struct mt7915_muru *muru)
+++{
+++        struct mt7915_dev *dev = phy->dev;
+++        struct {
+++                __le32 cmd;
+++                struct mt7915_muru muru;
+++        } __packed req = {
+++                .cmd = cpu_to_le32(MURU_SET_MANUAL_CFG),
+++        };
+++
+++        memcpy(&req.muru, muru, sizeof(struct mt7915_muru));
+++
+++        return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
+++                                 sizeof(req), false);
+++}
+++
+++int mt7915_set_muru_cfg(struct mt7915_phy *phy, u8 action, u8 val)
+++{
+++	struct mt7915_muru muru;
+++	struct mt7915_muru_dl *dl = &muru.dl;
+++	struct mt7915_muru_ul *ul = &muru.ul;
+++	struct mt7915_muru_comm *comm = &muru.comm;
+++
+++        memset(&muru, 0, sizeof(muru));
+++
+++	switch (action) {
+++	case MURU_DL_USER_CNT:
+++		dl->user_num = val;
+++		comm->ppdu_format |= MURU_PPDU_HE_MU;
+++		comm->sch_type |= MURU_OFDMA_SCH_TYPE_DL;
+++		muru.cfg_comm = cpu_to_le32(MURU_COMM_SET);
+++		muru.cfg_dl = cpu_to_le32(MURU_USER_CNT);
+++		return mt7915_mcu_set_muru_cfg(phy, &muru);
+++	case MURU_UL_USER_CNT:
+++		ul->user_num = val;
+++		comm->ppdu_format |= MURU_PPDU_HE_TRIG;
+++		comm->sch_type |= MURU_OFDMA_SCH_TYPE_UL;
+++		muru.cfg_comm = cpu_to_le32(MURU_COMM_SET);
+++		muru.cfg_ul = cpu_to_le32(MURU_USER_CNT);
+++		return mt7915_mcu_set_muru_cfg(phy, &muru);
+++	default:
+++		return 0;
+++        }
+++}
+++
+++void mt7915_mcu_set_ppdu_tx_type(struct mt7915_phy *phy, u8 ppdu_type)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		u8 enable_su;
+++	} __packed ppdu_type_req = {
+++		.cmd = cpu_to_le32(MURU_SET_SUTX),
+++	};
+++
+++	switch(ppdu_type) {
+++	case CAPI_SU:
+++		ppdu_type_req.enable_su = 1;
+++		mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++				  &ppdu_type_req, sizeof(ppdu_type_req), false);
+++		mt7915_set_muru_cfg(phy, MURU_DL_USER_CNT, 0);
+++		break;
+++	case CAPI_MU:
+++		ppdu_type_req.enable_su = 0;
+++		mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++				  &ppdu_type_req, sizeof(ppdu_type_req), false);
+++		break;
+++	default:
+++		break;
+++	}
+++}
+++
+++void mt7915_mcu_set_nusers_ofdma(struct mt7915_phy *phy, u8 type, u8 ofdma_user_cnt)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		u8 enable_su;
+++	} __packed nusers_ofdma_req = {
+++		.cmd = cpu_to_le32(MURU_SET_SUTX),
+++		.enable_su = 0,
+++	};
+++
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++			  &nusers_ofdma_req, sizeof(nusers_ofdma_req), false);
+++
+++	mt7915_mcu_set_mu_dl_ack_policy(phy, MU_DL_ACK_POLICY_SU_BAR);
+++	mt7915_mcu_set_mu_prot_frame_th(phy, 9999);
+++	switch(type) {
+++	case MURU_UL_USER_CNT:
+++		mt7915_set_muru_cfg(phy, MURU_UL_USER_CNT, ofdma_user_cnt);
+++		break;
+++	case MURU_DL_USER_CNT:
+++	default:
+++		mt7915_set_muru_cfg(phy, MURU_DL_USER_CNT, ofdma_user_cnt);
+++		break;
+++	}
+++}
+++
+++void mt7915_mcu_set_mimo(struct mt7915_phy *phy, u8 direction)
+++{
+++#define MUMIMO_SET_FIXED_RATE		10
+++#define MUMIMO_SET_FIXED_GRP_RATE	11
+++#define MUMIMO_SET_FORCE_MU		12
+++	struct mt7915_dev *dev = phy->dev;
+++	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+++	struct {
+++		__le32 cmd;
+++		__le16 sub_cmd;
+++		__le16 disable_ra;
+++	} __packed fixed_rate_req = {
+++		.cmd = cpu_to_le32(MURU_SET_MUMIMO_CTRL),
+++		.sub_cmd = cpu_to_le16(MUMIMO_SET_FIXED_RATE),
+++		.disable_ra = 1,
+++	};
+++	struct {
+++		__le32 cmd;
+++		__le32 sub_cmd;
+++		struct {
+++			u8 user_cnt:2;
+++			u8 rsv:2;
+++			u8 ns0:1;
+++			u8 ns1:1;
+++			u8 ns2:1;
+++			u8 ns3:1;
+++
+++			__le16 wlan_id_user0;
+++			__le16 wlan_id_user1;
+++			__le16 wlan_id_user2;
+++			__le16 wlan_id_user3;
+++
+++			u8 dl_mcs_user0:4;
+++			u8 dl_mcs_user1:4;
+++			u8 dl_mcs_user2:4;
+++			u8 dl_mcs_user3:4;
+++
+++			u8 ul_mcs_user0:4;
+++			u8 ul_mcs_user1:4;
+++			u8 ul_mcs_user2:4;
+++			u8 ul_mcs_user3:4;
+++
+++			u8 ru_alloc;
+++			u8 cap;
+++			u8 gi;
+++			u8 dl_ul;
+++		} grp_rate_conf;
+++	} fixed_grp_rate_req = {
+++		.cmd = cpu_to_le32(MURU_SET_MUMIMO_CTRL),
+++		.sub_cmd = cpu_to_le32(MUMIMO_SET_FIXED_GRP_RATE),
+++		.grp_rate_conf = {
+++			.user_cnt = 1,
+++			.ru_alloc = 134,
+++			.gi = 0,
+++			.cap = 1,
+++			.dl_ul = 0,
+++			.wlan_id_user0 = cpu_to_le16(1),
+++			.dl_mcs_user0 = 2,
+++			.wlan_id_user1 = cpu_to_le16(2),
+++			.dl_mcs_user1 = 2,
+++		},
+++	};
+++	struct {
+++		__le32 cmd;
+++		__le16 sub_cmd;
+++		bool force_mu;
+++	} __packed force_mu_req = {
+++		.cmd = cpu_to_le32(MURU_SET_MUMIMO_CTRL),
+++		.sub_cmd = cpu_to_le16(MUMIMO_SET_FORCE_MU),
+++		.force_mu = true,
+++	};
+++
+++	switch (chandef->width) {
+++	case NL80211_CHAN_WIDTH_20_NOHT:
+++	case NL80211_CHAN_WIDTH_20:
+++		fixed_grp_rate_req.grp_rate_conf.ru_alloc = 122;
+++		break;
+++	case NL80211_CHAN_WIDTH_80:
+++	default:
+++		break;
+++	}
+++
+++	mt7915_mcu_set_mu_dl_ack_policy(phy, MU_DL_ACK_POLICY_SU_BAR);
+++
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++			&fixed_rate_req, sizeof(fixed_rate_req), false);
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++			&fixed_grp_rate_req, sizeof(fixed_grp_rate_req), false);
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++			&force_mu_req, sizeof(force_mu_req), false);
+++}
+++
+++void mt7915_mcu_set_dynalgo(struct mt7915_phy *phy, u8 enable)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		u8 enable;
+++        } __packed req = {
+++		.cmd = cpu_to_le32(MURU_SET_20M_DYN_ALGO),
+++		.enable = enable,
+++        };
+++
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
+++			&req, sizeof(req), false);
+++}
+++
+++void mt7915_mcu_set_cert(struct mt7915_phy *phy, u8 type)
+++{
+++#define CFGINFO_CERT_CFG 4
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		struct basic_info{
+++			u8 dbdc_idx;
+++			u8 rsv[3];
+++			__le32 tlv_num;
+++			u8 tlv_buf[0];
+++		} hdr;
+++		struct cert_cfg{
+++			__le16 tag;
+++			__le16 length;
+++			u8 cert_program;
+++			u8 rsv[3];
+++		} tlv;
+++	} req = {
+++		.hdr = {
+++			.dbdc_idx = phy != &dev->phy,
+++			.tlv_num = cpu_to_le32(1),
+++		},
+++		.tlv = {
+++			.tag = cpu_to_le16(CFGINFO_CERT_CFG),
+++			.length = cpu_to_le16(sizeof(struct cert_cfg)),
+++			.cert_program = type, /* 1: CAPI Enable */
+++		}
+++	};
+++
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(CERT_CFG),
+++			  &req, sizeof(req), false);
+++}
+++
+++void mt7915_mcu_set_bypass_smthint(struct mt7915_phy *phy, u8 val)
+++{
+++#define BF_CMD_CFG_PHY		36
+++#define BF_PHY_SMTH_INTL_BYPASS 0
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		u8 cmd_category_id;
+++		u8 action;
+++		u8 band_idx;
+++		u8 smthintbypass;
+++		u8 rsv[12];
+++	} req = {
+++		.cmd_category_id = BF_CMD_CFG_PHY,
+++		.action = BF_PHY_SMTH_INTL_BYPASS,
+++		.band_idx = phy != &dev->phy,
+++		.smthintbypass = val,
+++	};
+++
+++	mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION),
+++			&req, sizeof(req), false);
+++}
+++
++ int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
++ 			u8 cfg, u8 v1, u32 v2, u8 *mac_addr)
++ {
++@@ -4445,4 +4795,117 @@ mt7915_mcu_report_csi(struct mt7915_dev *dev, struct sk_buff *skb)
++ 
++ 	return 0;
++ }
+++
+++int mt7915_mcu_set_bsrp_ctrl(struct mt7915_phy *phy, u16 interval,
+++			u16 ru_alloc, u32 ppdu_dur, u8 trig_flow, u8 ext_cmd)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		__le16 bsrp_interval;
+++		__le16 bsrp_ru_alloc;
+++		__le32 ppdu_duration;
+++		u8 trigger_flow;
+++		u8 ext_cmd_bsrp;
+++	} __packed req = {
+++		.cmd = cpu_to_le32(MURU_SET_BSRP_CTRL),
+++		.bsrp_interval = cpu_to_le16(interval),
+++		.bsrp_ru_alloc = cpu_to_le16(ru_alloc),
+++		.ppdu_duration = cpu_to_le32(ppdu_dur),
+++		.trigger_flow = trig_flow,
+++		.ext_cmd_bsrp = ext_cmd,
+++	};
+++
+++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
+++				sizeof(req), false);
+++}
+++
+++int mt7915_mcu_set_mu_dl_ack_policy(struct mt7915_phy *phy, u8 policy_num)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		__le32 cmd;
+++		u8 ack_policy;
+++	} __packed req = {
+++		.cmd = cpu_to_le32(MURU_SET_MU_DL_ACK_POLICY),
+++		.ack_policy = policy_num,
+++	};
+++
+++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
+++				sizeof(req), false);
+++}
+++
+++int mt7915_mcu_set_txbf_sound_info(struct mt7915_phy *phy, u8 action,
+++			u8 v1, u8 v2, u8 v3)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	struct {
+++		u8 cmd_category_id;
+++		u8 action;
+++		u8 read_clear;
+++		u8 vht_opt;
+++		u8 he_opt;
+++		u8 glo_opt;
+++		__le16 wlan_idx;
+++		u8 sound_interval;
+++		u8 sound_stop;
+++		u8 max_sound_sta;
+++		u8 tx_time;
+++		u8 mcs;
+++		bool ldpc;
+++		u8 inf;
+++		u8 rsv;
+++	} __packed req = {
+++		.cmd_category_id = BF_CMD_TXSND_INFO,
+++		.action = action,
+++	};
+++
+++	switch (action) {
+++	case BF_SND_CFG_OPT:
+++		req.vht_opt = v1;
+++		req.he_opt = v2;
+++		req.glo_opt = v3;
+++		break;
+++	default:
+++		return -EINVAL;
+++	}
+++
+++	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION), &req,
+++				sizeof(req), false);
+++}
+++
+++int mt7915_mcu_set_rfeature_trig_type(struct mt7915_phy *phy, u8 enable, u8 trig_type)
+++{
+++	struct mt7915_dev *dev = phy->dev;
+++	int ret = 0;
+++	struct {
+++		__le32 cmd;
+++		u8 trig_type;
+++	} __packed req = {
+++		.cmd = cpu_to_le32(MURU_SET_TRIG_TYPE),
+++		.trig_type = trig_type,
+++	};
+++
+++	if (enable) {
+++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
+++					 sizeof(req), false);
+++		if (ret)
+++			return ret;
+++	}
+++
+++	switch (trig_type) {
+++	case CAPI_BASIC:
+++		return mt7915_mcu_set_bsrp_ctrl(phy, 5, 67, 0, 0, enable);
+++	case CAPI_BRP:
+++		return mt7915_mcu_set_txbf_sound_info(phy, BF_SND_CFG_OPT,
+++				0x0, 0x0, 0x1b);
+++	case CAPI_MU_BAR:
+++		return mt7915_mcu_set_mu_dl_ack_policy(phy,
+++				MU_DL_ACK_POLICY_MU_BAR);
+++	case CAPI_BSRP:
+++		return mt7915_mcu_set_bsrp_ctrl(phy, 5, 67, 4, 0, enable);
+++	default:
+++		return 0;
+++	}
+++}
++ #endif
++diff --git a/mt7915/mcu.h b/mt7915/mcu.h
++index 41825cef..d22630aa 100644
++--- a/mt7915/mcu.h
+++++ b/mt7915/mcu.h
++@@ -319,6 +319,7 @@ enum {
++ 	MCU_EXT_CMD_PHY_STAT_INFO = 0xad,
++ #ifdef CONFIG_MTK_VENDOR
++ 	MCU_EXT_CMD_SMESH_CTRL = 0xae,
+++	MCU_EXT_CMD_CERT_CFG = 0xb7,
++ 	MCU_EXT_CMD_CSI_CTRL = 0xc2,
++ #endif
++ };
++@@ -999,9 +1000,13 @@ enum {
++ 	RATE_PARAM_FIXED = 3,
++ 	RATE_PARAM_MMPS_UPDATE = 5,
++ 	RATE_PARAM_FIXED_HE_LTF = 7,
++-	RATE_PARAM_FIXED_MCS,
+++	RATE_PARAM_FIXED_MCS = 8,
++ 	RATE_PARAM_FIXED_GI = 11,
++ 	RATE_PARAM_AUTO = 20,
+++#ifdef CONFIG_MTK_VENDOR
+++	RATE_PARAM_FIXED_MIMO = 30,
+++	RATE_PARAM_FIXED_OFDMA = 31,
+++#endif
++ };
++ 
++ #define RATE_CFG_MCS			GENMASK(3, 0)
++@@ -1149,6 +1154,9 @@ enum {
++ 	MURU_SET_PLATFORM_TYPE = 25,
++ };
++ 
+++#define RATE_CFG_MODE			GENMASK(15, 8)
+++#define RATE_CFG_VAL			GENMASK(7, 0)
+++
++ enum {
++ 	MURU_PLATFORM_TYPE_PERF_LEVEL_1 = 1,
++ 	MURU_PLATFORM_TYPE_PERF_LEVEL_2,
++@@ -1331,5 +1339,202 @@ struct csi_data {
++ #define OFDMA_UL			BIT(1)
++ #define MUMIMO_DL			BIT(2)
++ #define MUMIMO_UL			BIT(3)
+++#define MUMIMO_DL_CERT			BIT(4)
+++
+++struct mt7915_muru_comm {
+++	u8 ppdu_format;
+++	u8 sch_type;
+++	u8 band;
+++	u8 wmm_idx;
+++	u8 spe_idx;
+++	u8 proc_type;
+++};
+++
+++struct mt7915_muru_dl {
+++	u8 user_num;
+++	u8 tx_mode;
+++	u8 bw;
+++	u8 gi;
+++	u8 ltf;
+++	/* sigB */
+++	u8 mcs;
+++	u8 dcm;
+++	u8 cmprs;
+++
+++	u8 ru[8];
+++	u8 c26[2];
+++	u8 ack_policy;
+++
+++	struct {
+++		__le16 wlan_idx;
+++		u8 ru_alloc_seg;
+++		u8 ru_idx;
+++		u8 ldpc;
+++		u8 nss;
+++		u8 mcs;
+++		u8 mu_group_idx;
+++		u8 vht_groud_id;
+++		u8 vht_up;
+++		u8 he_start_stream;
+++		u8 he_mu_spatial;
+++		u8 ack_policy;
+++		__le16 tx_power_alpha;
+++	} usr[16];
+++};
+++
+++struct mt7915_muru_ul {
+++	u8 user_num;
+++
+++	/* UL TX */
+++	u8 trig_type;
+++	__le16 trig_cnt;
+++	__le16 trig_intv;
+++	u8 bw;
+++	u8 gi_ltf;
+++	__le16 ul_len;
+++	u8 pad;
+++	u8 trig_ta[ETH_ALEN];
+++	u8 ru[8];
+++	u8 c26[2];
+++
+++	struct {
+++		__le16 wlan_idx;
+++		u8 ru_alloc;
+++		u8 ru_idx;
+++		u8 ldpc;
+++		u8 nss;
+++		u8 mcs;
+++		u8 target_rssi;
+++		__le32 trig_pkt_size;
+++	} usr[16];
+++
+++	/* HE TB RX Debug */
+++	__le32 rx_hetb_nonsf_en_bitmap;
+++	__le32 rx_hetb_cfg[2];
+++
+++	/* DL TX */
+++	u8 ba_type;
+++};
+++
+++struct mt7915_muru {
+++	__le32 cfg_comm;
+++	__le32 cfg_dl;
+++	__le32 cfg_ul;
+++
+++	struct mt7915_muru_comm comm;
+++	struct mt7915_muru_dl dl;
+++	struct mt7915_muru_ul ul;
+++};
+++
+++#define MURU_PPDU_HE_TRIG		BIT(2)
+++#define MURU_PPDU_HE_MU                 BIT(3)
+++
+++#define MURU_OFDMA_SCH_TYPE_DL          BIT(0)
+++#define MURU_OFDMA_SCH_TYPE_UL          BIT(1)
+++
+++/* Common Config */
+++#define MURU_COMM_PPDU_FMT              BIT(0)
+++#define MURU_COMM_SCH_TYPE              BIT(1)
+++#define MURU_COMM_SET                   (MURU_COMM_PPDU_FMT | MURU_COMM_SCH_TYPE)
+++
+++/* DL&UL User config*/
+++#define MURU_USER_CNT                   BIT(4)
+++
+++enum {
+++	CAPI_SU,
+++	CAPI_MU,
+++	CAPI_ER_SU,
+++	CAPI_TB,
+++	CAPI_LEGACY
+++};
+++
+++enum {
+++	CAPI_BASIC,
+++	CAPI_BRP,
+++	CAPI_MU_BAR,
+++	CAPI_MU_RTS,
+++	CAPI_BSRP,
+++	CAPI_GCR_MU_BAR,
+++	CAPI_BQRP,
+++	CAPI_NDP_FRP
+++};
+++
+++enum {
+++	MURU_SET_BSRP_CTRL = 1,
+++	MURU_SET_SUTX = 16,
+++	MURU_SET_MUMIMO_CTRL = 17,
+++	MURU_SET_MANUAL_CFG = 100,
+++	MURU_SET_MU_DL_ACK_POLICY = 200,
+++	MURU_SET_TRIG_TYPE = 201,
+++	MURU_SET_20M_DYN_ALGO = 202,
+++	MURU_SET_PROT_FRAME_THR = 204,
+++	MURU_SET_CERT_MU_EDCA_OVERRIDE = 205,
+++};
+++
+++enum {
+++	MU_DL_ACK_POLICY_MU_BAR = 3,
+++	MU_DL_ACK_POLICY_TF_FOR_ACK = 4,
+++	MU_DL_ACK_POLICY_SU_BAR = 5,
+++};
+++
+++enum {
+++	BF_SOUNDING_OFF = 0,
+++	BF_SOUNDING_ON,
+++	BF_DATA_PACKET_APPLY,
+++	BF_PFMU_MEM_ALLOCATE,
+++	BF_PFMU_MEM_RELEASE,
+++	BF_PFMU_TAG_READ,
+++	BF_PFMU_TAG_WRITE,
+++	BF_PROFILE_READ,
+++	BF_PROFILE_WRITE,
+++	BF_PN_READ,
+++	BF_PN_WRITE,
+++	BF_PFMU_MEM_ALLOC_MAP_READ,
+++	BF_AID_SET,
+++	BF_STA_REC_READ,
+++	BF_PHASE_CALIBRATION,
+++	BF_IBF_PHASE_COMP,
+++	BF_LNA_GAIN_CONFIG,
+++	BF_PROFILE_WRITE_20M_ALL,
+++	BF_APCLIENT_CLUSTER,
+++	BF_AWARE_CTRL,
+++	BF_HW_ENABLE_STATUS_UPDATE,
+++	BF_REPT_CLONED_STA_TO_NORMAL_STA,
+++	BF_GET_QD,
+++	BF_BFEE_HW_CTRL,
+++	BF_PFMU_SW_TAG_WRITE,
+++	BF_MOD_EN_CTRL,
+++	BF_DYNSND_EN_INTR,
+++	BF_DYNSND_CFG_DMCS_TH,
+++	BF_DYNSND_EN_PFID_INTR,
+++	BF_CONFIG,
+++	BF_PFMU_DATA_WRITE,
+++	BF_FBRPT_DBG_INFO_READ,
+++	BF_CMD_TXSND_INFO,
+++	BF_CMD_PLY_INFO,
+++	BF_CMD_MU_METRIC,
+++	BF_CMD_TXCMD,
+++	BF_CMD_CFG_PHY,
+++	BF_CMD_SND_CNT,
+++	BF_CMD_MAX
+++};
+++
+++enum {
+++	BF_SND_READ_INFO = 0,
+++	BF_SND_CFG_OPT,
+++	BF_SND_CFG_INTV,
+++	BF_SND_STA_STOP,
+++	BF_SND_CFG_MAX_STA,
+++	BF_SND_CFG_BFRP,
+++	BF_SND_CFG_INF
+++};
+++
+++enum {
+++	MURU_UPDATE = 0,
+++	MURU_DL_USER_CNT,
+++	MURU_UL_USER_CNT,
+++	MURU_DL_INIT,
+++	MURU_UL_INIT,
+++};
++ 
++ #endif
++diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
++index 3edb1033..487af2d9 100644
++--- a/mt7915/mt7915.h
+++++ b/mt7915/mt7915.h
++@@ -605,8 +605,20 @@ int mt7915_init_debugfs(struct mt7915_phy *phy);
++ void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ 			    struct ieee80211_sta *sta, struct dentry *dir);
++ #endif
++-
++ #ifdef CONFIG_MTK_VENDOR
+++void mt7915_capi_sta_rc_work(void *data, struct ieee80211_sta *sta);
+++void mt7915_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+++void mt7915_mcu_set_rfeature_starec(void *data, struct mt7915_dev *dev,
+++		       struct ieee80211_vif *vif, struct ieee80211_sta *sta);
+++int mt7915_mcu_set_rfeature_trig_type(struct mt7915_phy *phy, u8 enable, u8 trig_type);
+++int mt7915_mcu_set_mu_dl_ack_policy(struct mt7915_phy *phy, u8 policy_num);
+++void mt7915_mcu_set_ppdu_tx_type(struct mt7915_phy *phy, u8 ppdu_type);
+++void mt7915_mcu_set_nusers_ofdma(struct mt7915_phy *phy, u8 type, u8 ofdma_user_cnt);
+++void mt7915_mcu_set_mimo(struct mt7915_phy *phy, u8 direction);
+++void mt7915_mcu_set_dynalgo(struct mt7915_phy *phy, u8 enable);
+++int mt7915_mcu_set_mu_edca(struct mt7915_phy *phy, u8 val);
+++void mt7915_mcu_set_cert(struct mt7915_phy *phy, u8 type);
+++void mt7915_mcu_set_bypass_smthint(struct mt7915_phy *phy, u8 val);
++ void mt7915_vendor_register(struct mt7915_phy *phy);
++ int mt7915_mcu_set_csi(struct mt7915_phy *phy, u8 mode,
++ 			u8 cfg, u8 v1, u32 v2, u8 *mac_addr);
++diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
++index a2cdc6d5..f56f5871 100644
++--- a/mt7915/mtk_debugfs.c
+++++ b/mt7915/mtk_debugfs.c
++@@ -2634,7 +2634,8 @@ static int mt7915_muru_onoff_get(void *data, u64 *val)
++ 
++ 	*val = dev->dbg.muru_onoff;
++ 
++-	printk("mumimo ul:%d, mumimo dl:%d, ofdma ul:%d, ofdma dl:%d\n",
+++	printk("cert mumimo dl:%d, mumimo ul:%d, mumimo dl:%d, ofdma ul:%d, ofdma dl:%d\n",
+++		!!(dev->dbg.muru_onoff & MUMIMO_DL_CERT),
++ 		!!(dev->dbg.muru_onoff & MUMIMO_UL),
++ 		!!(dev->dbg.muru_onoff & MUMIMO_DL),
++ 		!!(dev->dbg.muru_onoff & OFDMA_UL),
++@@ -2647,8 +2648,8 @@ static int mt7915_muru_onoff_set(void *data, u64 val)
++ {
++ 	struct mt7915_dev *dev = data;
++ 
++-	if (val > 15) {
++-		printk("Wrong value! The value is between 0 ~ 15.\n");
+++	if (val > 31) {
+++		printk("Wrong value! The value is between 0 ~ 31.\n");
++ 		goto exit;
++ 	}
++ 
++diff --git a/mt7915/vendor.c b/mt7915/vendor.c
++index b94d787e..a8381011 100644
++--- a/mt7915/vendor.c
+++++ b/mt7915/vendor.c
++@@ -22,6 +22,29 @@ csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
++ 	[MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
++ };
++ 
+++static const struct nla_policy
+++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
+++};
+++
+++static const struct nla_policy
+++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
+++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
+++};
+++
++ struct csi_null_tone {
++ 	u8 start;
++ 	u8 end;
++@@ -777,6 +800,149 @@ mt7915_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
++ 	return len + 1;
++ }
++ 
+++static int mt7915_vendor_rfeature_ctrl(struct wiphy *wiphy,
+++				  struct wireless_dev *wdev,
+++				  const void *data,
+++				  int data_len)
+++{
+++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+++	struct mt7915_phy *phy = mt7915_hw_phy(hw);
+++	struct mt7915_dev *dev = phy->dev;
+++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL];
+++	int err;
+++	u32 val;
+++
+++	err = nla_parse(tb, MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX, data, data_len,
+++			rfeature_ctrl_policy, NULL);
+++	if (err)
+++		return err;
+++
+++	val = CAPI_RFEATURE_CHANGED;
+++
+++	if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI]) {
+++		val |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_GI)|
+++			FIELD_PREP(RATE_CFG_VAL, nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI]));
+++		ieee80211_iterate_stations_atomic(hw, mt7915_capi_sta_rc_work, &val);
+++		ieee80211_queue_work(hw, &dev->rc_work);
+++	}
+++	else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF]) {
+++		val |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_HE_LTF)|
+++			FIELD_PREP(RATE_CFG_VAL, nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF]));
+++                ieee80211_iterate_stations_atomic(hw, mt7915_capi_sta_rc_work, &val);
+++		ieee80211_queue_work(hw, &dev->rc_work);
+++	}
+++	else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG]) {
+++		u8 enable, trig_type;
+++		int rem;
+++		struct nlattr *cur;
+++
+++		nla_for_each_nested(cur, tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG], rem) {
+++			switch(nla_type(cur)) {
+++			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN:
+++				enable = nla_get_u8(cur);
+++				break;
+++			case MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE:
+++				trig_type = nla_get_u8(cur);
+++				break;
+++			default:
+++				return -EINVAL;
+++			};
+++		}
+++
+++		err = mt7915_mcu_set_rfeature_trig_type(phy, enable, trig_type);
+++		if (err)
+++			return err;
+++	}
+++	else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]) {
+++		u8 ack_policy;
+++
+++		ack_policy = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY]);
+++#define HE_TB_PPDU_ACK 4
+++		switch (ack_policy) {
+++		case HE_TB_PPDU_ACK:
+++			return mt7915_mcu_set_mu_dl_ack_policy(phy, ack_policy);
+++		default:
+++			return 0;
+++		}
+++	}
+++	else if (tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF]) {
+++		u8 trig_txbf;
+++
+++		trig_txbf = nla_get_u8(tb[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF]);
+++		/* CAPI only issues trig_txbf=disable */
+++	}
+++
+++	return 0;
+++}
+++
+++static int mt7915_vendor_wireless_ctrl(struct wiphy *wiphy,
+++				  struct wireless_dev *wdev,
+++				  const void *data,
+++				  int data_len)
+++{
+++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+++	struct mt7915_phy *phy = mt7915_hw_phy(hw);
+++	struct mt7915_dev *dev = phy->dev;
+++	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL];
+++	int err;
+++	u8 val8;
+++	u16 val16;
+++	u32 val32;
+++
+++	err = nla_parse(tb, MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX, data, data_len,
+++			wireless_ctrl_policy, NULL);
+++	if (err)
+++		return err;
+++
+++	val32 = CAPI_WIRELESS_CHANGED;
+++
+++	if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS]) {
+++		val32 &= ~CAPI_WIRELESS_CHANGED;
+++		val32 |= CAPI_RFEATURE_CHANGED |
+++			FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MCS) |
+++			FIELD_PREP(RATE_CFG_VAL, nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS]));
+++		ieee80211_iterate_stations_atomic(hw, mt7915_capi_sta_rc_work, &val32);
+++		ieee80211_queue_work(hw, &dev->rc_work);
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]) {
+++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA]);
+++		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_OFDMA) |
+++			 FIELD_PREP(RATE_CFG_VAL, val8);
+++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+++			mt7915_set_wireless_vif, &val32);
+++		if (val8 == 3) /* DL20and80 */
+++			mt7915_mcu_set_dynalgo(phy, 1); /* Enable dynamic algo */
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]) {
+++		val16 = nla_get_u16(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE]);
+++		hw->max_tx_aggregation_subframes = val16;
+++		hw->max_rx_aggregation_subframes = val16;
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA]) {
+++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA]);
+++		mt7915_mcu_set_mu_edca(phy, val8);
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]) {
+++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE]);
+++		mt7915_mcu_set_ppdu_tx_type(phy, val8);
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]) {
+++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA]);
+++		if (FIELD_GET(OFDMA_UL, dev->dbg.muru_onoff) == 1)
+++			mt7915_mcu_set_nusers_ofdma(phy, MURU_UL_USER_CNT, val8);
+++		else
+++			mt7915_mcu_set_nusers_ofdma(phy, MURU_DL_USER_CNT, val8);
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]) {
+++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO]);
+++		val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_FIXED_MIMO) |
+++			 FIELD_PREP(RATE_CFG_VAL, val8);
+++		ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+++			mt7915_set_wireless_vif, &val32);
+++	} else if (tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]) {
+++		val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT]);
+++		mt7915_mcu_set_cert(phy, val8); /* Cert Enable for OMI */
+++		mt7915_mcu_set_bypass_smthint(phy, val8); /* Cert bypass smooth interpolation */
+++	}
+++
+++	return 0;
+++}
+++
+++
++ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
++ 	{
++ 		.info = {
++@@ -801,7 +967,29 @@ static const struct wiphy_vendor_command mt7915_vendor_commands[] = {
++ 		.dumpit = mt7915_vendor_amnt_ctrl_dump,
++ 		.policy = amnt_ctrl_policy,
++ 		.maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX,
++-	}
+++	},
+++	{
+++		.info = {
+++			.vendor_id = MTK_NL80211_VENDOR_ID,
+++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL,
+++		},
+++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+++			WIPHY_VENDOR_CMD_NEED_RUNNING,
+++		.doit = mt7915_vendor_rfeature_ctrl,
+++		.policy = rfeature_ctrl_policy,
+++		.maxattr = MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX,
+++	},
+++	{
+++		.info = {
+++			.vendor_id = MTK_NL80211_VENDOR_ID,
+++			.subcmd = MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL,
+++		},
+++		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+++			WIPHY_VENDOR_CMD_NEED_RUNNING,
+++		.doit = mt7915_vendor_wireless_ctrl,
+++		.policy = wireless_ctrl_policy,
+++		.maxattr = MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX,
+++	},
++ };
++ 
++ void mt7915_vendor_register(struct mt7915_phy *phy)
++diff --git a/mt7915/vendor.h b/mt7915/vendor.h
++index 976817f3..1b08321c 100644
++--- a/mt7915/vendor.h
+++++ b/mt7915/vendor.h
++@@ -6,6 +6,48 @@
++ 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,
+++};
+++
+++enum mtk_capi_control_changed {
+++	CAPI_RFEATURE_CHANGED		= BIT(16),
+++	CAPI_WIRELESS_CHANGED		= BIT(17),
+++};
+++
+++enum mtk_vendor_attr_wireless_ctrl {
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_UNSPEC,
+++
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_OFDMA,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+++
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MU_EDCA, /* reserve */
+++	/* keep last */
+++	NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL,
+++	MTK_VENDOR_ATTR_WIRELESS_CTRL_MAX =
+++		NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL - 1
+++};
+++
+++enum mtk_vendor_attr_rfeature_ctrl {
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+++
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
+++
+++	/* keep last */
+++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+++	MTK_VENDOR_ATTR_RFEATURE_CTRL_MAX =
+++	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
++ };
++ 
++ enum mtk_vendor_attr_csi_ctrl {
++-- 
++2.29.2
++
+-- 
+2.29.2
+