blob: 3bfd843db025faad97342ef37afbada81943a12e [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright 2014 Google Inc.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <cbmem.h>
#include <console/console.h>
#include <string.h>
#include <vendorcode/google/chromeos/chromeos.h>
#include <vendorcode/google/chromeos/cros_vpd.h>
/*
* This file provides functions looking in the VPD for WiFi calibration data,
* and if found, copying the calibration blobs into CBMEM.
*
* Per interface calibration data is stored in the VPD in opaque blobs. The
* keys of the blobs follow one of two possible patterns:
* "wifi_base64_calibration<N>" or "wifi_calibration<N>", where <N> is the
* interface number.
*
* This function accommodates up to 4 interfaces. All calibration blobs found
* in the VPD are packed into a single CBMEM entry as describe by the
* structures below:
*/
/* This structure describes a single calibration data blob */
struct calibration_blob {
uint32_t blob_size; /* Total size. rounded up to fall on a 4 byte
boundary. */
uint32_t key_size; /* Size of the name of this entry, \0 included. */
uint32_t value_size; /* Size of the value of this entry */
/* Zero terminated name(key) goes here, immediately followed by value */
};
/*
* This is the structure of the CBMEM entry containing WiFi calibration blobs.
* It starts with the total size (header size included) followed by an
* arbitrary number of concatenated 4 byte aligned calibration blobs.
*/
struct calibration_entry {
uint32_t size;
struct calibration_blob entries[0]; /* A varialble size container. */
};
#define MAX_WIFI_INTERFACE_COUNT 4
/*
* Structure of the cache to keep information about calibration blobs present
* in the VPD, one cache entry per VPD blob.
*
* Maintaing the cache allows to scan the VPD once, determine the CBMEM entry
* memory requirements, then allocate as much room as necessary and fill it
* up.
*/
struct vpd_blob_cache_t {
/* The longest name template must fit with an extra character. */
char key_name[40];
const void *value_pointer;
unsigned blob_size;
unsigned key_size;
unsigned value_size;
};
static const char * const templates[] = {
"wifi_base64_calibrationX",
"wifi_calibrationX"
};
/*
* Scan the VPD for WiFi calibration data, checking for all possible key names
* and caching discovered blobs.
*
* Return the sum of sizes of all blobs, as stored in CBMEM.
*/
static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache,
size_t max_entries, size_t *filled_entries)
{
int i;
int cbmem_entry_size = 0;
size_t used_entries = 0;
for (i = 0;
(i < ARRAY_SIZE(templates)) && (used_entries < max_entries);
i++) {
int j;
const int index_location = strlen(templates[i]) - 1;
const int key_length = index_location + 2;
if (key_length > sizeof(cache->key_name))
continue;
for (j = 0; j < MAX_WIFI_INTERFACE_COUNT; j++) {
const void *payload;
int payload_size;
strcpy(cache->key_name, templates[i]);
cache->key_name[index_location] = j + '0';
payload = cros_vpd_find(cache->key_name, &payload_size);
if (!payload)
continue;
cache->value_pointer = payload;
cache->key_size = key_length;
cache->value_size = payload_size;
cache->blob_size =
ALIGN(sizeof(struct calibration_blob) +
cache->key_size +
cache->value_size, 4);
cbmem_entry_size += cache->blob_size;
used_entries++;
if (used_entries == max_entries)
break;
cache++;
}
}
*filled_entries = used_entries;
return cbmem_entry_size;
}
void cbmem_add_vpd_calibration_data(void)
{
size_t cbmem_entry_size, filled_entries;
struct calibration_entry *cbmem_entry;
struct calibration_blob *cal_blob;
int i;
/*
* Allocate one more cache entry than max required, to make sure that
* the last entry can be identified by the key size of zero.
*/
struct vpd_blob_cache_t vpd_blob_cache[ARRAY_SIZE(templates) *
MAX_WIFI_INTERFACE_COUNT];
cbmem_entry_size = fill_up_entries_cache(vpd_blob_cache,
ARRAY_SIZE(vpd_blob_cache),
&filled_entries);
if (!cbmem_entry_size)
return; /* No calibration data found in the VPD. */
cbmem_entry_size += sizeof(struct calibration_entry);
cbmem_entry = cbmem_add(CBMEM_ID_WIFI_CALIBRATION, cbmem_entry_size);
if (!cbmem_entry) {
printk(BIOS_ERR, "%s: no room in cbmem to add %zd bytes\n",
__func__, cbmem_entry_size);
return;
}
cbmem_entry->size = cbmem_entry_size;
/* Copy cached data into the CBMEM entry. */
cal_blob = cbmem_entry->entries;
for (i = 0; i < filled_entries; i++) {
/* Use this as a pointer to the current cache entry. */
struct vpd_blob_cache_t *cache = vpd_blob_cache + i;
char *pointer;
cal_blob->blob_size = cache->blob_size;
cal_blob->key_size = cache->key_size;
cal_blob->value_size = cache->value_size;
/* copy the key */
pointer = (char *)(cal_blob + 1);
memcpy(pointer, cache->key_name, cache->key_size);
/* and the value */
pointer += cache->key_size;
memcpy(pointer, cache->value_pointer, cache->value_size);
printk(BIOS_INFO, "%s: added %s to CBMEM\n",
__func__, cache->key_name);
cal_blob = (struct calibration_blob *)
((char *)cal_blob + cal_blob->blob_size);
}
}