diff options
author | Sugnan Prabhu S <sugnan.prabhu.s@intel.com> | 2021-07-30 20:12:22 +0530 |
---|---|---|
committer | Tim Wawrzynczak <twawrzynczak@chromium.org> | 2021-09-02 22:53:26 +0000 |
commit | fcb4f2d77e4d0c09bf8e433d724ef0ea272df815 (patch) | |
tree | 012d0334a5d7918798dd7aad6139e4356b9faea9 /src | |
parent | 93ca873f209daef0e6a53b6f16e40be3ddb82ee1 (diff) |
wifi: Add support for new revisions of SAR table entries
Existing SAR infrastructure supports only revision 0 of the SAR tables.
This patch modifies it to extend support for intel wifi 6 and wifi 6e
configurations as per the connectivity document:
559910_Intel_Connectivity_Platforms_BIOS_Guidelines_Rev6_4.pdf
The SAR table and WGDS configuration block sizes were static in the
legacy SAR file format. Following is the format of the new binary file.
+------------------------------------------------------------+
| Field | Size | Description |
+------------------------------------------------------------+
| Marker | 4 bytes | "$SAR" |
+------------------------------------------------------------+
| Version | 1 byte | Current version = 1 |
+------------------------------------------------------------+
| SAR table | 2 bytes | Offset of SAR table from start of |
| offset | | the header |
+------------------------------------------------------------+
| WGDS | 2 bytes | Offset of WGDS table from start of |
| offset | | the header |
+------------------------------------------------------------+
| Data | n bytes | Data for the different tables |
+------------------------------------------------------------+
This change supports both the legacy and the new format of SAR file
BUG=b:193665559
TEST=Checked the SSDT entries for WRDS, EWRD and WGDS with different
binaries generated by setting different versions in the config.star
Change-Id: I08c3f321938eba04e8bcff4d87cb215422715bb2
Signed-off-by: Sugnan Prabhu S <sugnan.prabhu.s@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/56750
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/drivers/wifi/generic/acpi.c | 215 | ||||
-rw-r--r-- | src/include/sar.h | 64 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/sar.c | 244 |
3 files changed, 386 insertions, 137 deletions
diff --git a/src/drivers/wifi/generic/acpi.c b/src/drivers/wifi/generic/acpi.c index 3e3b5c1d8e..aa49be35e2 100644 --- a/src/drivers/wifi/generic/acpi.c +++ b/src/drivers/wifi/generic/acpi.c @@ -6,25 +6,14 @@ #include <console/console.h> #include <device/pci_ids.h> #include <sar.h> +#include <stdlib.h> #include <wrdd.h> #include "chip.h" #include "wifi_private.h" -/* WRDS Spec Revision */ -#define WRDS_REVISION 0x0 - -/* EWRD Spec Revision */ -#define EWRD_REVISION 0x0 - -/* WRDS Domain type */ -#define WRDS_DOMAIN_TYPE_WIFI 0x7 - -/* EWRD Domain type */ -#define EWRD_DOMAIN_TYPE_WIFI 0x7 - -/* WGDS Domain type */ -#define WGDS_DOMAIN_TYPE_WIFI 0x7 +/* WIFI Domain type */ +#define DOMAIN_TYPE_WIFI 0x7 /* * WIFI ACPI NAME = "WF" + hex value of last 8 bits of dev_path_encode + '\0' @@ -34,29 +23,33 @@ */ #define WIFI_ACPI_NAME_MAX_LEN 5 -__weak int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits) +__weak int get_wifi_sar_limits(union wifi_sar_limits *sar_limits) { return -1; } -static void emit_sar_acpi_structures(const struct device *dev) +static const uint8_t *sar_fetch_set(const struct sar_profile *sar, size_t set_num) { - int i, j, package_size; - struct wifi_sar_limits sar_limits; - struct wifi_sar_delta_table *wgds; + const uint8_t *sar_table = &sar->sar_table[0]; - /* - * If device type is PCI, ensure that the device has Intel vendor ID. CBFS SAR and SAR - * ACPI tables are currently used only by Intel WiFi devices. - */ - if (dev->path.type == DEVICE_PATH_PCI && dev->vendor != PCI_VENDOR_ID_INTEL) - return; + return sar_table + (sar->chains_count * sar->subbands_count * set_num); +} - /* Retrieve the sar limits data */ - if (get_wifi_sar_limits(&sar_limits) < 0) { - printk(BIOS_DEBUG, "failed from getting SAR limits!\n"); +static const uint8_t *wgds_fetch_set(struct geo_profile *wgds, size_t set_num) +{ + const uint8_t *wgds_table = &wgds->wgds_table[0]; + + return wgds_table + (wgds->bands_count * set_num); +} + +static void sar_emit_wrds(const struct sar_profile *sar) +{ + int i; + size_t package_size, table_size; + const uint8_t *set; + + if (sar == NULL) return; - } /* * Name ("WRDS", Package () { @@ -64,22 +57,42 @@ static void emit_sar_acpi_structures(const struct device *dev) * Package () { * Domain Type, // 0x7:WiFi * WiFi SAR BIOS, // BIOS SAR Enable/disable - * SAR Table Set // Set#1 of SAR Table (10 bytes) + * SAR Table Set // Set#1 of SAR Table * } * }) */ + if (sar->revision > MAX_SAR_REVISION) { + printk(BIOS_ERR, "ERROR: Invalid SAR table revision: %d\n", sar->revision); + return; + } + acpigen_write_name("WRDS"); acpigen_write_package(2); - acpigen_write_dword(WRDS_REVISION); - /* Emit 'Domain Type' + 'WiFi SAR BIOS' + 10 bytes for Set#1 */ - package_size = 1 + 1 + BYTES_PER_SAR_LIMIT; + acpigen_write_dword(sar->revision); + + table_size = sar->chains_count * sar->subbands_count; + /* Emit 'Domain Type' + 'WiFi SAR Enable' + Set#1 */ + package_size = 1 + 1 + table_size; acpigen_write_package(package_size); - acpigen_write_dword(WRDS_DOMAIN_TYPE_WIFI); - acpigen_write_dword(CONFIG(SAR_ENABLE)); - for (i = 0; i < BYTES_PER_SAR_LIMIT; i++) - acpigen_write_byte(sar_limits.sar_limit[0][i]); - acpigen_pop_len(); - acpigen_pop_len(); + acpigen_write_dword(DOMAIN_TYPE_WIFI); + acpigen_write_dword(1); + + set = sar_fetch_set(sar, 0); + for (i = 0; i < table_size; i++) + acpigen_write_byte(set[i]); + + acpigen_write_package_end(); + acpigen_write_package_end(); +} + +static void sar_emit_ewrd(const struct sar_profile *sar) +{ + int i; + size_t package_size, set_num, table_size; + const uint8_t *set; + + if (sar == NULL) + return; /* * Name ("EWRD", Package () { @@ -88,31 +101,58 @@ static void emit_sar_acpi_structures(const struct device *dev) * Domain Type, // 0x7:WiFi * Dynamic SAR Enable, // Dynamic SAR Enable/disable * Extended SAR sets, // Number of optional SAR table sets - * SAR Table Set, // Set#2 of SAR Table (10 bytes) - * SAR Table Set, // Set#3 of SAR Table (10 bytes) - * SAR Table Set // Set#4 of SAR Table (10 bytes) + * SAR Table Set, // Set#2 of SAR Table + * SAR Table Set, // Set#3 of SAR Table + * SAR Table Set // Set#4 of SAR Table * } * }) */ + if (sar->revision > MAX_SAR_REVISION) { + printk(BIOS_ERR, "ERROR: Invalid SAR table revision: %d\n", sar->revision); + return; + } + + if (sar->dsar_set_count == 0) { + printk(BIOS_WARNING, "WARNING: DSAR set count is 0\n"); + return; + } + acpigen_write_name("EWRD"); acpigen_write_package(2); - acpigen_write_dword(EWRD_REVISION); + acpigen_write_dword(sar->revision); + + table_size = sar->chains_count * sar->subbands_count; /* - * Emit 'Domain Type' + "Dynamic SAR Enable' + 'Extended SAR sets' + * Emit 'Domain Type' + 'Dynamic SAR Enable' + 'Extended SAR sets count' * + number of bytes for Set#2 & 3 & 4 */ - package_size = 1 + 1 + 1 + (NUM_SAR_LIMITS - 1) * BYTES_PER_SAR_LIMIT; + package_size = 1 + 1 + 1 + table_size * sar->dsar_set_count; acpigen_write_package(package_size); - acpigen_write_dword(EWRD_DOMAIN_TYPE_WIFI); - acpigen_write_dword(CONFIG(DSAR_ENABLE)); - acpigen_write_dword(CONFIG_DSAR_SET_NUM); - for (i = 1; i < NUM_SAR_LIMITS; i++) - for (j = 0; j < BYTES_PER_SAR_LIMIT; j++) - acpigen_write_byte(sar_limits.sar_limit[i][j]); - acpigen_pop_len(); - acpigen_pop_len(); - - if (!CONFIG(GEO_SAR_ENABLE)) + acpigen_write_dword(DOMAIN_TYPE_WIFI); + acpigen_write_dword(1); + acpigen_write_dword(sar->dsar_set_count); + + for (set_num = 1; set_num <= sar->dsar_set_count; set_num++) { + set = sar_fetch_set(sar, set_num); + for (i = 0; i < table_size; i++) + acpigen_write_byte(set[i]); + } + + /* wifi driver always expects 3 DSAR sets */ + for (i = 0; i < (table_size * (MAX_DSAR_SET_COUNT - sar->dsar_set_count)); i++) + acpigen_write_byte(0); + + acpigen_write_package_end(); + acpigen_write_package_end(); +} + +static void sar_emit_wgds(struct geo_profile *wgds) +{ + int i; + size_t package_size, set_num; + const uint8_t *set; + + if (wgds == NULL) return; /* @@ -126,43 +166,78 @@ static void emit_sar_acpi_structures(const struct device *dev) * WgdsWiFiSarDeltaGroup1PowerMax2, // Group 1 FCC 5200 Max * WgdsWiFiSarDeltaGroup1PowerChainA2, // Group 1 FCC 5200 A Offset * WgdsWiFiSarDeltaGroup1PowerChainB2, // Group 1 FCC 5200 B Offset + * WgdsWiFiSarDeltaGroup1PowerMax3, // Group 1 FCC 6000-7000 Max + * WgdsWiFiSarDeltaGroup1PowerChainA3, // Group 1 FCC 6000-7000 A Offset + * WgdsWiFiSarDeltaGroup1PowerChainB3, // Group 1 FCC 6000-7000 B Offset * WgdsWiFiSarDeltaGroup2PowerMax1, // Group 2 EC Jap 2400 Max * WgdsWiFiSarDeltaGroup2PowerChainA1, // Group 2 EC Jap 2400 A Offset * WgdsWiFiSarDeltaGroup2PowerChainB1, // Group 2 EC Jap 2400 B Offset * WgdsWiFiSarDeltaGroup2PowerMax2, // Group 2 EC Jap 5200 Max * WgdsWiFiSarDeltaGroup2PowerChainA2, // Group 2 EC Jap 5200 A Offset * WgdsWiFiSarDeltaGroup2PowerChainB2, // Group 2 EC Jap 5200 B Offset + * WgdsWiFiSarDeltaGroup2PowerMax3, // Group 2 EC Jap 6000-7000 Max + * WgdsWiFiSarDeltaGroup2PowerChainA3, // Group 2 EC Jap 6000-7000 A Offset + * WgdsWiFiSarDeltaGroup2PowerChainB3, // Group 2 EC Jap 6000-7000 B Offset * WgdsWiFiSarDeltaGroup3PowerMax1, // Group 3 ROW 2400 Max * WgdsWiFiSarDeltaGroup3PowerChainA1, // Group 3 ROW 2400 A Offset * WgdsWiFiSarDeltaGroup3PowerChainB1, // Group 3 ROW 2400 B Offset * WgdsWiFiSarDeltaGroup3PowerMax2, // Group 3 ROW 5200 Max * WgdsWiFiSarDeltaGroup3PowerChainA2, // Group 3 ROW 5200 A Offset * WgdsWiFiSarDeltaGroup3PowerChainB2, // Group 3 ROW 5200 B Offset + * WgdsWiFiSarDeltaGroup3PowerMax3, // Group 3 ROW 6000-7000 Max + * WgdsWiFiSarDeltaGroup3PowerChainA3, // Group 3 ROW 6000-7000 A Offset + * WgdsWiFiSarDeltaGroup3PowerChainB3, // Group 3 ROW 6000-7000 B Offset * } * }) */ + if (wgds->revision > MAX_GEO_OFFSET_REVISION) { + printk(BIOS_ERR, "ERROR: Invalid WGDS revision: %d\n", wgds->revision); + return; + } + + package_size = 1 + wgds->chains_count * wgds->bands_count; - wgds = &sar_limits.wgds; acpigen_write_name("WGDS"); acpigen_write_package(2); - acpigen_write_dword(wgds->version); + acpigen_write_dword(wgds->revision); /* Emit 'Domain Type' + * Group specific delta of power (6 bytes * NUM_WGDS_SAR_GROUPS) */ - package_size = sizeof(sar_limits.wgds.group) + 1; acpigen_write_package(package_size); - acpigen_write_dword(WGDS_DOMAIN_TYPE_WIFI); - for (i = 0; i < SAR_NUM_WGDS_GROUPS; i++) { - acpigen_write_byte(wgds->group[i].power_max_2400mhz); - acpigen_write_byte(wgds->group[i].power_chain_a_2400mhz); - acpigen_write_byte(wgds->group[i].power_chain_b_2400mhz); - acpigen_write_byte(wgds->group[i].power_max_5200mhz); - acpigen_write_byte(wgds->group[i].power_chain_a_5200mhz); - acpigen_write_byte(wgds->group[i].power_chain_b_5200mhz); + acpigen_write_dword(DOMAIN_TYPE_WIFI); + + for (set_num = 0; set_num < wgds->chains_count; set_num++) { + set = wgds_fetch_set(wgds, set_num); + for (i = 0; i < wgds->bands_count; i++) + acpigen_write_byte(set[i]); + } + + acpigen_write_package_end(); + acpigen_write_package_end(); +} + +static void emit_sar_acpi_structures(const struct device *dev) +{ + union wifi_sar_limits sar_limits; + + /* + * If device type is PCI, ensure that the device has Intel vendor ID. CBFS SAR and SAR + * ACPI tables are currently used only by Intel WiFi devices. + */ + if (dev->path.type == DEVICE_PATH_PCI && dev->vendor != PCI_VENDOR_ID_INTEL) + return; + + /* Retrieve the sar limits data */ + if (get_wifi_sar_limits(&sar_limits) < 0) { + printk(BIOS_ERR, "ERROR: failed getting SAR limits!\n"); + return; } - acpigen_pop_len(); - acpigen_pop_len(); + sar_emit_wrds(sar_limits.sar); + sar_emit_ewrd(sar_limits.sar); + sar_emit_wgds(sar_limits.wgds); + + free(sar_limits.sar); } static void wifi_ssdt_write_device(const struct device *dev, const char *path) @@ -197,7 +272,7 @@ static void wifi_ssdt_write_properties(const struct device *dev, const char *sco * Name ("WRDD", Package () { * WRDD_REVISION, // Revision * Package () { - * WRDD_DOMAIN_TYPE_WIFI, // Domain Type, 7:WiFi + * DOMAIN_TYPE_WIFI, // Domain Type, 7:WiFi * wifi_regulatory_domain() // Country Identifier * } * }) @@ -206,7 +281,7 @@ static void wifi_ssdt_write_properties(const struct device *dev, const char *sco acpigen_write_package(2); acpigen_write_integer(WRDD_REVISION); acpigen_write_package(2); - acpigen_write_dword(WRDD_DOMAIN_TYPE_WIFI); + acpigen_write_dword(DOMAIN_TYPE_WIFI); acpigen_write_dword(wifi_regulatory_domain()); acpigen_pop_len(); acpigen_pop_len(); diff --git a/src/include/sar.h b/src/include/sar.h index 78a1b09f2b..625fd51648 100644 --- a/src/include/sar.h +++ b/src/include/sar.h @@ -4,42 +4,54 @@ #include <stdint.h> -#define NUM_SAR_LIMITS 4 -#define BYTES_PER_SAR_LIMIT 10 -enum { - SAR_FCC, - SAR_EUROPE_JAPAN, - SAR_REST_OF_WORLD, - SAR_NUM_WGDS_GROUPS -}; +#define MAX_DSAR_SET_COUNT 3 +#define MAX_GEO_OFFSET_REVISION 3 +#define MAX_PROFILE_COUNT 2 +#define MAX_SAR_REVISION 2 +#define REVISION_SIZE 1 +#define SAR_REV0_CHAINS_COUNT 2 +#define SAR_REV0_SUBBANDS_COUNT 5 +#define SAR_FILE_REVISION 1 +#define SAR_STR_PREFIX "$SAR" +#define SAR_STR_PREFIX_SIZE 4 + +struct geo_profile { + uint8_t revision; + uint8_t chains_count; + uint8_t bands_count; + uint8_t wgds_table[0]; +} __packed; + +struct sar_profile { + uint8_t revision; + uint8_t dsar_set_count; + uint8_t chains_count; + uint8_t subbands_count; + uint8_t sar_table[0]; +} __packed; -struct wifi_sar_delta_table { +struct sar_header { + char marker[SAR_STR_PREFIX_SIZE]; uint8_t version; - struct { - uint8_t power_max_2400mhz; - uint8_t power_chain_a_2400mhz; - uint8_t power_chain_b_2400mhz; - uint8_t power_max_5200mhz; - uint8_t power_chain_a_5200mhz; - uint8_t power_chain_b_5200mhz; - } __packed group[SAR_NUM_WGDS_GROUPS]; + uint16_t offsets[0]; } __packed; /* Wifi SAR limit table structure */ -struct wifi_sar_limits { - /* Total 4 SAR limit sets, each has 10 bytes */ - uint8_t sar_limit[NUM_SAR_LIMITS][BYTES_PER_SAR_LIMIT]; - struct wifi_sar_delta_table wgds; -} __packed; +union wifi_sar_limits { + struct { + struct sar_profile *sar; + struct geo_profile *wgds; + }; + void *profile[MAX_PROFILE_COUNT]; +}; /* - * Retrieve the SAR limits data from VPD and decode it. + * Retrieve the wifi ACPI configuration data from CBFS and decode it * sar_limits: Pointer to wifi_sar_limits where the resulted data is stored * - * Returns: 0 on success, -1 on errors (The VPD entry doesn't exist, or the - * VPD entry contains non-heximal value.) + * Returns: 0 on success, -1 on errors (The .hex file doesn't exist, or the decode failed) */ -int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits); +int get_wifi_sar_limits(union wifi_sar_limits *sar_limits); #define WIFI_SAR_CBFS_DEFAULT_FILENAME "wifi_sar_defaults.hex" diff --git a/src/vendorcode/google/chromeos/sar.c b/src/vendorcode/google/chromeos/sar.c index a38ffe270b..d6cb021412 100644 --- a/src/vendorcode/google/chromeos/sar.c +++ b/src/vendorcode/google/chromeos/sar.c @@ -9,79 +9,241 @@ #include <string.h> #include <types.h> +#define LEGACY_BYTES_PER_GEO_OFFSET 6 +#define LEGACY_BYTES_PER_SAR_LIMIT 10 +#define LEGACY_NUM_SAR_LIMITS 4 +#define LEGACY_SAR_BIN_SIZE 81 +#define LEGACY_SAR_WGDS_BIN_SIZE 119 +#define LEGACY_SAR_NUM_WGDS_GROUPS 3 + +static uint8_t *wifi_hextostr(const char *sar_str, size_t str_len, size_t *sar_bin_len, + bool legacy_hex_format) +{ + uint8_t *sar_bin = NULL; + size_t bin_len; + + if (!legacy_hex_format) { + sar_bin = malloc(str_len); + if (!sar_bin) { + printk(BIOS_ERR, "ERROR: Failed to allocate space for SAR binary!\n"); + return NULL; + } + + memcpy(sar_bin, sar_str, str_len); + *sar_bin_len = str_len; + } else { + bin_len = ((str_len - 1) / 2); + sar_bin = malloc(bin_len); + if (!sar_bin) { + printk(BIOS_ERR, "ERROR: Failed to allocate space for SAR binary!\n"); + return NULL; + } + + if (hexstrtobin(sar_str, (uint8_t *)sar_bin, bin_len) != bin_len) { + printk(BIOS_ERR, "ERROR: sar_limits contains non-hex value!\n"); + free(sar_bin); + return NULL; + } + + *sar_bin_len = bin_len; + } + + return sar_bin; +} + +static int sar_table_size(const struct sar_profile *sar) +{ + if (sar == NULL) + return 0; + + return (sizeof(struct sar_profile) + ((1 + sar->dsar_set_count) * sar->chains_count * + sar->subbands_count)); +} + +static int wgds_table_size(const struct geo_profile *geo) +{ + if (geo == NULL) + return 0; + + return sizeof(struct geo_profile) + (geo->chains_count * geo->bands_count); +} + +static bool valid_legacy_length(size_t bin_len) +{ + if (bin_len == LEGACY_SAR_WGDS_BIN_SIZE) + return true; + + if (bin_len == LEGACY_SAR_BIN_SIZE && !CONFIG(GEO_SAR_ENABLE)) + return true; + + return false; +} + +static int sar_header_size(void) +{ + return (MAX_PROFILE_COUNT * sizeof(uint16_t)) + sizeof(struct sar_header); +} + +static int fill_wifi_sar_limits(union wifi_sar_limits *sar_limits, const uint8_t *sar_bin, + size_t sar_bin_size) +{ + struct sar_header *header; + size_t i = 0, expected_sar_bin_size; + size_t header_size = sar_header_size(); + + if (sar_bin_size < header_size) { + printk(BIOS_ERR, "ERROR: Invalid SAR format!\n"); + return -1; + } + + header = (struct sar_header *)sar_bin; + + if (header->version != SAR_FILE_REVISION) { + printk(BIOS_ERR, "ERROR: Invalid SAR file version: %d!\n", header->version); + return -1; + } + + for (i = 0; i < MAX_PROFILE_COUNT; i++) { + if (header->offsets[i] > sar_bin_size) { + printk(BIOS_ERR, "ERROR: Offset is outside the file size!\n"); + return -1; + } + + if (header->offsets[i]) + sar_limits->profile[i] = (void *) (sar_bin + header->offsets[i]); + } + + expected_sar_bin_size = header_size; + expected_sar_bin_size += sar_table_size(sar_limits->sar); + expected_sar_bin_size += wgds_table_size(sar_limits->wgds); + + if (sar_bin_size != expected_sar_bin_size) { + printk(BIOS_ERR, "ERROR: Invalid SAR size, expected: %ld, obtained: %ld\n", + expected_sar_bin_size, sar_bin_size); + return -1; + } + + return 0; +} + +static int fill_wifi_sar_limits_legacy(union wifi_sar_limits *sar_limits, + const uint8_t *sar_bin, size_t sar_bin_size) +{ + uint8_t *new_sar_bin; + size_t size = sar_bin_size + sizeof(struct sar_profile); + + if (CONFIG(GEO_SAR_ENABLE)) + size += sizeof(struct geo_profile); + + new_sar_bin = malloc(size); + if (!new_sar_bin) { + printk(BIOS_ERR, "ERROR: Failed to allocate space for SAR binary!\n"); + return -1; + } + + sar_limits->sar = (struct sar_profile *) new_sar_bin; + sar_limits->sar->revision = 0; + sar_limits->sar->dsar_set_count = CONFIG_DSAR_SET_NUM; + sar_limits->sar->chains_count = SAR_REV0_CHAINS_COUNT; + sar_limits->sar->subbands_count = SAR_REV0_SUBBANDS_COUNT; + memcpy(&sar_limits->sar->sar_table, sar_bin, + LEGACY_BYTES_PER_SAR_LIMIT * LEGACY_NUM_SAR_LIMITS); + + if (!CONFIG(GEO_SAR_ENABLE)) + return 0; + + sar_limits->wgds = (struct geo_profile *)(new_sar_bin + + sar_table_size(sar_limits->sar)); + sar_limits->wgds->revision = 0; + sar_limits->wgds->chains_count = LEGACY_SAR_NUM_WGDS_GROUPS; + sar_limits->wgds->bands_count = LEGACY_BYTES_PER_GEO_OFFSET; + memcpy(&sar_limits->wgds->wgds_table, + sar_bin + LEGACY_BYTES_PER_SAR_LIMIT * LEGACY_NUM_SAR_LIMITS + REVISION_SIZE, + LEGACY_BYTES_PER_GEO_OFFSET * LEGACY_SAR_NUM_WGDS_GROUPS); + + return 0; +} + /* * Retrieve WiFi SAR limits data from CBFS and decode it - * WiFi SAR data is expected in the format: [<WRDD><EWRD>][WGDS] + * Legacy WiFi SAR data is expected in the format: [<WRDD><EWRD>][WGDS] * * [<WRDD><EWRD>] = NUM_SAR_LIMITS * BYTES_PER_SAR_LIMIT bytes. - * [WGDS]=[WGDS_VERSION][WGDS_DATA] + * [WGDS]=[WGDS_REVISION][WGDS_DATA] + * + * Current SAR configuration data is expected in the format: + * "$SAR" Marker + * Version + * Offset count + * Offsets + * [SAR_REVISION,DSAR_SET_COUNT,CHAINS_COUNT,SUBBANDS_COUNT <WRDD>[EWRD]] + * [WGDS_REVISION,CHAINS_COUNT,SUBBANDS_COUNT<WGDS_DATA>] + * + * The configuration data will always have the revision added in the file for each of the + * block, based on the revision number and validity, size of the specific block will be + * calculated. * - * For [WGDS_VERSION] 0x00, * [WGDS_DATA] = [GROUP#0][GROUP#1][GROUP#2] * * [GROUP#<i>] = - * [2.4Ghz – Max Allowed][2.4Ghz – Chain A Offset] - * [2.4Ghz – Chain B Offset][5Ghz – Max Allowed] - * [5Ghz – Chain A Offset][5Ghz – Chain B Offset] + * Supported by Revision 0, 1 and 2 + * [2.4Ghz - Max Allowed][2.4Ghz - Chain A Offset][2.4Ghz - Chain B Offset] + * [5Ghz - Max Allowed][5Ghz - Chain A Offset][5Ghz - Chain B Offset] + * Supported by Revision 1 and 2 + * [6Ghz - Max Allowed][6Ghz - Chain A Offset][6Ghz - Chain B Offset] * * [GROUP#0] is for FCC * [GROUP#1] is for Europe/Japan * [GROUP#2] is for ROW -*/ -int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits) + */ +int get_wifi_sar_limits(union wifi_sar_limits *sar_limits) { const char *filename; - size_t sar_str_len, sar_bin_len; + size_t sar_bin_len, sar_str_len; + uint8_t *sar_bin; char *sar_str; int ret = -1; + bool legacy_hex_format = false; filename = get_wifi_sar_cbfs_filename(); if (filename == NULL) { - printk(BIOS_DEBUG, "Filename missing for CBFS SAR file!\n"); + printk(BIOS_ERR, "ERROR: Filename missing for CBFS SAR file!\n"); return ret; } - /* - * If GEO_SAR_ENABLE is not selected, SAR file does not contain - * delta table settings. - */ - if (CONFIG(GEO_SAR_ENABLE)) - sar_bin_len = sizeof(struct wifi_sar_limits); - else - sar_bin_len = sizeof(struct wifi_sar_limits) - - sizeof(struct wifi_sar_delta_table); - - /* - * Each hex digit is represented as a character in CBFS SAR file. Thus, - * the SAR file is double the size of its binary buffer equivalent. - * Hence, the buffer size allocated for SAR file is: - * `2 * sar_bin_len + 1` - * 1 additional byte is allocated to store the terminating '\0'. - */ - sar_str_len = 2 * sar_bin_len + 1; - sar_str = malloc(sar_str_len); - + sar_str = cbfs_map(filename, &sar_str_len); if (!sar_str) { - printk(BIOS_ERR, "Failed to allocate space for SAR string!\n"); + printk(BIOS_ERR, "ERROR: Failed to get the %s file size!\n", filename); return ret; } - printk(BIOS_DEBUG, "Checking CBFS for default SAR values\n"); + if (strncmp(sar_str, SAR_STR_PREFIX, SAR_STR_PREFIX_SIZE) == 0) { + legacy_hex_format = false; + } else if (valid_legacy_length(sar_str_len)) { + legacy_hex_format = true; + } else { + printk(BIOS_ERR, "ERROR: Invalid SAR format!\n"); + goto error; + } - if (cbfs_load(filename, sar_str, sar_str_len) != sar_str_len) { - printk(BIOS_ERR, "%s has bad len in CBFS\n", filename); - goto done; + sar_bin = wifi_hextostr(sar_str, sar_str_len, &sar_bin_len, legacy_hex_format); + if (sar_bin == NULL) { + printk(BIOS_ERR, "ERROR: Failed to parse SAR file %s\n", filename); + goto error; } memset(sar_limits, 0, sizeof(*sar_limits)); - if (hexstrtobin(sar_str, (uint8_t *)sar_limits, sar_bin_len) != sar_bin_len) { - printk(BIOS_ERR, "Error: wifi_sar contains non-hex value!\n"); - goto done; + if (legacy_hex_format) { + ret = fill_wifi_sar_limits_legacy(sar_limits, sar_bin, sar_bin_len); + free(sar_bin); + } else { + ret = fill_wifi_sar_limits(sar_limits, sar_bin, sar_bin_len); + if (ret < 0) + free(sar_bin); } - ret = 0; -done: - free(sar_str); +error: + cbfs_unmap(sar_str); return ret; } |