/* * Copyright (c) 2019 Laczen * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT zephyr_sim_eeprom #ifdef CONFIG_ARCH_POSIX #include "eeprom_simulator_native.h" #include "cmdline.h" #include "soc.h" #endif /* CONFIG_ARCH_POSIX */ #include #include #include #include #include #include #include #include #define LOG_LEVEL CONFIG_EEPROM_LOG_LEVEL #include LOG_MODULE_REGISTER(eeprom_simulator); struct eeprom_sim_config { size_t size; bool readonly; }; #define EEPROM(addr) (mock_eeprom + (addr)) #if defined(CONFIG_MULTITHREADING) /* semaphore for locking flash resources (tickers) */ static struct k_sem sem_lock; #define SYNC_INIT() k_sem_init(&sem_lock, 1, 1) #define SYNC_LOCK() k_sem_take(&sem_lock, K_FOREVER) #define SYNC_UNLOCK() k_sem_give(&sem_lock) #else #define SYNC_INIT() #define SYNC_LOCK() #define SYNC_UNLOCK() #endif /* simulator statistics */ STATS_SECT_START(eeprom_sim_stats) STATS_SECT_ENTRY32(bytes_read) /* total bytes read */ STATS_SECT_ENTRY32(bytes_written) /* total bytes written */ STATS_SECT_ENTRY32(eeprom_read_calls) /* calls to eeprom_read() */ STATS_SECT_ENTRY32(eeprom_read_time_us) /* time spent in eeprom_read() */ STATS_SECT_ENTRY32(eeprom_write_calls) /* calls to eeprom_write() */ STATS_SECT_ENTRY32(eeprom_write_time_us)/* time spent in eeprom_write() */ STATS_SECT_END; STATS_SECT_DECL(eeprom_sim_stats) eeprom_sim_stats; STATS_NAME_START(eeprom_sim_stats) STATS_NAME(eeprom_sim_stats, bytes_read) STATS_NAME(eeprom_sim_stats, bytes_written) STATS_NAME(eeprom_sim_stats, eeprom_read_calls) STATS_NAME(eeprom_sim_stats, eeprom_read_time_us) STATS_NAME(eeprom_sim_stats, eeprom_write_calls) STATS_NAME(eeprom_sim_stats, eeprom_write_time_us) STATS_NAME_END(eeprom_sim_stats); /* simulator dynamic thresholds */ STATS_SECT_START(eeprom_sim_thresholds) STATS_SECT_ENTRY32(max_write_calls) STATS_SECT_ENTRY32(max_len) STATS_SECT_END; STATS_SECT_DECL(eeprom_sim_thresholds) eeprom_sim_thresholds; STATS_NAME_START(eeprom_sim_thresholds) STATS_NAME(eeprom_sim_thresholds, max_write_calls) STATS_NAME(eeprom_sim_thresholds, max_len) STATS_NAME_END(eeprom_sim_thresholds); #ifdef CONFIG_ARCH_POSIX static char *mock_eeprom; static int eeprom_fd = -1; static const char *eeprom_file_path; #define DEFAULT_EEPROM_FILE_PATH "eeprom.bin" static bool eeprom_erase_at_start; static bool eeprom_rm_at_exit; static bool eeprom_in_ram; #else static uint8_t mock_eeprom[DT_INST_PROP(0, size)]; #endif /* CONFIG_ARCH_POSIX */ static int eeprom_range_is_valid(const struct device *dev, off_t offset, size_t len) { const struct eeprom_sim_config *config = dev->config; if ((offset + len) <= config->size) { return 1; } return 0; } static int eeprom_sim_read(const struct device *dev, off_t offset, void *data, size_t len) { if (!len) { return 0; } if (!eeprom_range_is_valid(dev, offset, len)) { LOG_WRN("attempt to read past device boundary"); return -EINVAL; } SYNC_LOCK(); STATS_INC(eeprom_sim_stats, eeprom_read_calls); memcpy(data, EEPROM(offset), len); STATS_INCN(eeprom_sim_stats, bytes_read, len); SYNC_UNLOCK(); #ifdef CONFIG_EEPROM_SIMULATOR_SIMULATE_TIMING k_busy_wait(CONFIG_EEPROM_SIMULATOR_MIN_READ_TIME_US); STATS_INCN(eeprom_sim_stats, eeprom_read_time_us, CONFIG_EEPROM_SIMULATOR_MIN_READ_TIME_US); #endif return 0; } static int eeprom_sim_write(const struct device *dev, off_t offset, const void *data, size_t len) { const struct eeprom_sim_config *config = dev->config; if (config->readonly) { LOG_WRN("attempt to write to read-only device"); return -EACCES; } if (!len) { return 0; } if (!eeprom_range_is_valid(dev, offset, len)) { LOG_WRN("attempt to write past device boundary"); return -EINVAL; } SYNC_LOCK(); STATS_INC(eeprom_sim_stats, eeprom_write_calls); bool data_part_ignored = false; if (eeprom_sim_thresholds.max_write_calls != 0) { if (eeprom_sim_stats.eeprom_write_calls > eeprom_sim_thresholds.max_write_calls) { goto end; } else if (eeprom_sim_stats.eeprom_write_calls == eeprom_sim_thresholds.max_write_calls) { if (eeprom_sim_thresholds.max_len == 0) { goto end; } data_part_ignored = true; } } if ((data_part_ignored) && (len > eeprom_sim_thresholds.max_len)) { len = eeprom_sim_thresholds.max_len; } memcpy(EEPROM(offset), data, len); STATS_INCN(eeprom_sim_stats, bytes_written, len); #ifdef CONFIG_EEPROM_SIMULATOR_SIMULATE_TIMING /* wait before returning */ k_busy_wait(CONFIG_EEPROM_SIMULATOR_MIN_WRITE_TIME_US); STATS_INCN(eeprom_sim_stats, eeprom_write_time_us, CONFIG_EEPROM_SIMULATOR_MIN_WRITE_TIME_US); #endif end: SYNC_UNLOCK(); return 0; } static size_t eeprom_sim_size(const struct device *dev) { const struct eeprom_sim_config *config = dev->config; return config->size; } static const struct eeprom_driver_api eeprom_sim_api = { .read = eeprom_sim_read, .write = eeprom_sim_write, .size = eeprom_sim_size, }; static const struct eeprom_sim_config eeprom_sim_config_0 = { .size = DT_INST_PROP(0, size), .readonly = DT_INST_PROP(0, read_only), }; #ifdef CONFIG_ARCH_POSIX static int eeprom_mock_init(const struct device *dev) { int rc; ARG_UNUSED(dev); if (eeprom_in_ram == false && eeprom_file_path == NULL) { eeprom_file_path = DEFAULT_EEPROM_FILE_PATH; } rc = eeprom_mock_init_native(eeprom_in_ram, &mock_eeprom, DT_INST_PROP(0, size), &eeprom_fd, eeprom_file_path, 0xFF, eeprom_erase_at_start); if (rc < 0) { return -EIO; } else { return 0; } } #else static int eeprom_mock_init(const struct device *dev) { memset(mock_eeprom, 0xFF, ARRAY_SIZE(mock_eeprom)); return 0; } #endif /* CONFIG_ARCH_POSIX */ static int eeprom_sim_init(const struct device *dev) { SYNC_INIT(); STATS_INIT_AND_REG(eeprom_sim_stats, STATS_SIZE_32, "eeprom_sim_stats"); STATS_INIT_AND_REG(eeprom_sim_thresholds, STATS_SIZE_32, "eeprom_sim_thresholds"); return eeprom_mock_init(dev); } DEVICE_DT_INST_DEFINE(0, &eeprom_sim_init, NULL, NULL, &eeprom_sim_config_0, POST_KERNEL, CONFIG_EEPROM_INIT_PRIORITY, &eeprom_sim_api); #ifdef CONFIG_ARCH_POSIX static void eeprom_native_cleanup(void) { eeprom_mock_cleanup_native(eeprom_in_ram, eeprom_fd, mock_eeprom, DT_INST_PROP(0, size), eeprom_file_path, eeprom_rm_at_exit); } static void eeprom_native_options(void) { static struct args_struct_t eeprom_options[] = { {.option = "eeprom", .name = "path", .type = 's', .dest = (void *)&eeprom_file_path, .descript = "Path to binary file to be used as EEPROM, by default " "\"" DEFAULT_EEPROM_FILE_PATH "\""}, {.is_switch = true, .option = "eeprom_erase", .type = 'b', .dest = (void *)&eeprom_erase_at_start, .descript = "Erase the EEPROM content at startup"}, {.is_switch = true, .option = "eeprom_rm", .type = 'b', .dest = (void *)&eeprom_rm_at_exit, .descript = "Remove the EEPROM file when terminating the execution"}, {.is_switch = true, .option = "eeprom_in_ram", .type = 'b', .dest = (void *)&eeprom_in_ram, .descript = "Instead of a file, keep the file content just in RAM. If this is " "set, eeprom, eeprom_erase & eeprom_rm are ignored, and the EEPROM " "content is always erased at startup"}, ARG_TABLE_ENDMARKER }; native_add_command_line_opts(eeprom_options); } NATIVE_TASK(eeprom_native_options, PRE_BOOT_1, 1); NATIVE_TASK(eeprom_native_cleanup, ON_EXIT, 1); #endif /* CONFIG_ARCH_POSIX */