[PATCH] wifi: mt76: connac3: implement 6GHz power type detection and dynamic CLC filtering
JB Tsai
jb.tsai at mediatek.com
Fri Jul 24 01:51:03 PDT 2026
From: Charlie-cy Wu <Charlie-cy.Wu at mediatek.com>
This patch enhances 6GHz regulatory handling by implementing proper
power type detection and selective CLC (Country Location Configuration)
table filtering.
Changes in main.c:
1. Add mt7925_regd_set_6ghz_power_type() with multi-source channel
detection fallback mechanism:
- Primary: link_conf->chanreq.oper.chan (per-link BSS config)
- Secondary: vif->bss_conf.chanreq.oper.chan (default link)
- Tertiary: phy->mt76->chandef.chan (PHY layer)
2. Move mt7925_calc_vif_num() and mt7925_regd_set_6ghz_power_type()
before mt7925_mac_link_sta_add() to avoid implicit declaration
3. Call mt7925_regd_set_6ghz_power_type() in station add/remove paths
Changes in mcu.c:
1. Add power_type_change parameter to __mt7925_mcu_set_clc() and
related functions to distinguish between regulatory updates and
power type changes
2. Implement CLC table filtering logic:
- When power_type_change=false (from mt7925_mcu_regd_update):
Send 2/5G ('-'), 6G LPI ('0'), and 6G VLP ('2') tables
- When power_type_change=true (from mt7925_regd_set_6ghz_power_type):
Send only the specific 6G power table matching current power_type
3. Map power_type to CLC type codes:
MT_AP_LPI -> '0', MT_AP_SP -> '1', MT_AP_VLP -> '2'
4. Add env_6g field to CLC request structure to pass power type to FW
This fixes the issue where vif->bss_conf.chanreq.oper.chan was NULL
during early connection stages, and enables proper 6GHz power type
switching based on regulatory requirements.
Signed-off-by: Charlie-cy Wu <Charlie-cy.Wu at mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 84 +++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 46 ++++++++--
.../wireless/mediatek/mt76/mt7925/mt7925.h | 4 +-
.../net/wireless/mediatek/mt76/mt7925/regd.c | 10 +--
4 files changed, 133 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index a9059866b701..5f6ad84da70a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -884,6 +884,86 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return mvif->basic_rates_idx;
}
+static void
+mt7925_calc_vif_num(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ u32 *num = priv;
+
+ if (!priv)
+ return;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ *num += 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+mt7925_regd_set_6ghz_power_type(struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ bool is_add)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ struct mt792x_phy *phy = mvif->phy;
+ struct mt792x_dev *dev = phy->dev;
+ struct ieee80211_channel *chan = NULL;
+ u32 valid_vif_num = 0;
+ enum mt792x_reg_power_type old_power_type;
+ bool power_type_changed = false;
+
+ old_power_type = phy->power_type;
+
+ /* Try to get channel from link_conf first, then vif->bss_conf, then phy */
+ if (link_conf && link_conf->chanreq.oper.chan)
+ chan = link_conf->chanreq.oper.chan;
+ else if (vif->bss_conf.chanreq.oper.chan)
+ chan = vif->bss_conf.chanreq.oper.chan;
+ else if (phy->mt76->chandef.chan)
+ chan = phy->mt76->chandef.chan;
+
+ ieee80211_iterate_active_interfaces(mt76_hw(dev),
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_calc_vif_num, &valid_vif_num);
+
+ if (valid_vif_num > 1) {
+ phy->power_type = MT_AP_DEFAULT;
+ goto out;
+ }
+
+ if (!is_add)
+ vif->bss_conf.power_type = IEEE80211_REG_UNSET_AP;
+
+ switch (vif->bss_conf.power_type) {
+ case IEEE80211_REG_SP_AP:
+ phy->power_type = MT_AP_SP;
+ break;
+ case IEEE80211_REG_VLP_AP:
+ phy->power_type = MT_AP_VLP;
+ break;
+ case IEEE80211_REG_LPI_AP:
+ phy->power_type = MT_AP_LPI;
+ break;
+ case IEEE80211_REG_UNSET_AP:
+ phy->power_type = MT_AP_UNSET;
+ break;
+ default:
+ phy->power_type = MT_AP_DEFAULT;
+ break;
+ }
+
+out:
+ power_type_changed = (old_power_type != phy->power_type);
+
+ if (power_type_changed && chan && chan->band == NL80211_BAND_6GHZ)
+ mt7925_mcu_apply_regd(dev, dev->mt76.alpha2, dev->country_ie_env, true);
+}
+
static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
@@ -1006,6 +1086,8 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
goto out_pm;
}
+ mt7925_regd_set_6ghz_power_type(vif, link_conf, true);
+
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
return 0;
@@ -1279,6 +1361,8 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
mt76_wcid_cleanup(mdev, wcid);
mt76_wcid_mask_clear(mdev->wcid_mask, idx);
+ mt7925_regd_set_6ghz_power_type(vif, link_conf, false);
+
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 9678ab50dbe6..a12b939b6123 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -3496,7 +3496,7 @@ EXPORT_SYMBOL_GPL(mt7925_mcu_set_channel_domain);
static int
__mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap env_cap,
- struct mt7925_clc *clc, u8 idx)
+ struct mt7925_clc *clc, u8 idx, bool power_type_change)
{
struct mt7925_clc_segment *seg;
struct sk_buff *skb;
@@ -3514,19 +3514,38 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
u8 pad1;
u8 alpha2[2];
u8 type[2];
- u8 rsvd[64];
+ u8 env_6g;
+ u8 rsvd[63];
} __packed req = {
.tag = cpu_to_le16(0x3),
.idx = idx,
.env = env_cap,
+ .env_6g = dev->phy.power_type,
};
int ret, valid_cnt = 0;
u8 *pos, *last_pos;
+ u8 target_6g_type;
if (!clc)
return 0;
+ /* Determine target 6G power type based on phy->power_type */
+ switch (dev->phy.power_type) {
+ case MT_AP_LPI:
+ target_6g_type = '0';
+ break;
+ case MT_AP_SP:
+ target_6g_type = '1';
+ break;
+ case MT_AP_VLP:
+ target_6g_type = '2';
+ break;
+ default:
+ target_6g_type = '0'; /* default to LPI */
+ break;
+ }
+
req.ver = clc->ver;
pos = clc->data + sizeof(*seg) * clc->t0.nr_seg;
last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4));
@@ -3538,6 +3557,23 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
rule->alpha2[1] != alpha2[1])
continue;
+ /* Filter power tables based on call source:
+ * power_type_change == false: from mt7925_mcu_regd_update()
+ * - Send 2/5G ('-') and 6G LPI ('0') and 6G VLP ('2')
+ * power_type_change == true: from mt7925_regd_set_6ghz_power_type()
+ * - Send only the specific 6G power table based on power_type
+ */
+ if (power_type_change) {
+ /* Only send the specific 6G power table */
+ if (rule->type[0] != target_6g_type)
+ continue;
+ } else {
+ /* Send 2/5G and 6G LPI and VLP only */
+ if (rule->type[0] != '-' && rule->type[0] != '0' &&
+ rule->type[0] != '2')
+ continue;
+ }
+
seg = (struct mt7925_clc_segment *)clc->data
+ rule->seg_idx - 1;
@@ -3571,7 +3607,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
}
int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
- enum environment_cap env_cap)
+ enum environment_cap env_cap, bool power_type_change)
{
struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy;
int i, ret;
@@ -3586,13 +3622,13 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
continue;
ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap,
- phy->clc[i], i);
+ phy->clc[i], i, power_type_change);
/* If no country found, set "00" as default */
if (ret == -ENOENT)
ret = __mt7925_mcu_set_clc(dev, "00",
ENVIRON_INDOOR,
- phy->clc[i], i);
+ phy->clc[i], i, power_type_change);
if (ret < 0)
return ret;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 321e732347f2..e3fef41cbb33 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -385,7 +385,9 @@ int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw,
int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set);
int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
- enum environment_cap env_cap);
+ enum environment_cap env_cap, bool power_type_change);
+int mt7925_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2,
+ enum environment_cap env, bool power_type_change);
int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
u16 sel_links, int duration, u8 token_id);
int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
index 3eb3f172dba4..a039ec3ee47b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
@@ -137,14 +137,14 @@ mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
}
}
-static int mt7925_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2,
- enum environment_cap env)
+int mt7925_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2,
+ enum environment_cap env, bool power_type_change)
{
struct ieee80211_hw *hw = mt76_hw(dev);
struct wiphy *wiphy = hw->wiphy;
int ret;
- ret = mt7925_mcu_set_clc(dev, alpha2, env);
+ ret = mt7925_mcu_set_clc(dev, alpha2, env, power_type_change);
if (ret < 0)
return ret;
@@ -167,7 +167,7 @@ int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
mt792x_mutex_acquire(dev);
if (dev->regd_change)
- ret = mt7925_mcu_apply_regd(dev, alpha2, country_ie_env);
+ ret = mt7925_mcu_apply_regd(dev, alpha2, country_ie_env, false);
mt792x_mutex_release(dev);
dev->regd_change = false;
dev->regd_in_progress = false;
@@ -407,7 +407,7 @@ int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2)
} else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
return regulatory_hint(wiphy, alpha2);
} else {
- return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
+ return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR, false);
}
}
EXPORT_SYMBOL_GPL(mt7925_regd_change);
--
2.18.0
More information about the Linux-mediatek
mailing list