aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/google/chromeos/sar.c
blob: 01de60c835dcfa4299b5dd3427521daf6320bfbc (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
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2017-2018 Intel Corp.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <cbfs.h>
#include <console/console.h>
#include <lib.h>
#include <types.h>
#include <string.h>
#include <sar.h>
#include <drivers/vpd/vpd.h>

#define WIFI_SAR_CBFS_FILENAME	"wifi_sar_defaults.hex"
#define CROS_VPD_WIFI_SAR_NAME	"wifi_sar"

static int load_sar_file_from_cbfs(void *buf, size_t buffer_size)
{
	const char *filename = get_wifi_sar_cbfs_filename();
	if (filename == NULL)
		filename = WIFI_SAR_CBFS_FILENAME;
	return cbfs_boot_load_file(filename, buf,
			buffer_size, CBFS_TYPE_RAW);
}

/* Retrieve the wifi SAR limits data from VPD and decode it

For VPD: key,value pair is in this format
"wifi_sar"=[<WRDD><EWRD>][WGDS]

WIFI SAR data in CBFS file is expected in same format: [<WRDD><EWRD>][WGDS]

[<WRDD><EWRD>] = NUM_SAR_LIMITS * BYTES_PER_SAR_LIMIT bytes.
[WGDS]=[WGDS_VERSION][WGDS_DATA]

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]

[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)
{
	const char *wifi_sar_limit_key = CROS_VPD_WIFI_SAR_NAME;
	/* vpd_gets() reads in one less than size characters from the VPD
	 * with a terminating null byte ('\0') stored as the last character into
	 * the buffer, thus the increasing by 1 for the buffer size. */
	char wifi_sar_limit_str[2 * sizeof(struct wifi_sar_limits) + 1];
	uint8_t bin_buffer[sizeof(struct wifi_sar_limits)];
	const size_t buffer_size = ARRAY_SIZE(wifi_sar_limit_str);
	size_t sar_cbfs_len, sar_expected_len, bin_buff_adjusted_size;

	/* keep it backward compatible. Some older platform are shipping
	   without GEO SAR and so older wifi_sar VPD key */

	sar_expected_len = buffer_size;
	bin_buff_adjusted_size = sizeof(struct wifi_sar_limits);

	if (!CONFIG(GEO_SAR_ENABLE)) {
		sar_expected_len = buffer_size -
					sizeof(struct wifi_sar_delta_table) *
					sizeof(uint8_t) * 2;
		bin_buff_adjusted_size = sizeof(struct wifi_sar_limits) -
					 sizeof(struct wifi_sar_delta_table);
	}

	/* Try to read the SAR limit entry from VPD */
	if (!vpd_gets(wifi_sar_limit_key, wifi_sar_limit_str,
							 buffer_size, VPD_ANY)) {
		printk(BIOS_ERR, "Error: Could not locate '%s' in VPD.\n",
				wifi_sar_limit_key);

		if (!CONFIG(WIFI_SAR_CBFS))
			return -1;

		printk(BIOS_DEBUG, "Checking CBFS for default SAR values\n");

		sar_cbfs_len = load_sar_file_from_cbfs(
					(void *) wifi_sar_limit_str,
						sar_expected_len);

		if (sar_cbfs_len != sar_expected_len) {
			printk(BIOS_ERR, "%s has bad len in CBFS\n",
					WIFI_SAR_CBFS_FILENAME);
			return -1;
		}
	} else {
		/* VPD key "wifi_sar" found. strlen is checked with addition of
		 * 1 as we have created buffer size 1 char larger for the reason
		 * mentioned at start of this function itself */
		if (strlen(wifi_sar_limit_str) + 1 != sar_expected_len) {
			printk(BIOS_ERR, "WIFI SAR key has bad len in VPD\n");
			return -1;
		}
	}

	/* Decode the heximal encoded string to binary values */
	if (hexstrtobin(wifi_sar_limit_str, bin_buffer, bin_buff_adjusted_size)
			< bin_buff_adjusted_size) {
		printk(BIOS_ERR, "Error: wifi_sar contains non-hex value!\n");
		return -1;
	}

	memset(sar_limits, 0, sizeof(*sar_limits));
	memcpy(sar_limits, bin_buffer, bin_buff_adjusted_size);
	return 0;
}

__weak
const char *get_wifi_sar_cbfs_filename(void)
{
	return NULL;
}