]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mt76: mt7996: add support for ERP CTS & HT protection
authorRyder Lee <ryder.lee@mediatek.com>
Wed, 21 Jan 2026 17:41:58 +0000 (09:41 -0800)
committerFelix Fietkau <nbd@nbd.name>
Mon, 23 Mar 2026 09:21:25 +0000 (09:21 +0000)
This patch adds support for handling BSS_CHANGED_ERP_CTS_PROT and
BSS_CHANGED_HT.

With this change, when the Wi-Fi driver needs to adjust its behavior for
compatibility or performance, especially concerning older 11g/n devices,
by enabling or disabling CTS protection frames, often for hidden SSIDs or
to manage legacy clients.

It also introduces debugfs options to manually control protection mode,
allowing users to select betweenno protection, RTS/CTS, and CTS-to-self.

Reviewed-by: Money Wang <money.wang@mediatek.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Link: https://patch.msgid.link/942ddb5777d5c201930d6609e9ba877a6ba6714a.1768879119.git.ryder.lee@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
drivers/net/wireless/mediatek/mt76/mt7996/main.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h

index 8d59cf43f0e2dc8d8e42d6b2ec7565ad69a79ff8..f44977f9093da76a9f5e2b4d7ce147de28c5b18e 100644 (file)
@@ -1363,6 +1363,7 @@ enum {
        UNI_BSS_INFO_BASIC = 0,
        UNI_BSS_INFO_RA = 1,
        UNI_BSS_INFO_RLM = 2,
+       UNI_BSS_INFO_PROTECT_INFO = 3,
        UNI_BSS_INFO_BSS_COLOR = 4,
        UNI_BSS_INFO_HE_BASIC = 5,
        UNI_BSS_INFO_11V_MBSSID = 6,
index 583724c31099cf162f0cecdc25ab34e1f5d5e31b..493c47c59d57272c19d1f80175c8d0f30125069e 100644 (file)
@@ -874,6 +874,10 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                                   !!(changed & BSS_CHANGED_BSSID));
        }
 
+       if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
+               mt7996_mcu_set_protection(phy, link, info->ht_operation_mode,
+                                         info->use_cts_prot);
+
        if (changed & BSS_CHANGED_ERP_SLOT) {
                int slottime = info->use_short_slot ? 9 : 20;
 
index aab83ad9c1b5762f4f0ab1ebb758462b1dcf55d6..82eea809c47b7769506c1c2f37381528c73736c2 100644 (file)
@@ -1247,6 +1247,52 @@ int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy, struct mt7996_vif_link *l
                                     MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
 }
 
+int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link,
+                             u8 ht_mode, bool use_cts_prot)
+{
+       struct mt7996_dev *dev = phy->dev;
+       struct bss_prot_tlv *prot;
+       struct sk_buff *skb;
+       struct tlv *tlv;
+       enum {
+               PROT_NONMEMBER   = BIT(1),
+               PROT_20MHZ       = BIT(2),
+               PROT_NONHT_MIXED = BIT(3),
+               PROT_LEGACY_ERP  = BIT(5),
+               PROT_NONGF_STA   = BIT(7),
+       };
+
+       skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76,
+                                        MT7996_BSS_UPDATE_MAX_SIZE);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PROTECT_INFO,
+                                    sizeof(*prot));
+       prot = (struct bss_prot_tlv *)tlv;
+
+       switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) {
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+               prot->prot_mode = cpu_to_le32(PROT_NONMEMBER);
+               break;
+       case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+               prot->prot_mode = cpu_to_le32(PROT_20MHZ);
+               break;
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+               prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED);
+               break;
+       }
+
+       if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+               prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA);
+
+       if (use_cts_prot)
+               prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP);
+
+       return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+                                    MCU_WM_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
 int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif,
                          struct ieee80211_bss_conf *link_conf)
 {
index de14394bec226819f589451eafacac06a4dab192..d9fb49f7b01b6cca2fd7a0e760c1d2b1b74eab5b 100644 (file)
@@ -481,6 +481,12 @@ struct bss_mld_tlv {
        u8 __rsv[2];
 } __packed;
 
+struct bss_prot_tlv {
+       __le16 tag;
+       __le16 len;
+       __le32 prot_mode;
+} __packed;
+
 struct sta_rec_ht_uni {
        __le16 tag;
        __le16 len;
index f850be874b1b1664eb54933e392481c5c5590229..f8b79b05169b063d7e4a29c25ad99504cc1b6eb4 100644 (file)
@@ -723,6 +723,8 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
                            const struct mt7996_dfs_pattern *pattern);
 int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
 int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link,
+                             u8 ht_mode, bool use_cts_prot);
 int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif,
                          struct ieee80211_bss_conf *link_conf);
 int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);