/* 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);