/* SPDX-License-Identifier: GPL-2.0-only */ #include <acpi/acpigen.h> #include <cbfs.h> #include <mtcl.h> #include <stdint.h> #include <string.h> #define WIFI_MTCL_CBFS_DEFAULT_FILENAME "wifi_mtcl.bin" #define MAX_VERSION 2 #define MAX_SUPPORT_STATE 2 #define COUNTRY_LIST_SIZE 6 #define NAME_SIZE 4 #define MTCL_NAME "MTCL" /* * Represent the structured MTCL data. * This struct is used to cast from an array of uint8_t in order to help * understand the semantic purpose of individual bytes. This struct is used in * order to verify that the bytes included match the known MTCL data format. * This struct is explicitly __packed because it is explicitly cast to from an * array of uint8_t. */ struct wifi_mtcl { uint8_t name[NAME_SIZE]; uint8_t revision; uint8_t support_6ghz; uint8_t country_list_6ghz[COUNTRY_LIST_SIZE]; uint8_t support_5p9ghz; uint8_t country_list_5p9ghz[COUNTRY_LIST_SIZE]; } __packed; void write_mtcl_aml(uint8_t *bytes, size_t count); int validate_mtcl(uint8_t *mtcl_bytes, size_t count); /* * Generate ACPI AML code for MTCL method. * This function takes as input an array of bytes that correspond to the value * map to be passed as a package, as well as the count of bytes to be written. * * AML code generate would look like: * Method(MTCL, 0, Serialized) * { * Name (LIST, Package() * { * // data table * }) * Return (LIST) * } */ void write_mtcl_aml(uint8_t *bytes, size_t count) { /* Method (MTCL, 0, Serialized) */ acpigen_write_method_serialized("MTCL", 0x0); /* Name (LIST */ acpigen_write_name("LIST"); /* Package () */ acpigen_write_package(count); /* Write the provided bytes. */ for (int i = 0; i < count; ++i) acpigen_write_byte(bytes[i]); acpigen_write_package_end(); /* Package */ /* Return MTCL */ acpigen_write_return_namestr("LIST"); acpigen_write_method_end(); /* Method MTCL */ } /* * Validate the WiFi MTCL data that is passed in from CBFS. * * Data is expected in the format: * [Revision, * 6GHz Support, * 6GHz Country List, * 5.9GHz Support, * 5.9GHz Country List] * * The revision is expected to be "2". * * 6GHz support is a byte with the following states: * - 0 - 6GHz operation disabled * - 1 - 6GHz operation dictated by the country list and Operating System * - 2 - 6GHz operation dictated by the Operating System * * 6GHz Country List is a set of 6 bytes that represent a bitmask of countries * in which 6GHz operation is enabled. * * 5.9GHz Support is a byte with the following known states: * - 0 - 5.9GHz operation disabled * - 1 - 5.9GHz operation dictated by the country list and Operating System * - 2 - 5.9GHz operation dictated by the Operating System * * 5.9GHz Country List is a set of 6 bytes that represent a bitmask of countries * in which 5.9GHz operation is enabled * * Validation: * - Verify that there are MTCL_SIZE bytes. * - Verify that the name is MTCL_NAME. * - Verify that the version is less than or equal to MAX_MTCL_VERSION. * - Verify that the support bytes are less than or equal to the * MAX_SUPPORT_STATE. * * Returns 0 for a valid file, -1 for an invalid file. */ int validate_mtcl(uint8_t *mtcl_bytes, size_t count) { if (!mtcl_bytes) { printk(BIOS_ERR, "Failed to get the %s file size!\n", WIFI_MTCL_CBFS_DEFAULT_FILENAME); return -1; } if (count != sizeof(struct wifi_mtcl)) { printk(BIOS_ERR, "Size of file read was: %zu, expected: %zu\n", count, sizeof(struct wifi_mtcl)); return -1; } struct wifi_mtcl *mtcl = (struct wifi_mtcl *)mtcl_bytes; if (strncmp(((char *)mtcl->name), MTCL_NAME, NAME_SIZE)) { printk(BIOS_ERR, "MTCL string not present but expected\n"); return -1; } if (mtcl->revision > MAX_VERSION) { printk(BIOS_ERR, "MTCL version too high\n"); return -1; } if (mtcl->support_6ghz > MAX_SUPPORT_STATE) { printk(BIOS_ERR, "MTCL 6GHz support state too high\n"); return -1; } if (mtcl->support_5p9ghz > MAX_SUPPORT_STATE) { printk(BIOS_ERR, "MTCL 5.9GHz support state too high\n"); return -1; } return 0; } /* * Retrieve WiFi MTCL data from CBFS, decode it, validate it and write it to * AML. * * Returns the number of bytes read. */ void write_mtcl_function(void) { size_t mtcl_bin_len; uint8_t *mtcl_bin; mtcl_bin = cbfs_map(WIFI_MTCL_CBFS_DEFAULT_FILENAME, &mtcl_bin_len); if (validate_mtcl(mtcl_bin, mtcl_bin_len) == 0) write_mtcl_aml(mtcl_bin, mtcl_bin_len); cbfs_unmap(mtcl_bin); }