blob: 4604f8dd1c404d90b16b0bd4bc7c1830d982f256 [file] [log] [blame]
David Ruthea8330e2023-12-06 21:39:54 +00001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <acpi/acpigen.h>
4#include <cbfs.h>
5#include <mtcl.h>
6#include <stdint.h>
7#include <string.h>
8
9#define WIFI_MTCL_CBFS_DEFAULT_FILENAME "wifi_mtcl.bin"
10#define MAX_VERSION 2
11#define MAX_SUPPORT_STATE 2
12#define COUNTRY_LIST_SIZE 6
13#define NAME_SIZE 4
14#define MTCL_NAME "MTCL"
15
16/*
17 * Represent the structured MTCL data.
18 * This struct is used to cast from an array of uint8_t in order to help
19 * understand the semantic purpose of individual bytes. This struct is used in
20 * order to verify that the bytes included match the known MTCL data format.
21 * This struct is explicitly __packed because it is explicitly cast to from an
22 * array of uint8_t.
23 */
24struct wifi_mtcl {
25 uint8_t name[NAME_SIZE];
26 uint8_t revision;
27 uint8_t support_6ghz;
28 uint8_t country_list_6ghz[COUNTRY_LIST_SIZE];
29 uint8_t support_5p9ghz;
30 uint8_t country_list_5p9ghz[COUNTRY_LIST_SIZE];
31} __packed;
32
33void write_mtcl_aml(uint8_t *bytes, size_t count);
34int validate_mtcl(uint8_t *mtcl_bytes, size_t count);
35
36/*
37 * Generate ACPI AML code for MTCL method.
38 * This function takes as input an array of bytes that correspond to the value
39 * map to be passed as a package, as well as the count of bytes to be written.
40 *
41 * AML code generate would look like:
42 * Method(MTCL, 0, Serialized)
43 * {
44 * Name (LIST, Package()
45 * {
46 * // data table
47 * })
48 * Return (LIST)
49 * }
50 */
51void write_mtcl_aml(uint8_t *bytes, size_t count)
52{
53 /* Method (MTCL, 0, Serialized) */
54 acpigen_write_method_serialized("MTCL", 0x0);
55
56 /* Name (LIST */
57 acpigen_write_name("LIST");
58
59 /* Package () */
60 acpigen_write_package(count);
61
62 /* Write the provided bytes. */
63 for (int i = 0; i < count; ++i)
64 acpigen_write_byte(bytes[i]);
65
66 acpigen_write_package_end(); /* Package */
67
68 /* Return MTCL */
69 acpigen_write_return_namestr("LIST");
70 acpigen_write_method_end(); /* Method MTCL */
71}
72
73/*
74 * Validate the WiFi MTCL data that is passed in from CBFS.
75 *
76 * Data is expected in the format:
77 * [Revision,
78 * 6GHz Support,
79 * 6GHz Country List,
80 * 5.9GHz Support,
81 * 5.9GHz Country List]
82 *
83 * The revision is expected to be "2".
84 *
85 * 6GHz support is a byte with the following states:
86 * - 0 - 6GHz operation disabled
87 * - 1 - 6GHz operation dictated by the country list and Operating System
88 * - 2 - 6GHz operation dictated by the Operating System
89 *
90 * 6GHz Country List is a set of 6 bytes that represent a bitmask of countries
91 * in which 6GHz operation is enabled.
92 *
93 * 5.9GHz Support is a byte with the following known states:
94 * - 0 - 5.9GHz operation disabled
95 * - 1 - 5.9GHz operation dictated by the country list and Operating System
96 * - 2 - 5.9GHz operation dictated by the Operating System
97 *
98 * 5.9GHz Country List is a set of 6 bytes that represent a bitmask of countries
99 * in which 5.9GHz operation is enabled
100 *
101 * Validation:
102 * - Verify that there are MTCL_SIZE bytes.
103 * - Verify that the name is MTCL_NAME.
104 * - Verify that the version is less than or equal to MAX_MTCL_VERSION.
105 * - Verify that the support bytes are less than or equal to the
106 * MAX_SUPPORT_STATE.
107 *
108 * Returns 0 for a valid file, -1 for an invalid file.
109 */
110int validate_mtcl(uint8_t *mtcl_bytes, size_t count)
111{
112 if (!mtcl_bytes) {
113 printk(BIOS_ERR, "Failed to get the %s file size!\n",
114 WIFI_MTCL_CBFS_DEFAULT_FILENAME);
115 return -1;
116 }
117
118 if (count != sizeof(struct wifi_mtcl)) {
119 printk(BIOS_ERR, "Size of file read was: %zu, expected: %zu\n",
120 count, sizeof(struct wifi_mtcl));
121 return -1;
122 }
123
124 struct wifi_mtcl *mtcl = (struct wifi_mtcl *)mtcl_bytes;
125
126 if (strncmp(((char *)mtcl->name), MTCL_NAME, NAME_SIZE)) {
127 printk(BIOS_ERR, "MTCL string not present but expected\n");
128 return -1;
129 }
130
131 if (mtcl->revision > MAX_VERSION) {
132 printk(BIOS_ERR, "MTCL version too high\n");
133 return -1;
134 }
135
136 if (mtcl->support_6ghz > MAX_SUPPORT_STATE) {
137 printk(BIOS_ERR, "MTCL 6GHz support state too high\n");
138 return -1;
139 }
140
141 if (mtcl->support_5p9ghz > MAX_SUPPORT_STATE) {
142 printk(BIOS_ERR, "MTCL 5.9GHz support state too high\n");
143 return -1;
144 }
145
146 return 0;
147}
148
149/*
150 * Retrieve WiFi MTCL data from CBFS, decode it, validate it and write it to
151 * AML.
152 *
153 * Returns the number of bytes read.
154 */
155void write_mtcl_function(void)
156{
157 size_t mtcl_bin_len;
158 uint8_t *mtcl_bin;
159
160 mtcl_bin = cbfs_map(WIFI_MTCL_CBFS_DEFAULT_FILENAME, &mtcl_bin_len);
161
162 if (validate_mtcl(mtcl_bin, mtcl_bin_len) == 0)
163 write_mtcl_aml(mtcl_bin, mtcl_bin_len);
164
165 cbfs_unmap(mtcl_bin);
166}