| 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 |
| |