[PATCH] wifi: mt76: connac3: implement 6GHz power type detection and dynamic CLC filtering

JB Tsai <[email protected]>
Newsgroups org.infradead.lists.linux-mediatek,org.kernel.vger.linux-wireless
Message-ID <[email protected]>
From: Charlie-cy Wu <[email protected]>

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 <[email protected]>
---
 .../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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.