wifi: rtw89: regd: handle policy of 6 GHz according to BIOS
authorZong-Zhe Yang <kevin_yang@realtek.com>
Tue, 14 Nov 2023 09:13:58 +0000 (17:13 +0800)
committerKalle Valo <kvalo@kernel.org>
Wed, 22 Nov 2023 15:48:00 +0000 (17:48 +0200)
According to BIOS configuration of Realtek ACPI DSM function 4,
RTW89_ACPI_DSM_FUNC_6G_BP, we handle the regd policy of 6 GHz.

Policy defines two modes as below.
1. `BLOCK` mode:
    The countries in configured list are blocked.
2. `ALLOW` mode:
    _Only_ the countries in configured list are allowed.
    (i.e. others are all blocked.)

Then, when receiving regulatory notification at runtime, if 6 GHz
is blocked on the country, 6 GHz channels will be disabled.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20231114091359.50664-3-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/regd.c

index 07b529a763963863a6529edb883288ce76bd1bd3..9c9e8427abd5579484beba53a224f882df44aa90 100644 (file)
@@ -4327,9 +4327,12 @@ struct rtw89_regd {
        u8 txpwr_regd[RTW89_BAND_NUM];
 };
 
+#define RTW89_REGD_MAX_COUNTRY_NUM U8_MAX
+
 struct rtw89_regulatory_info {
        const struct rtw89_regd *regd;
        enum rtw89_reg_6ghz_power reg_6ghz_power;
+       DECLARE_BITMAP(block_6ghz, RTW89_REGD_MAX_COUNTRY_NUM);
 };
 
 enum rtw89_ifs_clm_application {
index e86480c625c5cbbb18553283a4af3f94e3d74f75..d9d13887604b90418785e0fbb4f86b927389de46 100644 (file)
@@ -257,7 +257,42 @@ static const struct rtw89_regd rtw89_regd_map[] = {
        COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA),
 };
 
-static const struct rtw89_regd *rtw89_regd_find_reg_by_name(char *alpha2)
+static const char rtw89_alpha2_list_eu[][3] = {
+       "AT",
+       "BE",
+       "CY",
+       "CZ",
+       "DK",
+       "EE",
+       "FI",
+       "FR",
+       "DE",
+       "GR",
+       "HU",
+       "IS",
+       "IE",
+       "IT",
+       "LV",
+       "LI",
+       "LT",
+       "LU",
+       "MT",
+       "MC",
+       "NL",
+       "NO",
+       "PL",
+       "PT",
+       "SK",
+       "SI",
+       "ES",
+       "SE",
+       "CH",
+       "BG",
+       "HR",
+       "RO",
+};
+
+static const struct rtw89_regd *rtw89_regd_find_reg_by_name(const char *alpha2)
 {
        u32 i;
 
@@ -274,6 +309,24 @@ static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)
        return regd == &rtw89_ww_regd;
 }
 
+static u8 rtw89_regd_get_index(const struct rtw89_regd *regd)
+{
+       BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM);
+
+       if (rtw89_regd_is_ww(regd))
+               return RTW89_REGD_MAX_COUNTRY_NUM;
+
+       return regd - rtw89_regd_map;
+}
+
+static u8 rtw89_regd_get_index_by_name(const char *alpha2)
+{
+       const struct rtw89_regd *regd;
+
+       regd = rtw89_regd_find_reg_by_name(alpha2);
+       return rtw89_regd_get_index(regd);
+}
+
 #define rtw89_debug_regd(_dev, _regd, _desc, _argv...) \
 do { \
        typeof(_regd) __r = _regd; \
@@ -335,6 +388,77 @@ bottom:
        sband->n_channels -= 3;
 }
 
+static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block,
+                                          const char *alpha2)
+{
+       struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+       u8 index;
+
+       index = rtw89_regd_get_index_by_name(alpha2);
+       if (index == RTW89_REGD_MAX_COUNTRY_NUM) {
+               rtw89_debug(rtwdev, RTW89_DBG_REGD, "%s: unknown alpha2 %c%c\n",
+                           __func__, alpha2[0], alpha2[1]);
+               return;
+       }
+
+       if (block)
+               set_bit(index, regulatory->block_6ghz);
+       else
+               clear_bit(index, regulatory->block_6ghz);
+}
+
+static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev)
+{
+       struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+       const struct rtw89_acpi_country_code *country;
+       const struct rtw89_acpi_policy_6ghz *ptr;
+       struct rtw89_acpi_dsm_result res = {};
+       bool to_block;
+       int i, j;
+       int ret;
+
+       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_BP, &res);
+       if (ret) {
+               rtw89_debug(rtwdev, RTW89_DBG_REGD,
+                           "acpi: cannot eval policy 6ghz: %d\n", ret);
+               return;
+       }
+
+       ptr = res.u.policy_6ghz;
+
+       switch (ptr->policy_mode) {
+       case RTW89_ACPI_POLICY_BLOCK:
+               to_block = true;
+               break;
+       case RTW89_ACPI_POLICY_ALLOW:
+               to_block = false;
+               /* only below list is allowed; block all first */
+               bitmap_fill(regulatory->block_6ghz, RTW89_REGD_MAX_COUNTRY_NUM);
+               break;
+       default:
+               rtw89_debug(rtwdev, RTW89_DBG_REGD,
+                           "%s: unknown policy mode: %d\n", __func__,
+                           ptr->policy_mode);
+               goto out;
+       }
+
+       for (i = 0; i < ptr->country_count; i++) {
+               country = &ptr->country_list[i];
+               if (memcmp("EU", country->alpha2, 2) != 0) {
+                       __rtw89_regd_setup_policy_6ghz(rtwdev, to_block,
+                                                      country->alpha2);
+                       continue;
+               }
+
+               for (j = 0; j < ARRAY_SIZE(rtw89_alpha2_list_eu); j++)
+                       __rtw89_regd_setup_policy_6ghz(rtwdev, to_block,
+                                                      rtw89_alpha2_list_eu[j]);
+       }
+
+out:
+       kfree(ptr);
+}
+
 static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy)
 {
        const struct rtw89_chip_info *chip = rtwdev->chip;
@@ -375,8 +499,10 @@ bottom:
        rtw89_debug(rtwdev, RTW89_DBG_REGD, "regd: allow 6ghz: %d\n",
                    regd_allow_6ghz);
 
-       if (regd_allow_6ghz)
+       if (regd_allow_6ghz) {
+               rtw89_regd_setup_policy_6ghz(rtwdev);
                return;
+       }
 
        sband = wiphy->bands[NL80211_BAND_6GHZ];
        if (!sband)
@@ -436,6 +562,33 @@ int rtw89_regd_init(struct rtw89_dev *rtwdev,
        return 0;
 }
 
+static void rtw89_regd_apply_policy_6ghz(struct rtw89_dev *rtwdev,
+                                        struct wiphy *wiphy)
+{
+       struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+       const struct rtw89_regd *regd = regulatory->regd;
+       struct ieee80211_supported_band *sband;
+       u8 index;
+       int i;
+
+       index = rtw89_regd_get_index(regd);
+       if (index == RTW89_REGD_MAX_COUNTRY_NUM)
+               return;
+
+       if (!test_bit(index, regulatory->block_6ghz))
+               return;
+
+       rtw89_debug(rtwdev, RTW89_DBG_REGD, "%c%c 6 GHz is blocked by policy\n",
+                   regd->alpha2[0], regd->alpha2[1]);
+
+       sband = wiphy->bands[NL80211_BAND_6GHZ];
+       if (!sband)
+               return;
+
+       for (i = 0; i < sband->n_channels; i++)
+               sband->channels[i].flags |= IEEE80211_CHAN_DISABLED;
+}
+
 static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
                                      struct wiphy *wiphy,
                                      struct regulatory_request *request)
@@ -450,6 +603,8 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
                wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
        else
                wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
+
+       rtw89_regd_apply_policy_6ghz(rtwdev, wiphy);
 }
 
 void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request)