blob: ade9a1e7cc191de1affe6ccb70dafc2de0efe5c5 [file] [log] [blame]
From 0e813e3d82da805835d53d1af182bd2d700aaafa Mon Sep 17 00:00:00 2001
From: MeiChia Chiu <meichia.chiu@mediatek.com>
Date: Tue, 13 Dec 2022 15:17:43 +0800
Subject: [PATCH 031/116] mtk: wifi: mt76: mt7996: add vendor commands support
mtk: wifi: mt76: fix muru_onoff as all enabled by default
Fix muru_onoff default value as 0xF, which means all MU & RU are
enabled. The purpose of this commit is to align muru_onoff value with
hostapd and mt76 driver
Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
mtk: wifi: mt76: mt7996: add vendor cmd to get available color bitmap
Add a vendor cmd to notify user space available color bitmap.
The OBSS BSS color bitmap is maintained in mac80211, so mt76 will make use of that.
Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
mtk: wifi: mt76: mt7996: add vendor subcmd EDCCA ctrl enable
mtk: wifi: mt76: mt7996: add ibf control vendor cmd
Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
mtk: wifi: mt76: mt7996: add three wire pta support
three wire enable bit 0 & 1 for EXT0 & EXT1, respectively
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
mtk: wifi: mt76: mt7996: Add static pp vendor support
Add static pp vendor support for setting pp mode.
User can enable/disable preamble puncture feature through hostapd
configuration and hostapd_cli. Driver can receive the nl80211 vendor
message and convert it to mcu commands.
Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
Signed-off-by: Allen Ye <allen.ye@mediatek.com>
---
mt76_connac_mcu.h | 2 +
mt7996/Makefile | 3 +-
mt7996/init.c | 9 +
mt7996/mac.c | 4 +
mt7996/main.c | 4 +
mt7996/mcu.c | 37 ++-
mt7996/mcu.h | 14 +
mt7996/mt7996.h | 53 +++
mt7996/mtk_mcu.c | 87 +++++
mt7996/mtk_mcu.h | 15 +
mt7996/vendor.c | 805 ++++++++++++++++++++++++++++++++++++++++++++++
mt7996/vendor.h | 153 +++++++++
12 files changed, 1180 insertions(+), 6 deletions(-)
create mode 100644 mt7996/vendor.c
create mode 100644 mt7996/vendor.h
diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
index d8830dc25..2a6091939 100644
--- a/mt76_connac_mcu.h
+++ b/mt76_connac_mcu.h
@@ -1252,6 +1252,7 @@ enum {
MCU_UNI_CMD_REG_ACCESS = 0x0d,
MCU_UNI_CMD_CHIP_CONFIG = 0x0e,
MCU_UNI_CMD_POWER_CTRL = 0x0f,
+ MCU_UNI_CMD_CFG_SMESH = 0x10,
MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
MCU_UNI_CMD_SER = 0x13,
MCU_UNI_CMD_TWT = 0x14,
@@ -1284,6 +1285,7 @@ enum {
MCU_UNI_CMD_PER_STA_INFO = 0x6d,
MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
};
enum {
diff --git a/mt7996/Makefile b/mt7996/Makefile
index 7bb17f440..6643c7a38 100644
--- a/mt7996/Makefile
+++ b/mt7996/Makefile
@@ -1,11 +1,12 @@
# SPDX-License-Identifier: ISC
EXTRA_CFLAGS += -DCONFIG_MT76_LEDS
EXTRA_CFLAGS += -DCONFIG_MTK_DEBUG
+EXTRA_CFLAGS += -DCONFIG_MTK_VENDOR
obj-$(CONFIG_MT7996E) += mt7996e.o
mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
- debugfs.o mmio.o
+ debugfs.o mmio.o vendor.o
mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
diff --git a/mt7996/init.c b/mt7996/init.c
index f1d681787..5cc2e6fbf 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -378,6 +378,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
phy->slottime = 9;
phy->beacon_rate = -1;
+ phy->muru_onoff = OFDMA_UL | OFDMA_DL | MUMIMO_DL | MUMIMO_UL;
hw->sta_data_size = sizeof(struct mt7996_sta);
hw->vif_data_size = sizeof(struct mt7996_vif);
@@ -629,6 +630,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
if (ret)
goto error;
+#ifdef CONFIG_MTK_VENDOR
+ mt7996_vendor_register(phy);
+#endif
+
ret = mt76_register_phy(mphy, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret)
@@ -1446,6 +1451,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
dev->mt76.test_ops = &mt7996_testmode_ops;
#endif
+#ifdef CONFIG_MTK_VENDOR
+ mt7996_vendor_register(&dev->phy);
+#endif
+
ret = mt76_register_device(&dev->mt76, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret)
diff --git a/mt7996/mac.c b/mt7996/mac.c
index c9f45abef..d55e5a761 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -679,6 +679,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
if (ieee80211_has_a4(fc) && is_mesh && status->amsdu)
*qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
}
+#ifdef CONFIG_MTK_VENDOR
+ if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc))
+ mt7996_vendor_amnt_fill_rx(phy, skb);
+#endif
} else {
status->flag |= RX_FLAG_8023;
mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
diff --git a/mt7996/main.c b/mt7996/main.c
index 1a1f0242c..6d1f61cd0 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -760,6 +760,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
mt7996_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+#ifdef CONFIG_MTK_VENDOR
+ mt7996_vendor_amnt_sta_remove(mvif->phy, sta);
+#endif
+
ret = mt7996_mcu_add_sta(dev, vif, sta, true, true);
if (ret)
return ret;
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 15751a54b..91c2dab1a 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -1378,6 +1378,8 @@ static void
mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mvif->phy;
struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
struct sta_rec_muru *muru;
struct tlv *tlv;
@@ -1389,11 +1391,14 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru));
muru = (struct sta_rec_muru *)tlv;
- muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer ||
- vif->bss_conf.he_mu_beamformer ||
- vif->bss_conf.vht_mu_beamformer ||
- vif->bss_conf.vht_mu_beamformee;
- muru->cfg.ofdma_dl_en = true;
+ muru->cfg.mimo_dl_en = (vif->bss_conf.eht_mu_beamformer ||
+ vif->bss_conf.he_mu_beamformer ||
+ vif->bss_conf.vht_mu_beamformer ||
+ vif->bss_conf.vht_mu_beamformee) &&
+ !!(phy->muru_onoff & MUMIMO_DL);
+ muru->cfg.mimo_ul_en = !!(phy->muru_onoff & MUMIMO_UL);
+ muru->cfg.ofdma_dl_en = !!(phy->muru_onoff & OFDMA_DL);
+ muru->cfg.ofdma_ul_en = !!(phy->muru_onoff & OFDMA_UL);
if (sta->deflink.vht_cap.vht_supported)
muru->mimo_dl.vht_mu_bfee =
@@ -4963,3 +4968,25 @@ int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable)
return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(SCS),
&req, sizeof(req), false);
}
+
+#ifdef CONFIG_MTK_VENDOR
+void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ u8 mode, val;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mvif->phy;
+
+ mode = FIELD_GET(RATE_CFG_MODE, *((u32 *)data));
+ val = FIELD_GET(RATE_CFG_VAL, *((u32 *)data));
+
+ switch (mode) {
+ case RATE_PARAM_AUTO_MU:
+ if (val < 0 || val > 15) {
+ printk("Wrong value! The value is between 0-15.\n");
+ break;
+ }
+ phy->muru_onoff = val;
+ break;
+ }
+}
+#endif
diff --git a/mt7996/mcu.h b/mt7996/mcu.h
index 5a60ccc55..2e845e920 100644
--- a/mt7996/mcu.h
+++ b/mt7996/mcu.h
@@ -754,8 +754,20 @@ enum {
RATE_PARAM_FIXED_MCS,
RATE_PARAM_FIXED_GI = 11,
RATE_PARAM_AUTO = 20,
+#ifdef CONFIG_MTK_VENDOR
+ RATE_PARAM_AUTO_MU = 32,
+#endif
};
+#define RATE_CFG_MODE GENMASK(15, 8)
+#define RATE_CFG_VAL GENMASK(7, 0)
+
+/* MURU */
+#define OFDMA_DL BIT(0)
+#define OFDMA_UL BIT(1)
+#define MUMIMO_DL BIT(2)
+#define MUMIMO_UL BIT(3)
+
enum {
BF_SOUNDING_ON = 1,
BF_HW_EN_UPDATE = 17,
@@ -833,6 +845,8 @@ mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower)
enum {
UNI_BAND_CONFIG_RADIO_ENABLE,
+ UNI_BAND_CONFIG_EDCCA_ENABLE = 0x05,
+ UNI_BAND_CONFIG_EDCCA_THRESHOLD = 0x06,
UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
};
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index b2a938162..402327d92 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -258,6 +258,34 @@ struct mt7996_wed_rro_session_id {
u16 id;
};
+#ifdef CONFIG_MTK_VENDOR
+#define MT7996_AIR_MONITOR_MAX_ENTRY 16
+#define MT7996_AIR_MONITOR_MAX_GROUP (MT7996_AIR_MONITOR_MAX_ENTRY >> 1)
+
+struct mt7996_air_monitor_group {
+ bool enable;
+ bool used[2];
+};
+
+struct mt7996_air_monitor_entry {
+ bool enable;
+
+ u8 group_idx;
+ u8 group_used_idx;
+ u8 muar_idx;
+ u8 addr[ETH_ALEN];
+ u32 last_seen;
+ s8 rssi[4];
+ struct ieee80211_sta *sta;
+};
+
+struct mt7996_air_monitor_ctrl {
+ u8 enable;
+ struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP];
+ struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY];
+};
+#endif
+
struct mt7996_phy {
struct mt76_phy *mt76;
struct mt7996_dev *dev;
@@ -300,6 +328,8 @@ struct mt7996_phy {
bool sku_limit_en;
bool sku_path_en;
+ u8 muru_onoff;
+
#ifdef CONFIG_NL80211_TESTMODE
struct {
u32 *reg_backup;
@@ -314,6 +344,10 @@ struct mt7996_phy {
u8 spe_idx;
} test;
#endif
+#ifdef CONFIG_MTK_VENDOR
+ spinlock_t amnt_lock;
+ struct mt7996_air_monitor_ctrl amnt_ctrl;
+#endif
};
struct mt7996_dev {
@@ -740,6 +774,25 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
bool hif2, int *irq);
u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id);
+#ifdef CONFIG_MTK_VENDOR
+void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
+void mt7996_vendor_register(struct mt7996_phy *phy);
+void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb);
+int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
+ struct ieee80211_sta *sta);
+#endif
+
+int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable);
+int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set);
+
+enum edcca_bw_id {
+ EDCCA_BW_20 = 0,
+ EDCCA_BW_40,
+ EDCCA_BW_80,
+ EDCCA_BW_160,
+ EDCCA_MAX_BW_NUM,
+};
+
#ifdef CONFIG_MTK_DEBUG
int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val);
diff --git a/mt7996/mtk_mcu.c b/mt7996/mtk_mcu.c
index e56ddd8ff..5c54d02c4 100644
--- a/mt7996/mtk_mcu.c
+++ b/mt7996/mtk_mcu.c
@@ -59,4 +59,91 @@ int mt7996_mcu_muru_dbg_info(struct mt7996_dev *dev, u16 item, u8 val)
return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(MURU), &req,
sizeof(req), true);
}
+
+int mt7996_mcu_edcca_enable(struct mt7996_phy *phy, bool enable)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct {
+ u8 band_idx;
+ u8 _rsv[3];
+
+ __le16 tag;
+ __le16 len;
+ u8 enable;
+ u8 std;
+ u8 _rsv2[2];
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_ENABLE),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .enable = enable,
+ .std = EDCCA_DEFAULT,
+ };
+
+ switch (dev->mt76.region) {
+ case NL80211_DFS_JP:
+ req.std = EDCCA_JAPAN;
+ break;
+ case NL80211_DFS_FCC:
+ if (band == NL80211_BAND_6GHZ)
+ req.std = EDCCA_FCC;
+ break;
+ case NL80211_DFS_ETSI:
+ if (band == NL80211_BAND_6GHZ)
+ req.std = EDCCA_ETSI;
+ break;
+ default:
+ break;
+ }
+
+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_edcca_threshold_ctrl(struct mt7996_phy *phy, u8 *value, bool set)
+{
+ struct {
+ u8 band_idx;
+ u8 _rsv[3];
+
+ __le16 tag;
+ __le16 len;
+ u8 threshold[4];
+ bool init;
+ } __packed *res, req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_BAND_CONFIG_EDCCA_THRESHOLD),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .init = false,
+ };
+ struct sk_buff *skb;
+ int ret;
+ int i;
+
+ for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
+ req.threshold[i] = value[i];
+
+ if (set)
+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
+ &req, sizeof(req), true);
+
+ ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76,
+ MCU_WM_UNI_CMD_QUERY(BAND_CONFIG),
+ &req, sizeof(req), true, &skb);
+
+ if (ret)
+ return ret;
+
+ res = (void *)skb->data;
+
+ for (i = 0; i < EDCCA_MAX_BW_NUM; i++)
+ value[i] = res->threshold[i];
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
#endif
diff --git a/mt7996/mtk_mcu.h b/mt7996/mtk_mcu.h
index c30418cae..36a58ad63 100644
--- a/mt7996/mtk_mcu.h
+++ b/mt7996/mtk_mcu.h
@@ -106,6 +106,21 @@ enum txpower_event {
UNI_TXPOWER_PHY_RATE_INFO = 5,
};
+enum {
+ EDCCA_CTRL_SET_EN = 0,
+ EDCCA_CTRL_SET_THRES,
+ EDCCA_CTRL_GET_EN,
+ EDCCA_CTRL_GET_THRES,
+ EDCCA_CTRL_NUM,
+};
+
+enum {
+ EDCCA_DEFAULT = 0,
+ EDCCA_FCC = 1,
+ EDCCA_ETSI = 2,
+ EDCCA_JAPAN = 3
+};
+
#endif
#endif
diff --git a/mt7996/vendor.c b/mt7996/vendor.c
new file mode 100644
index 000000000..0d6fa7792
--- /dev/null
+++ b/mt7996/vendor.c
@@ -0,0 +1,805 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2020, MediaTek Inc. All rights reserved.
+ */
+
+#include <net/netlink.h>
+
+#include "mt7996.h"
+#include "mcu.h"
+#include "vendor.h"
+#include "mtk_mcu.h"
+
+static const struct nla_policy
+mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = {
+ [MTK_VENDOR_ATTR_MU_CTRL_ONOFF] = {.type = NLA_U8 },
+ [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 },
+};
+
+static const struct nla_policy
+amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = {
+ [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED },
+ [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = {
+ [MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 },
+ [MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
+};
+
+static const struct nla_policy
+amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = {
+ [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 },
+ [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy
+bss_color_ctrl_policy[NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL] = {
+ [MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP] = { .type = NLA_U64 },
+};
+
+static const struct nla_policy
+edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_S8 },
+ [MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL] = { .type = NLA_U8 },
+};
+
+static const struct nla_policy
+edcca_dump_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP] = {
+ [MTK_VENDOR_ATTR_EDCCA_DUMP_MODE] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL] = { .type = NLA_U8 },
+};
+
+static const struct nla_policy
+three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
+ [MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
+};
+
+static const struct nla_policy
+ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+ [MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
+};
+
+static struct nla_policy
+pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = {
+ [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_PP_BAND_IDX] = { .type = NLA_U8 },
+};
+
+struct mt7996_amnt_data {
+ u8 idx;
+ u8 addr[ETH_ALEN];
+ s8 rssi[4];
+ u32 last_seen;
+};
+
+static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data,
+ int data_len)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_MU_CTRL];
+ int err;
+ u8 val8;
+ u32 val32 = 0;
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_MU_CTRL_MAX, data, data_len,
+ mu_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]) {
+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_MU_CTRL_ONOFF]);
+ val32 |= FIELD_PREP(RATE_CFG_MODE, RATE_PARAM_AUTO_MU) |
+ FIELD_PREP(RATE_CFG_VAL, val8);
+ ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_set_wireless_vif, &val32);
+ }
+
+ return 0;
+}
+
+static int
+mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct sk_buff *skb, const void *data, int data_len,
+ unsigned long *storage)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int len = 0;
+
+ if (*storage == 1)
+ return -ENOENT;
+ *storage = 1;
+
+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_MU_CTRL_DUMP, phy->muru_onoff))
+ return -ENOMEM;
+ len += 1;
+
+ return len;
+}
+
+void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl;
+ struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
+ __le16 fc = hdr->frame_control;
+ u8 addr[ETH_ALEN];
+ int i;
+
+ if (!ieee80211_has_fromds(fc))
+ ether_addr_copy(addr, hdr->addr2);
+ else if (ieee80211_has_tods(fc))
+ ether_addr_copy(addr, hdr->addr4);
+ else
+ ether_addr_copy(addr, hdr->addr3);
+
+ spin_lock_bh(&phy->amnt_lock);
+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) {
+ struct mt7996_air_monitor_entry *entry;
+
+ if (ether_addr_equal(addr, ctrl->entry[i].addr)) {
+ entry = &ctrl->entry[i];
+ entry->rssi[0] = status->chain_signal[0];
+ entry->rssi[1] = status->chain_signal[1];
+ entry->rssi[2] = status->chain_signal[2];
+ entry->rssi[3] = status->chain_signal[3];
+ entry->last_seen = jiffies;
+ break;
+ }
+ }
+ spin_unlock_bh(&phy->amnt_lock);
+}
+
+static int
+mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write,
+ u8 enable, u8 *value)
+{
+#define UNI_CMD_SMESH_PARAM 0
+ struct mt7996_dev *dev = phy->dev;
+ struct smesh_param {
+ __le16 tag;
+ __le16 length;
+
+ u8 enable;
+ bool a2;
+ bool a1;
+ bool data;
+ bool mgnt;
+ bool ctrl;
+ u8 padding[2];
+ } req = {
+ .tag = cpu_to_le16(UNI_CMD_SMESH_PARAM),
+ .length = cpu_to_le16(sizeof(req) - 4),
+
+ .enable = enable,
+ .a2 = true,
+ .a1 = true,
+ .data = true,
+ .mgnt = false,
+ .ctrl = false,
+ };
+ struct smesh_param *res;
+ struct sk_buff *skb;
+ int ret = 0;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH),
+ &req, sizeof(req), !write, &skb);
+
+ if (ret || write)
+ return ret;
+
+ res = (struct smesh_param *) skb->data;
+
+ *value = res->enable;
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static int
+mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr)
+{
+#define UNI_CMD_MUAR_ENTRY 2
+ struct mt7996_dev *dev = phy->dev;
+ struct muar_entry {
+ __le16 tag;
+ __le16 length;
+
+ bool smesh;
+ u8 hw_bss_index;
+ u8 muar_idx;
+ u8 entry_add;
+ u8 mac_addr[6];
+ u8 padding[2];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY),
+ .length = cpu_to_le16(sizeof(req) - 4),
+
+ .smesh = true,
+ .hw_bss_index = phy != &dev->phy,
+ .muar_idx = muar_idx,
+ .entry_add = 1,
+ };
+
+ ether_addr_copy(req.mac_addr, addr);
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
+ sizeof(req), true);
+}
+
+static int
+mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable)
+{
+ u8 status;
+ int ret;
+
+ ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status);
+ if (ret)
+ return ret;
+
+ if (status == enable)
+ return 0;
+
+ ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr)
+{
+ struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl;
+ struct mt7996_air_monitor_group *group;
+ struct mt7996_air_monitor_entry *entry;
+ int ret, i, j;
+
+ if (index >= MT7996_AIR_MONITOR_MAX_ENTRY)
+ return -1;
+
+ spin_lock_bh(&phy->amnt_lock);
+ entry = &amnt_ctrl->entry[index];
+ if (!is_zero_ether_addr(addr)) {
+ if (entry->enable == false) {
+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) {
+ group = &(amnt_ctrl->group[i]);
+ if (group->used[0] == false)
+ j = 0;
+ else if (group->used[1] == false)
+ j = 1;
+ else
+ continue;
+
+ group->enable = true;
+ group->used[j] = true;
+ entry->enable = true;
+ entry->group_idx = i;
+ entry->group_used_idx = j;
+ entry->muar_idx = 32 + 4 * i + 2 * j;
+ break;
+ }
+ }
+ } else {
+ group = &(amnt_ctrl->group[entry->group_idx]);
+
+ group->used[entry->group_used_idx] = false;
+ if (group->used[0] == false && group->used[1] == false)
+ group->enable = false;
+
+ entry->enable = false;
+ }
+ ether_addr_copy(entry->addr, addr);
+ amnt_ctrl->enable &= ~(1 << entry->group_idx);
+ amnt_ctrl->enable |= entry->enable << entry->group_idx;
+ spin_unlock_bh(&phy->amnt_lock);
+
+ ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr);
+ if (ret)
+ return ret;
+
+ return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable);
+}
+
+static int
+mt7966_vendor_amnt_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_phy *phy = mt7996_hw_phy(hw);
+ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
+ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET];
+ u8 index = 0;
+ u8 mac_addr[ETH_ALEN];
+ int err;
+
+ err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
+ amnt_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET])
+ return -EINVAL;
+
+ err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX,
+ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL);
+
+ if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] ||
+ !tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR])
+ return -EINVAL;
+
+ index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]);
+ memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN);
+
+ return mt7996_vendor_amnt_set_addr(phy, index, mac_addr);
+}
+
+int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy,
+ struct ieee80211_sta *sta)
+{
+ u8 zero[ETH_ALEN] = {};
+ int i;
+
+ if (!phy->amnt_ctrl.enable)
+ return 0;
+
+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
+ if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr))
+ return mt7996_vendor_amnt_set_addr(phy, i, zero);
+ return 0;
+}
+
+static int
+mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb,
+ u8 amnt_idx, int *attrtype)
+{
+ struct mt7996_air_monitor_entry *entry;
+ struct mt7996_amnt_data data;
+ u32 last_seen = 0;
+
+ spin_lock_bh(&phy->amnt_lock);
+ entry = &phy->amnt_ctrl.entry[amnt_idx];
+ if (entry->enable == 0) {
+ spin_unlock_bh(&phy->amnt_lock);
+ return 0;
+ }
+
+ last_seen = jiffies_to_msecs(jiffies - entry->last_seen);
+ ether_addr_copy(data.addr, entry->addr);
+ data.rssi[0] = entry->rssi[0];
+ data.rssi[1] = entry->rssi[1];
+ data.rssi[2] = entry->rssi[2];
+ data.rssi[3] = entry->rssi[3];
+ spin_unlock_bh(&phy->amnt_lock);
+
+ data.idx = amnt_idx;
+ data.last_seen = last_seen;
+
+ nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data);
+
+ return 1;
+}
+
+static int
+mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct sk_buff *skb, const void *data, int data_len,
+ unsigned long *storage)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL];
+ struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP];
+ void *a, *b;
+ int err = 0, attrtype = 0, i, len = 0;
+ u8 amnt_idx;
+
+ if (*storage == 1)
+ return -ENOENT;
+ *storage = 1;
+
+ err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len,
+ amnt_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP])
+ return -EINVAL;
+
+ err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX,
+ tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP],
+ amnt_dump_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX])
+ return -EINVAL;
+
+ amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]);
+
+ a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP);
+ b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT);
+
+ if (amnt_idx != 0xff) {
+ len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype);
+ } else {
+ for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++)
+ len += mt7996_amnt_dump(phy, skb, i, &attrtype);
+ }
+
+ nla_nest_end(skb, b);
+
+ nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len);
+
+ nla_nest_end(skb, a);
+
+ return len + 1;
+}
+
+static int
+mt7996_vendor_bss_color_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct sk_buff *skb, const void *data, int data_len,
+ unsigned long *storage)
+{
+ struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ int len = 0;
+
+ if (*storage == 1)
+ return -ENOENT;
+ *storage = 1;
+
+ if (nla_put_u64_64bit(skb, MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
+ ~bss_conf->used_color_bitmap, NL80211_ATTR_PAD))
+ return -ENOMEM;
+ len += 1;
+
+ return len;
+}
+
+static int mt7996_vendor_edcca_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_phy *phy = mt7996_hw_phy(hw);
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
+ int err;
+ u8 edcca_mode;
+ u8 edcca_value[EDCCA_MAX_BW_NUM];
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
+ edcca_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
+ return -EINVAL;
+
+ edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
+ if (edcca_mode == EDCCA_CTRL_SET_EN) {
+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL])
+ return -EINVAL;
+
+ edcca_value[0] =
+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
+
+ err = mt7996_mcu_edcca_enable(phy, !!edcca_value[0]);
+ if (err)
+ return err;
+ } else if (edcca_mode == EDCCA_CTRL_SET_THRES) {
+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] ||
+ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] ||
+ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] ||
+ !tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]) {
+ return -EINVAL;
+ }
+ edcca_value[EDCCA_BW_20] =
+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL]);
+ edcca_value[EDCCA_BW_40] =
+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL]);
+ edcca_value[EDCCA_BW_80] =
+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL]);
+ edcca_value[EDCCA_BW_160] =
+ nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL]);
+
+ err = mt7996_mcu_edcca_threshold_ctrl(phy, edcca_value, true);
+
+ if (err)
+ return err;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int
+mt7996_vendor_edcca_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct sk_buff *skb, const void *data, int data_len,
+ unsigned long *storage)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL];
+ int err;
+ u8 edcca_mode;
+ u8 value[EDCCA_MAX_BW_NUM];
+
+ if (*storage == 1)
+ return -ENOENT;
+ *storage = 1;
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_EDCCA_CTRL_MAX, data, data_len,
+ edcca_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE])
+ return -EINVAL;
+
+ edcca_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE]);
+
+ if (edcca_mode != EDCCA_CTRL_GET_THRES)
+ return -EINVAL;
+
+ err = mt7996_mcu_edcca_threshold_ctrl(phy, value, false);
+
+ if (err)
+ return err;
+
+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL, value[EDCCA_BW_20]) ||
+ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL, value[EDCCA_BW_40]) ||
+ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL, value[EDCCA_BW_80]) ||
+ nla_put_u8(skb, MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL, value[EDCCA_BW_160]))
+ return -ENOMEM;
+
+ return EDCCA_MAX_BW_NUM;
+}
+
+static int mt7996_vendor_3wire_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+#define UNI_3WIRE_EXT_EN 0
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL];
+ struct {
+ u8 __rsv1[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 three_wire_mode;
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_3WIRE_EXT_EN),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ };
+ int err;
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_3WIRE_CTRL_MAX, data, data_len,
+ three_wire_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE])
+ return -EINVAL;
+
+ req.three_wire_mode = nla_get_u8(tb[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE]);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PTA_3WIRE_CTRL), &req,
+ sizeof(req), false);
+}
+
+static int mt7996_vendor_ibf_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_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_IBF_CTRL];
+ int err;
+ u8 val;
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_IBF_CTRL_MAX, data, data_len,
+ ibf_ctrl_policy, NULL);
+ if (err)
+ return err;
+
+ if (tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]) {
+ val = nla_get_u8(tb[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE]);
+
+ dev->ibf = !!val;
+
+ err = mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int
+mt7996_vendor_ibf_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct sk_buff *skb, const void *data, int data_len,
+ unsigned long *storage)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+
+ if (*storage == 1)
+ return -ENOENT;
+ *storage = 1;
+
+ if (nla_put_u8(skb, MTK_VENDOR_ATTR_IBF_DUMP_ENABLE, dev->ibf))
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int mt7996_vendor_pp_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PP_CTRL];
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy;
+ struct mt76_phy *mphy;
+ struct cfg80211_chan_def *chandef;
+ int err;
+ u8 val8, band_idx = 0;
+
+ err = nla_parse(tb, MTK_VENDOR_ATTR_PP_CTRL_MAX, data, data_len,
+ pp_ctrl_policy, NULL);
+
+ if (tb[MTK_VENDOR_ATTR_PP_BAND_IDX]) {
+ band_idx = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_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;
+
+ chandef = &phy->chanctx->chandef;
+ if (!chandef)
+ goto error;
+
+ if (chandef->chan->band == NL80211_BAND_2GHZ)
+ return 0;
+
+ if (tb[MTK_VENDOR_ATTR_PP_MODE]) {
+ val8 = nla_get_u8(tb[MTK_VENDOR_ATTR_PP_MODE]);
+ switch (val8) {
+ case PP_DISABLE:
+ case PP_FW_MODE:
+ err = mt7996_mcu_set_pp_en(phy, val8, 0);
+ break;
+ case PP_USR_MODE:
+ /* handled by add_chanctx */
+ err = 0;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ }
+
+ return err;
+error:
+ dev_err(dev->mt76.dev, "Invalid band idx: %d\n", band_idx);
+ return -EINVAL;
+}
+
+static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_MU_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = mt7996_vendor_mu_ctrl,
+ .dumpit = mt7996_vendor_mu_ctrl_dump,
+ .policy = mu_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX,
+ },
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = mt7966_vendor_amnt_ctrl,
+ .dumpit = mt7966_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_BSS_COLOR_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .dumpit = mt7996_vendor_bss_color_ctrl_dump,
+ .policy = bss_color_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX,
+ },
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = mt7996_vendor_edcca_ctrl,
+ .dumpit = mt7996_vendor_edcca_ctrl_dump,
+ .policy = edcca_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_EDCCA_CTRL_MAX,
+ },
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = mt7996_vendor_3wire_ctrl,
+ .policy = three_wire_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_3WIRE_CTRL_MAX,
+ },
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = mt7996_vendor_ibf_ctrl,
+ .dumpit = mt7996_vendor_ibf_ctrl_dump,
+ .policy = ibf_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_IBF_CTRL_MAX,
+ },
+ {
+ .info = {
+ .vendor_id = MTK_NL80211_VENDOR_ID,
+ .subcmd = MTK_NL80211_VENDOR_SUBCMD_PP_CTRL,
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = mt7996_vendor_pp_ctrl,
+ .policy = pp_ctrl_policy,
+ .maxattr = MTK_VENDOR_ATTR_PP_CTRL_MAX,
+ },
+};
+
+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);
+
+ spin_lock_init(&phy->amnt_lock);
+}
diff --git a/mt7996/vendor.h b/mt7996/vendor.h
new file mode 100644
index 000000000..8aaa18eec
--- /dev/null
+++ b/mt7996/vendor.h
@@ -0,0 +1,153 @@
+#ifndef __MT7996_VENDOR_H
+#define __MT7996_VENDOR_H
+
+#define MTK_NL80211_VENDOR_ID 0x0ce7
+
+enum mtk_nl80211_vendor_subcmds {
+ MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae,
+ MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5,
+ MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL = 0xc7,
+ MTK_NL80211_VENDOR_SUBCMD_3WIRE_CTRL = 0xc8,
+ MTK_NL80211_VENDOR_SUBCMD_IBF_CTRL = 0xc9,
+ MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL = 0xca,
+ MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
+};
+
+enum mtk_vendor_attr_edcca_ctrl {
+ MTK_VENDOR_ATTR_EDCCA_THRESHOLD_INVALID = 0,
+
+ MTK_VENDOR_ATTR_EDCCA_CTRL_MODE,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_SEC160_VAL,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL,
+ MTK_VENDOR_ATTR_EDCCA_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
+};
+
+enum mtk_vendor_attr_edcca_dump {
+ MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
+
+ MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
+ MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
+ MTK_VENDOR_ATTR_EDCCA_DUMP_SEC160_VAL,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
+ MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
+ NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+};
+
+enum mtk_vendor_attr_3wire_ctrl {
+ MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_3WIRE_CTRL_MODE,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL,
+ MTK_VENDOR_ATTR_3WIRE_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
+};
+
+enum mtk_vendor_attr_mu_ctrl {
+ MTK_VENDOR_ATTR_MU_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_MU_CTRL_ONOFF,
+ MTK_VENDOR_ATTR_MU_CTRL_DUMP,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL,
+ MTK_VENDOR_ATTR_MU_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1
+};
+
+enum mtk_vendor_attr_mnt_ctrl {
+ MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_AMNT_CTRL_SET,
+ MTK_VENDOR_ATTR_AMNT_CTRL_DUMP,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL,
+ MTK_VENDOR_ATTR_AMNT_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1
+};
+
+enum mtk_vendor_attr_mnt_set {
+ MTK_VENDOR_ATTR_AMNT_SET_UNSPEC,
+
+ MTK_VENDOR_ATTR_AMNT_SET_INDEX,
+ MTK_VENDOR_ATTR_AMNT_SET_MACADDR,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_AMNT_SET,
+ MTK_VENDOR_ATTR_AMNT_SET_MAX =
+ NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1
+};
+
+enum mtk_vendor_attr_mnt_dump {
+ MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC,
+
+ MTK_VENDOR_ATTR_AMNT_DUMP_INDEX,
+ MTK_VENDOR_ATTR_AMNT_DUMP_LEN,
+ MTK_VENDOR_ATTR_AMNT_DUMP_RESULT,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP,
+ MTK_VENDOR_ATTR_AMNT_DUMP_MAX =
+ NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1
+};
+
+enum mtk_vendor_attr_bss_color_ctrl {
+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_AVAL_BSS_COLOR_BMP,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL,
+ MTK_VENDOR_ATTR_BSS_COLOR_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_BSS_COLOR_CTRL - 1
+};
+
+enum mtk_vendor_attr_ibf_ctrl {
+ MTK_VENDOR_ATTR_IBF_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_IBF_CTRL_ENABLE,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL,
+ MTK_VENDOR_ATTR_IBF_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_IBF_CTRL - 1
+};
+
+enum mtk_vendor_attr_ibf_dump {
+ MTK_VENDOR_ATTR_IBF_DUMP_UNSPEC,
+
+ MTK_VENDOR_ATTR_IBF_DUMP_ENABLE,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP,
+ MTK_VENDOR_ATTR_IBF_DUMP_MAX =
+ NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
+};
+
+enum mtk_vendor_attr_pp_ctrl {
+ MTK_VENDOR_ATTR_PP_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_PP_MODE,
+ MTK_VENDOR_ATTR_PP_BAND_IDX,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL,
+ MTK_VENDOR_ATTR_PP_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_PP_CTRL - 1
+};
+
+#endif
--
2.39.2