]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mt76: mt7921: add auto regdomain switch support
authorJB Tsai <jb.tsai@mediatek.com>
Tue, 3 Mar 2026 05:36:36 +0000 (13:36 +0800)
committerFelix Fietkau <nbd@nbd.name>
Tue, 9 Jun 2026 10:26:43 +0000 (10:26 +0000)
Implement 802.11d-based automatic regulatory domain switching to
dynamically determine the regulatory domain at runtime.

The scan-done event structure by reusing reserved padding and appending
new fields; the layout and values remains backward-compatible with
existing users.

Co-developed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
Link: https://patch.msgid.link/20260303053637.465465-4-jb.tsai@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
drivers/net/wireless/mediatek/mt76/mt7921/mac.c
drivers/net/wireless/mediatek/mt76/mt7921/main.c
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
drivers/net/wireless/mediatek/mt76/mt7921/regd.c
drivers/net/wireless/mediatek/mt76/mt7921/regd.h

index 6d0429f40b0f796abea50f5216b3270e7950b02a..78f633ad81a0760755640ecf1bff323b123659d8 100644 (file)
@@ -1599,7 +1599,7 @@ struct mt76_connac_hw_scan_done {
        u8 pno_enabled;
        u8 pad2[3];
        u8 sparse_channel_valid_num;
-       u8 pad3[3];
+       u8 alpha2[3];
        u8 channel_num[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM];
        /* idle format for channel_idle_time
         * 0: first bytes: idle time(ms) 2nd byte: dwell time(ms)
@@ -1612,6 +1612,7 @@ struct mt76_connac_hw_scan_done {
        u8 mdrdy_count[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM];
        __le32 beacon_2g_num;
        __le32 beacon_5g_num;
+       __le16 channel_scan_time[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM];
 } __packed;
 
 struct mt76_connac_sched_scan_req {
index d27dbee8df1b5199fcd194ec231720f495dc4922..85434996f8e70bfce05df447b159153d4b5907da 100644 (file)
@@ -7,6 +7,7 @@
 #include "mt7921.h"
 #include "../dma.h"
 #include "../mt76_connac2_mac.h"
+#include "regd.h"
 #include "mcu.h"
 
 #define MT_WTBL_TXRX_CAP_RATE_OFFSET   7
@@ -699,6 +700,8 @@ void mt7921_mac_reset_work(struct work_struct *work)
                                            IEEE80211_IFACE_ITER_RESUME_ALL,
                                            mt7921_vif_connect_iter, NULL);
        mt76_connac_power_save_sched(&dev->mt76.phy, pm);
+
+       mt7921_regd_change(&dev->phy, "00");
 }
 
 void mt7921_coredump_work(struct work_struct *work)
index 55ffd8eb8c6a66860f13c4d0f957a2865f06e9eb..0e19ba2b094deff624b66c2bfd008ad6a159e619 100644 (file)
@@ -1031,8 +1031,16 @@ void mt7921_scan_work(struct work_struct *work)
                rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
                if (rxd->eid == MCU_EVENT_SCHED_SCAN_DONE) {
                        ieee80211_sched_scan_results(phy->mt76->hw);
-               } else if (test_and_clear_bit(MT76_HW_SCANNING,
-                                             &phy->mt76->state)) {
+               } else if (rxd->eid == MCU_EVENT_SCAN_DONE) {
+                       struct mt76_connac_hw_scan_done *event = NULL;
+
+                       skb_pull(skb, sizeof(*rxd));
+                       event = (struct mt76_connac_hw_scan_done *)skb->data;
+                       mt7921_regd_change(phy, event->alpha2);
+               }
+
+               if (test_and_clear_bit(MT76_HW_SCANNING,
+                                      &phy->mt76->state)) {
                        struct cfg80211_scan_info info = {
                                .aborted = false,
                        };
index 9777c899e503f3ae0b373434d8107c9dedc0ae75..3e605a9ab9191e2fad85c223bcf1cb3dfc4be8b5 100644 (file)
@@ -484,7 +484,8 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
                        goto out;
                }
        }
-       ret = mt7921_mcu_set_clc(dev, "00", ENVIRON_INDOOR);
+
+       ret = mt7921_regd_init(phy);
 out:
        release_firmware(fw);
 
index fa753e8e041de2e417fb7fc0d60e963f0d63511f..d14ed100345b5883d87a14694c6157ae83aa5b72 100644 (file)
@@ -110,26 +110,92 @@ err:
 EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update);
 
 void mt7921_regd_notifier(struct wiphy *wiphy,
-                         struct regulatory_request *request)
+                         struct regulatory_request *req)
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct mt792x_dev *dev = mt792x_hw_dev(hw);
        struct mt76_connac_pm *pm = &dev->pm;
+       struct mt76_dev *mdev = &dev->mt76;
+
+       /* do not need to update the same country twice */
+       if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
+           dev->country_ie_env == req->country_ie_env)
+               return;
 
-       memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
-       dev->mt76.region = request->dfs_region;
-       dev->country_ie_env = request->country_ie_env;
+       memcpy(mdev->alpha2, req->alpha2, 2);
+       mdev->region = req->dfs_region;
+       dev->country_ie_env = req->country_ie_env;
 
-       if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
+       if (req->initiator == NL80211_REGDOM_SET_BY_USER) {
                if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
                        wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
                else
                        wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
        }
 
+       dev->regd_change = true;
+
        if (pm->suspended)
                return;
 
-       mt7921_mcu_regd_update(dev, request->alpha2,
-                              request->country_ie_env);
+       mt7921_mcu_regd_update(dev, req->alpha2,
+                              req->country_ie_env);
+}
+
+static bool
+mt7921_regd_is_valid_alpha2(const char *alpha2)
+{
+       if (!alpha2)
+               return false;
+
+       if (alpha2[0] == '0' && alpha2[1] == '0')
+               return true;
+
+       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+               return true;
+
+       return false;
+}
+
+int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2)
+{
+       struct wiphy *wiphy = phy->mt76->hw->wiphy;
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_dev *mdev = &dev->mt76;
+
+       if (dev->hw_full_reset)
+               return 0;
+
+       if (!mt7921_regd_is_valid_alpha2(alpha2) ||
+           !mt7921_regd_clc_supported(dev))
+               return -EINVAL;
+
+       if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
+               return 0;
+
+       /* do not need to update the same country twice */
+       if (!memcmp(alpha2, mdev->alpha2, 2))
+               return 0;
+
+       if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
+               return regulatory_hint(wiphy, alpha2);
+       else
+               return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
+}
+
+int mt7921_regd_init(struct mt792x_phy *phy)
+{
+       struct wiphy *wiphy = phy->mt76->hw->wiphy;
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt76_dev *mdev = &dev->mt76;
+
+       if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
+               wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
+                                          REGULATORY_DISABLE_BEACON_HINTS;
+       else
+               memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2));
+
+       return 0;
 }
index c7dcf747843cf9a792ded2a29ae18649fe1c0112..571f31629e9e015756bef9df39a30c38cced7d32 100644 (file)
@@ -13,5 +13,7 @@ int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
 void mt7921_regd_notifier(struct wiphy *wiphy,
                          struct regulatory_request *request);
 bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
+int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2);
+int mt7921_regd_init(struct mt792x_phy *phy);
 
 #endif