]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: rtw89: regd/acpi: support 6 GHz VLP policy via ACPI DSM
authorZong-Zhe Yang <kevin_yang@realtek.com>
Wed, 9 Jul 2025 06:50:06 +0000 (14:50 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Tue, 15 Jul 2025 01:13:32 +0000 (09:13 +0800)
Process ACPI DSM function 11 to get 6 GHz VLP support by country. If
not allowed, return error to block the connection. By default, i.e.
ACPI DSM function is not configured, disallow 6 GHz VLP on country US
and country CA, because some platform-level certifications are needed
in FCC regulation before operating on 6 GHz VLP connection.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250709065006.32028-5-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/acpi.c
drivers/net/wireless/realtek/rtw89/acpi.h
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/regd.c

index c35a36e0f4d91fc418eae4b6301e765c7720df9b..f1e758a5f32b288bad9cf5948dc951e93eec2ac9 100644 (file)
@@ -236,6 +236,50 @@ int rtw89_acpi_dsm_get_policy_6ghz_sp(struct rtw89_dev *rtwdev,
        return 0;
 }
 
+static bool chk_acpi_policy_6ghz_vlp_sig(const struct rtw89_acpi_policy_6ghz_vlp *p)
+{
+       return p->signature[0] == 0x52 &&
+              p->signature[1] == 0x54 &&
+              p->signature[2] == 0x4B &&
+              p->signature[3] == 0x0B;
+}
+
+static
+int rtw89_acpi_dsm_get_policy_6ghz_vlp(struct rtw89_dev *rtwdev,
+                                      union acpi_object *obj,
+                                      struct rtw89_acpi_policy_6ghz_vlp **policy)
+{
+       const struct rtw89_acpi_policy_6ghz_vlp *ptr;
+       u32 buf_len;
+
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+                           "acpi: expect buffer but type: %d\n", obj->type);
+               return -EINVAL;
+       }
+
+       buf_len = obj->buffer.length;
+       if (buf_len < sizeof(*ptr)) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n",
+                           __func__, buf_len);
+               return -EINVAL;
+       }
+
+       ptr = (typeof(ptr))obj->buffer.pointer;
+       if (!chk_acpi_policy_6ghz_vlp_sig(ptr)) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__);
+               return -EINVAL;
+       }
+
+       *policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL);
+       if (!*policy)
+               return -ENOMEM;
+
+       rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_6ghz_vlp: ", *policy,
+                      sizeof(*ptr));
+       return 0;
+}
+
 static bool chk_acpi_policy_tas_sig(const struct rtw89_acpi_policy_tas *p)
 {
        return p->signature[0] == 0x52 &&
@@ -345,6 +389,9 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
        else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP)
                ret = rtw89_acpi_dsm_get_policy_6ghz_sp(rtwdev, obj,
                                                        &res->u.policy_6ghz_sp);
+       else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_VLP_SUP)
+               ret = rtw89_acpi_dsm_get_policy_6ghz_vlp(rtwdev, obj,
+                                                        &res->u.policy_6ghz_vlp);
        else if (func == RTW89_ACPI_DSM_FUNC_TAS_EN)
                ret = rtw89_acpi_dsm_get_policy_tas(rtwdev, obj, &res->u.policy_tas);
        else if (func == RTW89_ACPI_DSM_FUNC_REG_RULES_EN)
index 5811afebc2e6f014ab9487dd48c0edb6ae772f70..48a46f2005b199cecd082f3d77ec5461e7dd5e99 100644 (file)
@@ -20,6 +20,7 @@ enum rtw89_acpi_dsm_func {
        RTW89_ACPI_DSM_FUNC_UNII4_SUP = 6,
        RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP = 7,
        RTW89_ACPI_DSM_FUNC_REG_RULES_EN = 10,
+       RTW89_ACPI_DSM_FUNC_6GHZ_VLP_SUP = 11,
 };
 
 enum rtw89_acpi_conf_unii4 {
@@ -68,6 +69,19 @@ struct rtw89_acpi_policy_6ghz_sp {
        u8 rsvd;
 } __packed;
 
+enum rtw89_acpi_conf_6ghz_vlp {
+       RTW89_ACPI_CONF_6GHZ_VLP_US = BIT(0),
+       RTW89_ACPI_CONF_6GHZ_VLP_CA = BIT(1),
+};
+
+struct rtw89_acpi_policy_6ghz_vlp {
+       u8 signature[4];
+       u8 revision;
+       u8 override;
+       u8 conf;
+       u8 rsvd;
+} __packed;
+
 struct rtw89_acpi_policy_tas {
        u8 signature[4];
        u8 revision;
@@ -93,6 +107,7 @@ struct rtw89_acpi_dsm_result {
                /* caller needs to free it after using */
                struct rtw89_acpi_policy_6ghz *policy_6ghz;
                struct rtw89_acpi_policy_6ghz_sp *policy_6ghz_sp;
+               struct rtw89_acpi_policy_6ghz_vlp *policy_6ghz_vlp;
                struct rtw89_acpi_policy_tas *policy_tas;
                struct rtw89_acpi_policy_reg_rules *policy_reg_rules;
        } u;
index 1d8f89e83c9a3d0ecf01bc3db6bf459b650f30c6..8c53c5db0cbc4f8d063d6b1c59f7052162ebc6f8 100644 (file)
@@ -5386,6 +5386,7 @@ struct rtw89_regulatory_info {
        DECLARE_BITMAP(block_unii4, RTW89_REGD_MAX_COUNTRY_NUM);
        DECLARE_BITMAP(block_6ghz, RTW89_REGD_MAX_COUNTRY_NUM);
        DECLARE_BITMAP(block_6ghz_sp, RTW89_REGD_MAX_COUNTRY_NUM);
+       DECLARE_BITMAP(block_6ghz_vlp, RTW89_REGD_MAX_COUNTRY_NUM);
 };
 
 enum rtw89_ifs_clm_application {
index 42678ff2a0452abfbd168d1e9c4d6378e69a4881..58582f8d2b74c2dbb3930254b865fb06b27fa81b 100644 (file)
@@ -521,6 +521,55 @@ out:
        kfree(ptr);
 }
 
+static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+       const struct rtw89_acpi_policy_6ghz_vlp *ptr = NULL;
+       struct rtw89_acpi_dsm_result res = {};
+       bool enable;
+       u8 index;
+       int ret;
+       u8 val;
+
+       /* By default, allow 6 GHz VLP on all countries except US and CA. */
+       val = ~(RTW89_ACPI_CONF_6GHZ_VLP_US | RTW89_ACPI_CONF_6GHZ_VLP_CA);
+
+       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6GHZ_VLP_SUP, &res);
+       if (ret) {
+               rtw89_debug(rtwdev, RTW89_DBG_REGD,
+                           "acpi: cannot eval policy 6ghz-vlp: %d\n", ret);
+               goto bottom;
+       }
+
+       ptr = res.u.policy_6ghz_vlp;
+
+       switch (ptr->override) {
+       default:
+               rtw89_debug(rtwdev, RTW89_DBG_REGD,
+                           "%s: unknown override case: %d\n", __func__,
+                           ptr->override);
+               fallthrough;
+       case 0:
+               break;
+       case 1:
+               val = ptr->conf;
+               break;
+       }
+
+bottom:
+       index = rtw89_regd_get_index_by_name(rtwdev, "US");
+       enable = u8_get_bits(val, RTW89_ACPI_CONF_6GHZ_VLP_US);
+       if (!enable && index != RTW89_REGD_MAX_COUNTRY_NUM)
+               set_bit(index, regulatory->block_6ghz_vlp);
+
+       index = rtw89_regd_get_index_by_name(rtwdev, "CA");
+       enable = u8_get_bits(val, RTW89_ACPI_CONF_6GHZ_VLP_CA);
+       if (!enable && index != RTW89_REGD_MAX_COUNTRY_NUM)
+               set_bit(index, regulatory->block_6ghz_vlp);
+
+       kfree(ptr);
+}
+
 static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy)
 {
        const struct rtw89_chip_info *chip = rtwdev->chip;
@@ -564,6 +613,7 @@ bottom:
        if (regd_allow_6ghz) {
                rtw89_regd_setup_policy_6ghz(rtwdev);
                rtw89_regd_setup_policy_6ghz_sp(rtwdev);
+               rtw89_regd_setup_policy_6ghz_vlp(rtwdev);
                return;
        }
 
@@ -1059,7 +1109,16 @@ static int rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev,
                                       struct rtw89_vif_link *rtwvif_link, bool active,
                                       unsigned int *changed)
 {
+       struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+       const struct rtw89_regd *regd = regulatory->regd;
+       bool blocked[NUM_OF_RTW89_REG_6GHZ_POWER] = {};
+       u8 index = rtw89_regd_get_index(rtwdev, regd);
        struct ieee80211_bss_conf *bss_conf;
+       bool dflt = false;
+
+       if (index == RTW89_REGD_MAX_COUNTRY_NUM ||
+           test_bit(index, regulatory->block_6ghz_vlp))
+               blocked[RTW89_REG_6GHZ_POWER_VLP] = true;
 
        rcu_read_lock();
 
@@ -1078,6 +1137,7 @@ static int rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev,
                        break;
                default:
                        rtwvif_link->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT;
+                       dflt = true;
                        break;
                }
        } else {
@@ -1086,6 +1146,14 @@ static int rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev,
 
        rcu_read_unlock();
 
+       if (!dflt && blocked[rtwvif_link->reg_6ghz_power]) {
+               rtw89_debug(rtwdev, RTW89_DBG_REGD,
+                           "%c%c 6 GHz power type-%u is blocked by policy\n",
+                           regd->alpha2[0], regd->alpha2[1],
+                           rtwvif_link->reg_6ghz_power);
+               return -EINVAL;
+       }
+
        *changed += __rtw89_reg_6ghz_power_recalc(rtwdev);
        return 0;
 }