| /* |
| * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu> |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <device.h> |
| #include <drivers/flash.h> |
| |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "cmdline.h" |
| #include "soc.h" |
| |
| #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(flash_native_posix); |
| |
| static const char default_flash_path[] = "flash.bin"; |
| |
| struct flash_native_posix_data { |
| const char *flash_path; |
| int fd; |
| u8_t *flash; |
| bool init_called; |
| }; |
| |
| struct flash_native_posix_config { |
| size_t flash_size; |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| struct flash_pages_layout layout; |
| #endif |
| }; |
| |
| #define DEV_NAME(dev) ((dev)->config->name) |
| #define DEV_CONFIG(dev) ((dev)->config->config_info) |
| #define DEV_DATA(dev) \ |
| ((struct flash_native_posix_data *const)(dev)->driver_data) |
| |
| static int flash_native_posix_read(struct device *dev, off_t offset, void *data, |
| size_t size) |
| { |
| struct flash_native_posix_data *const dev_data = DEV_DATA(dev); |
| const struct flash_native_posix_config *config = DEV_CONFIG(dev); |
| |
| if (dev_data->flash == MAP_FAILED) { |
| LOG_ERR("No flash device mapped"); |
| return -EIO; |
| } |
| |
| if ((offset + size) > config->flash_size) { |
| LOG_WRN("Reading outside of flash boundaries"); |
| return -EINVAL; |
| } |
| |
| memcpy(data, dev_data->flash + offset, size); |
| |
| return 0; |
| } |
| |
| static int flash_native_posix_write(struct device *dev, off_t offset, |
| const void *data, size_t size) |
| { |
| struct flash_native_posix_data *const dev_data = DEV_DATA(dev); |
| const struct flash_native_posix_config *config = DEV_CONFIG(dev); |
| |
| if (dev_data->flash == MAP_FAILED) { |
| LOG_ERR("No flash device mapped"); |
| return -EIO; |
| } |
| |
| if ((offset + size) > config->flash_size) { |
| LOG_WRN("Writing outside of flash boundaries"); |
| return -EINVAL; |
| } |
| |
| memcpy(dev_data->flash + offset, data, size); |
| |
| return 0; |
| } |
| |
| static int flash_native_posix_erase(struct device *dev, off_t offset, |
| size_t size) |
| { |
| struct flash_native_posix_data *const dev_data = DEV_DATA(dev); |
| const struct flash_native_posix_config *config = DEV_CONFIG(dev); |
| |
| if (dev_data->flash == MAP_FAILED) { |
| LOG_ERR("No flash device mapped"); |
| return -EIO; |
| } |
| |
| if ((offset + size) > config->flash_size) { |
| LOG_WRN("Erasing outside of flash boundaries"); |
| return -EINVAL; |
| } |
| |
| memset(dev_data->flash + offset, 0xff, size); |
| |
| return 0; |
| } |
| |
| static int flash_native_posix_write_protection(struct device *dev, bool enable) |
| { |
| return 0; |
| } |
| |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| static void |
| flash_native_posix_pages_layout(struct device *dev, |
| const struct flash_pages_layout **layout, |
| size_t *layout_size) |
| { |
| const struct flash_native_posix_config *config = DEV_CONFIG(dev); |
| *layout = &config->layout; |
| *layout_size = 1; |
| } |
| #endif /* CONFIG_FLASH_PAGE_LAYOUT */ |
| |
| static int flash_native_posix_init(struct device *dev) |
| { |
| struct flash_native_posix_data *const data = DEV_DATA(dev); |
| const struct flash_native_posix_config *config = DEV_CONFIG(dev); |
| |
| data->init_called = true; |
| |
| if (data->flash_path == NULL) { |
| data->flash_path = default_flash_path; |
| } |
| |
| data->fd = open(data->flash_path, O_RDWR | O_CREAT, (mode_t)0600); |
| if (data->fd == -1) { |
| posix_print_warning("Failed to open flash device file " |
| "%s: %s\n", |
| data->flash_path, strerror(errno)); |
| return -EIO; |
| } |
| |
| if (ftruncate(data->fd, config->flash_size) == -1) { |
| posix_print_warning("Failed to resize flash device file " |
| "%s: %s\n", |
| data->flash_path, strerror(errno)); |
| return -EIO; |
| } |
| |
| data->flash = mmap(NULL, config->flash_size, |
| PROT_WRITE | PROT_READ, MAP_SHARED, data->fd, 0); |
| if (data->flash == MAP_FAILED) { |
| posix_print_warning("Failed to mmap flash device file " |
| "%s: %s\n", |
| data->flash_path, strerror(errno)); |
| return -EIO; |
| } |
| |
| LOG_INF("Device %s initialized", DEV_NAME(dev)); |
| |
| return 0; |
| } |
| |
| static const struct flash_driver_api flash_native_posix_driver_api = { |
| .read = flash_native_posix_read, |
| .write = flash_native_posix_write, |
| .erase = flash_native_posix_erase, |
| .write_protection = flash_native_posix_write_protection, |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| .page_layout = flash_native_posix_pages_layout, |
| #endif |
| .write_block_size = 1, |
| }; |
| |
| static const struct flash_native_posix_config flash_native_posix_config = { |
| .flash_size = DT_FLASH_SIZE * 1024, |
| #if defined(CONFIG_FLASH_PAGE_LAYOUT) |
| .layout = { .pages_count = (DT_FLASH_SIZE * 1024) / |
| (CONFIG_FLASH_NATIVE_POSIX_SECTOR_SIZE * 1024), |
| .pages_size = CONFIG_FLASH_NATIVE_POSIX_SECTOR_SIZE * 1024 |
| }, |
| #endif |
| }; |
| |
| static struct flash_native_posix_data flash_native_posix_data; |
| |
| DEVICE_AND_API_INIT(flash_native_posix_0, DT_FLASH_DEV_NAME, |
| &flash_native_posix_init, &flash_native_posix_data, |
| &flash_native_posix_config, POST_KERNEL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &flash_native_posix_driver_api); |
| |
| static void flash_native_posix_cleanup(void) |
| { |
| struct flash_native_posix_data *const data = &flash_native_posix_data; |
| const struct flash_native_posix_config *config = |
| &flash_native_posix_config; |
| |
| if (!data->init_called) { |
| return; |
| } |
| |
| if (data->flash != MAP_FAILED) { |
| munmap(data->flash, config->flash_size); |
| } |
| |
| if (data->fd != -1) { |
| close(data->fd); |
| } |
| } |
| |
| void flash_native_posix_options(void) |
| { |
| static struct args_struct_t flash_options[] = { |
| { .manual = false, |
| .is_mandatory = false, |
| .is_switch = false, |
| .option = "flash", |
| .name = "path", |
| .type = 's', |
| .dest = (void *)&flash_native_posix_data.flash_path, |
| .call_when_found = NULL, |
| .descript = "Path to binary file to be used as flash" }, |
| ARG_TABLE_ENDMARKER |
| }; |
| |
| native_add_command_line_opts(flash_options); |
| } |
| |
| NATIVE_TASK(flash_native_posix_options, PRE_BOOT_1, 1); |
| NATIVE_TASK(flash_native_posix_cleanup, ON_EXIT, 1); |