wifi: rtw89: regd/acpi: support 6 GHz VLP policy via ACPI DSM

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
This commit is contained in:
Zong-Zhe Yang 2025-07-09 14:50:06 +08:00 committed by Ping-Ke Shih
parent 08fbc2b688
commit b99d7cd36d
4 changed files with 131 additions and 0 deletions

View 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)

View 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;

View 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 {

View File

@ -521,6 +521,55 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)
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 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy)
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;
}