wifi: mt76: mt7921: add auto regdomain switch support

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 <[email protected]>
Signed-off-by: Sean Wang <[email protected]>
Signed-off-by: JB Tsai <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
This commit is contained in:
JB Tsai
2026-06-09 12:28:21 +02:00
committed by Felix Fietkau
parent cd3d2a68c9
commit 06d1ccbecd
6 changed files with 92 additions and 11 deletions
+73 -7
View 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;
memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
dev->mt76.region = request->dfs_region;
dev->country_ie_env = request->country_ie_env;
/* 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;
if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
memcpy(mdev->alpha2, req->alpha2, 2);
mdev->region = req->dfs_region;
dev->country_ie_env = req->country_ie_env;
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;
}