diff --git a/drivers/bbram/CMakeLists.txt b/drivers/bbram/CMakeLists.txt index 6a2dcf09ccc..425d0482bc0 100644 --- a/drivers/bbram/CMakeLists.txt +++ b/drivers/bbram/CMakeLists.txt @@ -3,6 +3,8 @@ zephyr_library() +zephyr_library_sources_ifdef(CONFIG_BBRAM_SHELL bbram_shell.c) + zephyr_library_sources_ifdef(CONFIG_BBRAM_NPCX bbram_npcx.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_IT8XXX2 bbram_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_EMUL bbram_emul.c) diff --git a/drivers/bbram/Kconfig b/drivers/bbram/Kconfig index 337eb6781a4..a6815905f85 100644 --- a/drivers/bbram/Kconfig +++ b/drivers/bbram/Kconfig @@ -12,6 +12,12 @@ module = BBRAM module-str = bbram source "subsys/logging/Kconfig.template.log_config" +config BBRAM_SHELL + bool "Battery-backed RAM shell" + depends on SHELL + help + Enable the BBRAM shell with read and write commands. + config BBRAM_INIT_PRIORITY int "Init priority" # In STM32, BBRAM is a part of RTC. In this case init priority must be diff --git a/drivers/bbram/bbram_shell.c b/drivers/bbram/bbram_shell.c new file mode 100644 index 00000000000..f04549d7470 --- /dev/null +++ b/drivers/bbram/bbram_shell.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2022 Google Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include + +/* Buffer is only needed for bytes that follow command, device and address. */ +#define BUF_ARRAY_CNT (CONFIG_SHELL_ARGC_MAX - 3) + +static inline int parse_ul(const char *str, unsigned long *result) +{ + char *end; + unsigned long val; + + val = strtoul(str, &end, 0); + + if (*str == '\0' || *end != '\0') { + return -EINVAL; + } + + *result = val; + return 0; +} + +static inline int parse_u32(const char *str, uint32_t *result) +{ + unsigned long val; + + if (parse_ul(str, &val) || val > 0xffffffff) { + return -EINVAL; + } + *result = (uint32_t)val; + return 0; +} + +static inline int parse_u8(const char *str, uint8_t *result) +{ + unsigned long val; + + if (parse_ul(str, &val) || val > 0xff) { + return -EINVAL; + } + *result = (uint8_t)val; + return 0; +} + +static inline int parse_device(const struct shell *sh, size_t argc, char *argv[], + const struct device **bbram_dev) +{ + if (argc < 2) { + shell_error(sh, "Missing BBRAM device"); + return -EINVAL; + } + + *bbram_dev = device_get_binding(argv[1]); + if (!*bbram_dev) { + shell_error(sh, "Given BBRAM device was not found"); + return -ENODEV; + } + + return 0; +} + +static int cmd_read(const struct shell *sh, size_t argc, char *argv[]) +{ + const struct device *bbram_dev; + uint32_t addr; + size_t size; + int part_size, ret; + + ret = parse_device(sh, argc, argv, &bbram_dev); + if (ret) { + return ret; + } + + if (argc < 3) { + /* Dump whole BBRAM if address not provided. */ + addr = 0; + ret = bbram_get_size(bbram_dev, &size); + if (ret < 0) { + shell_error(sh, "Can't get BBRAM size: %d", ret); + return -EIO; + } + } else { + /* Parse address if provided. */ + ret = parse_u32(argv[2], &addr); + if (ret) { + return ret; + } + + /* If size not provided read one byte. */ + size = 1; + + if (argc >= 4) { + /* Parse size if provided. */ + ret = parse_u32(argv[3], &size); + if (ret) { + return ret; + } + } + } + + for (int cnt = 0; cnt < size; cnt += part_size) { + uint8_t data[SHELL_HEXDUMP_BYTES_IN_LINE]; + + part_size = MIN(size - cnt, SHELL_HEXDUMP_BYTES_IN_LINE); + ret = bbram_read(bbram_dev, addr, part_size, data); + if (ret != 0) { + shell_error(sh, "BBRAM read error: %d", ret); + return -EIO; + } + shell_hexdump_line(sh, addr, data, part_size); + addr += part_size; + } + + shell_print(sh, ""); + return 0; +} + +static int cmd_write(const struct shell *sh, size_t argc, char *argv[]) +{ + const struct device *bbram_dev; + uint8_t buf[BUF_ARRAY_CNT]; + uint32_t addr; + size_t size = 0; + int ret; + + ret = parse_device(sh, argc, argv, &bbram_dev); + if (ret) { + return ret; + } + + if (argc < 3) { + shell_error(sh, "Missing address"); + return -EINVAL; + } + + /* Parse address. */ + ret = parse_u32(argv[2], &addr); + if (ret) { + return ret; + } + + /* Parse bytes and place them in the buffer. */ + for (int i = 3; i < argc; i++) { + ret = parse_u8(argv[i], &buf[i - 3]); + if (ret) { + return ret; + } + size++; + } + + if (size == 0) { + shell_error(sh, "Missing data"); + return -EINVAL; + } + + ret = bbram_write(bbram_dev, addr, size, buf); + if (ret < 0) { + shell_error(sh, "BBRAM write error: %d", ret); + return -EIO; + } + + return 0; +} + +static void device_name_get(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, NULL); + + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); + +SHELL_STATIC_SUBCMD_SET_CREATE(bbram_cmds, + SHELL_CMD_ARG(read, &dsub_device_name, + " [
] []", cmd_read, 2, 2), + SHELL_CMD_ARG(write, &dsub_device_name, + "
[...]", cmd_write, 4, + BUF_ARRAY_CNT), + SHELL_SUBCMD_SET_END); + +static int cmd_bbram(const struct shell *sh, size_t argc, char **argv) +{ + shell_error(sh, "%s: unknown parameter: %s", argv[0], argv[1]); + return -EINVAL; +} + +SHELL_CMD_ARG_REGISTER(bbram, &bbram_cmds, "Battery-backed RAM shell commands", cmd_bbram, 2, 0);