| /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* Persistent EC options stored in EEPROM */ |
| |
| #include "console.h" |
| #include "eeprom.h" |
| #include "eoption.h" |
| #include "util.h" |
| |
| #define EOPTION_MAGIC 0x456f /* Magic number for header 'Eo' */ |
| #define EOPTION_VERSION_CURRENT 1 /* Current version of options data */ |
| |
| struct eoption_bool_data { |
| int offset; /* Word offset of option */ |
| int mask; /* Option mask within byte */ |
| const char *name; /* Option name */ |
| }; |
| |
| |
| /* Word offsets inside the EOPTION EEPROM block */ |
| enum block_offsets { |
| OFFSET_HEADER = 0, |
| OFFSET_BOOL0, |
| }; |
| |
| /* |
| * Boolean options. Must be in the same order as enum eoption_bool, and must |
| * be terminated by an entry with a NULL name. |
| */ |
| static const struct eoption_bool_data bool_opts[] = { |
| {OFFSET_BOOL0, (1 << 0), "bool_test"}, |
| {0, 0, NULL}, |
| }; |
| |
| /** |
| * Read a uint32_t from the specified EEPROM word offset. |
| */ |
| static int read32(int offset, uint32_t *dest) |
| { |
| return eeprom_read(EEPROM_BLOCK_EOPTION, offset * 4, sizeof(uint32_t), |
| (char *)dest); |
| } |
| |
| |
| /** |
| * Write a uint32_t to the specified EEPROM word offset. |
| */ |
| static int write32(int offset, uint32_t v) |
| { |
| return eeprom_write(EEPROM_BLOCK_EOPTION, offset * 4, sizeof(v), |
| (char *)&v); |
| } |
| |
| int eoption_get_bool(enum eoption_bool opt) |
| { |
| const struct eoption_bool_data *d = bool_opts + opt; |
| uint32_t v = 0; |
| |
| read32(d->offset, &v); |
| return v & d->mask ? 1 : 0; |
| } |
| |
| int eoption_set_bool(enum eoption_bool opt, int value) |
| { |
| const struct eoption_bool_data *d = bool_opts + opt; |
| uint32_t v; |
| int rv; |
| |
| rv = read32(d->offset, &v); |
| if (rv != EC_SUCCESS) |
| return rv; |
| |
| if (value) |
| v |= d->mask; |
| else |
| v &= ~d->mask; |
| |
| return write32(d->offset, v); |
| } |
| |
| /** |
| * Find an option by name. |
| * |
| * @return The option index, or -1 if no match. |
| */ |
| static int find_option_by_name(const char *name, |
| const struct eoption_bool_data *d) |
| { |
| int i; |
| |
| if (!name || !*name) |
| return -1; |
| |
| for (i = 0; d->name; i++, d++) { |
| if (!strcasecmp(name, d->name)) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| void eoption_init(void) |
| { |
| uint32_t v; |
| int version; |
| |
| /* Initialize EEPROM if necessary */ |
| read32(OFFSET_HEADER, &v); |
| |
| /* Check header */ |
| if (v >> 16 != EOPTION_MAGIC) |
| v = EOPTION_MAGIC << 16; /* (implicitly sets version=0) */ |
| |
| version = (v >> 8) & 0xff; |
| if (version == EOPTION_VERSION_CURRENT) |
| return; |
| |
| /* |
| * TODO(crosbug.com/p/23558): The header should really have a checksum |
| * field, so that we can detect uninitialized/corrupt data. |
| */ |
| |
| /* Initialize fields which weren't set in previous versions */ |
| if (version < 1) |
| write32(OFFSET_BOOL0, 0); |
| |
| /* Update the header */ |
| v = (v & ~0xff00) | (EOPTION_VERSION_CURRENT << 8); |
| write32(OFFSET_HEADER, v); |
| } |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| static int command_eoption_get(int argc, char **argv) |
| { |
| const struct eoption_bool_data *d; |
| int i; |
| |
| /* If a signal is specified, print only that one */ |
| if (argc == 2) { |
| i = find_option_by_name(argv[1], bool_opts); |
| if (i == -1) |
| return EC_ERROR_PARAM1; |
| d = bool_opts + i; |
| ccprintf(" %d %s\n", eoption_get_bool(i), d->name); |
| return EC_SUCCESS; |
| } |
| |
| /* Otherwise print them all */ |
| for (i = 0, d = bool_opts; d->name; i++, d++) { |
| ccprintf(" %d %s\n", eoption_get_bool(i), d->name); |
| |
| /* We have enough options that we'll overflow the output buffer |
| * without flushing */ |
| cflush(); |
| } |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(optget, command_eoption_get, |
| "[name]", |
| "Print EC option(s)", |
| NULL); |
| |
| static int command_eoption_set(int argc, char **argv) |
| { |
| char *e; |
| int v, i; |
| |
| if (argc < 3) |
| return EC_ERROR_PARAM_COUNT; |
| |
| v = strtoi(argv[2], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM2; |
| |
| i = find_option_by_name(argv[1], bool_opts); |
| if (i == -1) |
| return EC_ERROR_PARAM1; |
| |
| return eoption_set_bool(i, v); |
| } |
| DECLARE_CONSOLE_COMMAND(optset, command_eoption_set, |
| "name value", |
| "Set EC option", |
| NULL); |