summaryrefslogtreecommitdiffstats
path: root/src/drivers/wifi/generic/mtcl.c
blob: 4604f8dd1c404d90b16b0bd4bc7c1830d982f256 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* 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);
}