wifi: rtw89: acpi: process 6 GHz band policy from DSM
authorZong-Zhe Yang <kevin_yang@realtek.com>
Tue, 14 Nov 2023 09:13:57 +0000 (17:13 +0800)
committerKalle Valo <kvalo@kernel.org>
Wed, 22 Nov 2023 15:48:00 +0000 (17:48 +0200)
Realtek ACPI DSM func 4, RTW89_ACPI_DSM_FUNC_6G_BP,
accepts a configuration via ACPI buffer as below.

| index | description   |
-------------------------
| [0-2] | signature     |
| [3]   | reserved      |
| [4]   | policy mode   |
| [5]   | country count |
| [6-]  | country list  |

Through this function, BIOS can indicate to allow/block
6 GHz on some specific countries. Still, driver should
follow regd first before taking this configuration into
account.

Besides, add a bit in debug mask for ACPI.

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-2-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/acpi.c
drivers/net/wireless/realtek/rtw89/acpi.h
drivers/net/wireless/realtek/rtw89/debug.h
drivers/net/wireless/realtek/rtw89/regd.c
drivers/net/wireless/realtek/rtw89/sar.c

index 8aaf83a2a6b48d0f3bbbfa457862f724a5a990ae..2e7326a8e3e47b597653b58061f46df6f6ac60ef 100644 (file)
@@ -12,27 +12,74 @@ static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00,
                                           0x82, 0xBD, 0xFE, 0x86,
                                           0x07, 0x80, 0x3A, 0xA7);
 
-static int rtw89_acpi_dsm_get(struct rtw89_dev *rtwdev, union acpi_object *obj,
-                             u8 *value)
+static
+int rtw89_acpi_dsm_get_value(struct rtw89_dev *rtwdev, union acpi_object *obj,
+                            u8 *value)
 {
-       switch (obj->type) {
-       case ACPI_TYPE_INTEGER:
-               *value = (u8)obj->integer.value;
-               break;
-       case ACPI_TYPE_BUFFER:
-               *value = obj->buffer.pointer[0];
-               break;
-       default:
-               rtw89_debug(rtwdev, RTW89_DBG_UNEXP,
-                           "acpi dsm return unhandled type: %d\n", obj->type);
+       if (obj->type != ACPI_TYPE_INTEGER) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+                           "acpi: expect integer but type: %d\n", obj->type);
                return -EINVAL;
        }
 
+       *value = (u8)obj->integer.value;
+       return 0;
+}
+
+static bool chk_acpi_policy_6ghz_sig(const struct rtw89_acpi_policy_6ghz *p)
+{
+       return p->signature[0] == 0x00 &&
+              p->signature[1] == 0xE0 &&
+              p->signature[2] == 0x4C;
+}
+
+static
+int rtw89_acpi_dsm_get_policy_6ghz(struct rtw89_dev *rtwdev,
+                                  union acpi_object *obj,
+                                  struct rtw89_acpi_policy_6ghz **policy_6ghz)
+{
+       const struct rtw89_acpi_policy_6ghz *ptr;
+       u32 expect_len;
+       u32 len;
+
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI,
+                           "acpi: expect buffer but type: %d\n", obj->type);
+               return -EINVAL;
+       }
+
+       len = obj->buffer.length;
+       if (len < sizeof(*ptr)) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n",
+                           __func__, len);
+               return -EINVAL;
+       }
+
+       ptr = (typeof(ptr))obj->buffer.pointer;
+       if (!chk_acpi_policy_6ghz_sig(ptr)) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__);
+               return -EINVAL;
+       }
+
+       expect_len = struct_size(ptr, country_list, ptr->country_count);
+       if (len < expect_len) {
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: expect %u but length: %u\n",
+                           __func__, expect_len, len);
+               return -EINVAL;
+       }
+
+       *policy_6ghz = kmemdup(ptr, expect_len, GFP_KERNEL);
+       if (!*policy_6ghz)
+               return -ENOMEM;
+
+       rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_6ghz: ", *policy_6ghz,
+                      expect_len);
        return 0;
 }
 
 int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
-                           enum rtw89_acpi_dsm_func func, u8 *value)
+                           enum rtw89_acpi_dsm_func func,
+                           struct rtw89_acpi_dsm_result *res)
 {
        union acpi_object *obj;
        int ret;
@@ -40,12 +87,16 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
        obj = acpi_evaluate_dsm(ACPI_HANDLE(rtwdev->dev), &rtw89_guid,
                                0, func, NULL);
        if (!obj) {
-               rtw89_debug(rtwdev, RTW89_DBG_UNEXP,
+               rtw89_debug(rtwdev, RTW89_DBG_ACPI,
                            "acpi dsm fail to evaluate func: %d\n", func);
                return -ENOENT;
        }
 
-       ret = rtw89_acpi_dsm_get(rtwdev, obj, value);
+       if (func == RTW89_ACPI_DSM_FUNC_6G_BP)
+               ret = rtw89_acpi_dsm_get_policy_6ghz(rtwdev, obj,
+                                                    &res->u.policy_6ghz);
+       else
+               ret = rtw89_acpi_dsm_get_value(rtwdev, obj, &res->u.value);
 
        ACPI_FREE(obj);
        return ret;
index ed74d8ceb733994b89f2add2e443da4b9a91c414..fe85b40cf07698c54f80fb5994bd28a9859cb2bf 100644 (file)
@@ -15,7 +15,37 @@ enum rtw89_acpi_dsm_func {
        RTW89_ACPI_DSM_FUNC_59G_EN = 6,
 };
 
+enum rtw89_acpi_policy_mode {
+       RTW89_ACPI_POLICY_BLOCK = 0,
+       RTW89_ACPI_POLICY_ALLOW = 1,
+};
+
+struct rtw89_acpi_country_code {
+       /* below are allowed:
+        * * ISO alpha2 country code
+        * * EU for countries in Europe
+        */
+       char alpha2[2];
+} __packed;
+
+struct rtw89_acpi_policy_6ghz {
+       u8 signature[3];
+       u8 rsvd;
+       u8 policy_mode;
+       u8 country_count;
+       struct rtw89_acpi_country_code country_list[] __counted_by(country_count);
+} __packed;
+
+struct rtw89_acpi_dsm_result {
+       union {
+               u8 value;
+               /* caller needs to free it after using */
+               struct rtw89_acpi_policy_6ghz *policy_6ghz;
+       } u;
+};
+
 int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,
-                           enum rtw89_acpi_dsm_func func, u8 *value);
+                           enum rtw89_acpi_dsm_func func,
+                           struct rtw89_acpi_dsm_result *res);
 
 #endif
index 079269bb5251c9f168a7263a84c025b7fd7edcfa..b663ee24555a1510272fc08d083b04b965b36e5c 100644 (file)
@@ -29,6 +29,7 @@ enum rtw89_debug_mask {
        RTW89_DBG_WOW = BIT(18),
        RTW89_DBG_UL_TB = BIT(19),
        RTW89_DBG_CHAN = BIT(20),
+       RTW89_DBG_ACPI = BIT(21),
 
        RTW89_DBG_UNEXP = BIT(31),
 };
index ca99422e600f19b1018ee60744716f7b7b4c1900..e86480c625c5cbbb18553283a4af3f94e3d74f75 100644 (file)
@@ -291,19 +291,22 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,
        const struct rtw89_chip_info *chip = rtwdev->chip;
        bool regd_allow_unii_4 = chip->support_unii4;
        struct ieee80211_supported_band *sband;
+       struct rtw89_acpi_dsm_result res = {};
        int ret;
        u8 val;
 
        if (!chip->support_unii4)
                goto bottom;
 
-       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_59G_EN, &val);
+       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_59G_EN, &res);
        if (ret) {
                rtw89_debug(rtwdev, RTW89_DBG_REGD,
                            "acpi: cannot eval unii 4: %d\n", ret);
                goto bottom;
        }
 
+       val = res.u.value;
+
        rtw89_debug(rtwdev, RTW89_DBG_REGD,
                    "acpi: eval if allow unii 4: %d\n", val);
 
@@ -338,19 +341,22 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy)
        bool chip_support_6ghz = chip->support_bands & BIT(NL80211_BAND_6GHZ);
        bool regd_allow_6ghz = chip_support_6ghz;
        struct ieee80211_supported_band *sband;
+       struct rtw89_acpi_dsm_result res = {};
        int ret;
        u8 val;
 
        if (!chip_support_6ghz)
                goto bottom;
 
-       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_DIS, &val);
+       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_DIS, &res);
        if (ret) {
                rtw89_debug(rtwdev, RTW89_DBG_REGD,
                            "acpi: cannot eval 6ghz: %d\n", ret);
                goto bottom;
        }
 
+       val = res.u.value;
+
        rtw89_debug(rtwdev, RTW89_DBG_REGD,
                    "acpi: eval if disallow 6ghz: %d\n", val);
 
index aed05b026c6c32c23bde647b11305680cb0607d1..1b2a400406ae133d643f40f90a2b67443a478aad 100644 (file)
@@ -404,16 +404,18 @@ out:
 void rtw89_tas_init(struct rtw89_dev *rtwdev)
 {
        struct rtw89_tas_info *tas = &rtwdev->tas;
+       struct rtw89_acpi_dsm_result res = {};
        int ret;
        u8 val;
 
-       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &val);
+       ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res);
        if (ret) {
                rtw89_debug(rtwdev, RTW89_DBG_SAR,
                            "acpi: cannot get TAS: %d\n", ret);
                return;
        }
 
+       val = res.u.value;
        switch (val) {
        case 0:
                tas->enable = false;