Skip to content

Commit

Permalink
drivers: disk_flash: Create disk_flash driver
Browse files Browse the repository at this point in the history
Create a driver that emulates a flash storage on the disk api.
This enables e.g. using an spi SD-Card as secondary partition
for mcuboot.

Signed-off-by: Carlo Kirchmeier <[email protected]>
  • Loading branch information
kica-z committed Sep 19, 2023
1 parent f007f21 commit f7228a7
Show file tree
Hide file tree
Showing 11 changed files with 542 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/flash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ if(CONFIG_FLASH_SIMULATOR)
zephyr_library_sources(flash_simulator_native.c)
endif()
endif()
zephyr_library_sources_ifdef(CONFIG_FLASH_DRIVER_DISK flash_disk.c)
zephyr_library_sources_ifdef(CONFIG_SPI_FLASH_AT45 spi_flash_at45.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_ITE_IT8XXX2 flash_ite_it8xxx2.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF soc_flash_nrf.c)
Expand Down
2 changes: 2 additions & 0 deletions drivers/flash/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ source "drivers/flash/Kconfig.sam"

source "drivers/flash/Kconfig.simulator"

source "drivers/flash/Kconfig.disk"

source "drivers/flash/Kconfig.rv32m1"

source "drivers/flash/Kconfig.nordic_qspi_nor"
Expand Down
13 changes: 13 additions & 0 deletions drivers/flash/Kconfig.disk
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Disk flash emulator config

# Copyright (c) 2023 Endress+Hauser AG
# SPDX-License-Identifier: Apache-2.0

config FLASH_DRIVER_DISK
bool "Use the flash on disk emulator"
default n
select FLASH_HAS_DRIVER_ENABLED
help
This option enables the flash on disk emulator
which can be used to emulate a flash on a disk
device like sd-card or ramdisk.
312 changes: 312 additions & 0 deletions drivers/flash/flash_disk.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
/*
* Copyright (c) 2023 Endress+Hauser AG
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT zephyr_disk_flash

#include <string.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/storage/disk_access.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/drivers/disk.h>

LOG_MODULE_REGISTER(disk_flash, CONFIG_FLASH_LOG_LEVEL);

struct flash_disk_dev_data {
struct k_sem sem;
char *const buf;
uint16_t const bufsize;
uint32_t disk_total_sector_cnt;
uint32_t disk_sector_size;
};

struct flash_disk_dev_config {
int flash_size;
struct flash_parameters flash_parameters;
char disk_name[Z_DEVICE_MAX_NAME_LEN];
int disk_offset;

IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, (struct flash_pages_layout flash_pages_layout;))
};

static void acquire_device(const struct device *dev);
static void release_device(const struct device *dev);

static int flash_disk_init(const struct device *dev)
{
LOG_DBG("%s called", __func__);
struct flash_disk_dev_data *data = dev->data;
const struct flash_disk_dev_config *cfg = dev->config;

if (IS_ENABLED(CONFIG_MULTITHREADING)) {
k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT);
}

int res = disk_access_init(cfg->disk_name);

if (res != 0) {
LOG_ERR("init disk failed: %d", res);
return -EINVAL;
}

res = disk_access_ioctl(cfg->disk_name, DISK_IOCTL_GET_SECTOR_COUNT,
&data->disk_total_sector_cnt);
if (res != 0) {
LOG_ERR("read total sector cnt failed: %d", res);
return -EINVAL;
}

res = disk_access_ioctl(cfg->disk_name, DISK_IOCTL_GET_SECTOR_SIZE,
&data->disk_sector_size);
if (res != 0) {
LOG_ERR("read sector size failed: %d", res);
return -EINVAL;
}

if (data->disk_sector_size > data->bufsize) {
LOG_ERR("sector size %u of disk to big for buffer %u", data->disk_sector_size,
(uint32_t)sizeof(data->buf));
return -EINVAL;
}

if (cfg->disk_offset % data->disk_sector_size != 0) {
LOG_WRN("offset is not aligned with disk sectors (disk sector size: %u)",
data->disk_sector_size);
}

return 0;
}

static int flash_disk_read(const struct device *dev, off_t offset, void *dst, size_t len)
{
LOG_DBG("%s called", __func__);
acquire_device(dev);
int ret = 0;
char *dstBuf = (char *)dst;
struct flash_disk_dev_data *data = dev->data;
const struct flash_disk_dev_config *cfg = dev->config;
uint32_t write_cursor = 0;

if (data->disk_sector_size == 0) {
ret = -EINVAL;
goto release_device;
}

while (write_cursor < len) {
uint32_t read_cursor = cfg->disk_offset + offset + write_cursor;
uint32_t read_sector = read_cursor / data->disk_sector_size;
uint32_t read_padding = read_cursor % data->disk_sector_size;
uint32_t len_write = (len - write_cursor);

if (read_sector < 0 || read_sector >= data->disk_total_sector_cnt) {
ret = -EINVAL;
goto release_device;
}

if (disk_access_read(cfg->disk_name, data->buf, read_sector, 1)) {
ret = -EINVAL;
goto release_device;
}

if (len_write > sizeof(data->buf) - read_padding) {
len_write = sizeof(data->buf) - read_padding;
}

memcpy(&dstBuf[write_cursor], &data->buf[read_padding], len_write);
write_cursor += len_write;
}
release_device:
release_device(dev);
return ret;
}

static int flash_disk_write(const struct device *dev, off_t offset, const void *src, size_t len)
{
LOG_DBG("%s called", __func__);
acquire_device(dev);
int ret = 0;
const char *srcBuf = (const char *)src;
struct flash_disk_dev_data *data = dev->data;
const struct flash_disk_dev_config *cfg = dev->config;
uint32_t read_cursor = 0;

if (data->disk_sector_size == 0) {
ret = -EINVAL;
goto release_device;
}

while (read_cursor < len) {
uint32_t write_cursor = cfg->disk_offset + offset + read_cursor;
uint32_t write_sector = write_cursor / data->disk_sector_size;
uint32_t write_padding = write_cursor % data->disk_sector_size;
uint32_t len_write = (len - read_cursor);

if (write_sector < 0 || write_sector >= data->disk_total_sector_cnt) {
ret = -EINVAL;
goto release_device;
}

if (disk_access_read(cfg->disk_name, data->buf, write_sector, 1)) {
ret = -EINVAL;
goto release_device;
}

if (len_write > sizeof(data->buf) - write_padding) {
len_write = sizeof(data->buf) - write_padding;
}

memcpy(&data->buf[write_padding], &srcBuf[read_cursor], len_write);

if (disk_access_write(cfg->disk_name, data->buf, write_sector, 1)) {
ret = -EINVAL;
goto release_device;
}
read_cursor += len_write;
}
release_device:
release_device(dev);
return ret;
}

static int flash_disk_erase(const struct device *dev, off_t offset, size_t size)
{
LOG_DBG("%s called", __func__);
acquire_device(dev);
int ret = 0;
struct flash_disk_dev_data *data = dev->data;
const struct flash_disk_dev_config *cfg = dev->config;
uint32_t read_cursor = 0;

if (data->disk_sector_size == 0) {
return -EINVAL;
}

while (read_cursor < size) {
uint32_t write_cursor = cfg->disk_offset + offset + read_cursor;
uint32_t write_sector = write_cursor / data->disk_sector_size;
uint32_t write_padding = write_cursor % data->disk_sector_size;
uint32_t len_write = (size - read_cursor);

if (write_sector < 0 || write_sector >= data->disk_total_sector_cnt) {
ret = -EINVAL;
goto release_device;
}

if (disk_access_read(cfg->disk_name, data->buf, write_sector, 1)) {
ret = -EINVAL;
goto release_device;
}

if (len_write > sizeof(data->buf) - write_padding) {
len_write = sizeof(data->buf) - write_padding;
}

memset(&data->buf[write_padding], cfg->flash_parameters.erase_value, len_write);

if (disk_access_write(cfg->disk_name, data->buf, write_sector, 1)) {
ret = -EINVAL;
goto release_device;
}
read_cursor += len_write;
}
release_device:
release_device(dev);
return ret;
}

#if defined(CONFIG_FLASH_PAGE_LAYOUT)
static void flash_disk_pages_layout(const struct device *dev,
const struct flash_pages_layout **layout, size_t *layout_size)
{
LOG_DBG("%s called", __func__);
const struct flash_disk_dev_config *cfg = dev->config;

*layout = &cfg->flash_pages_layout;
*layout_size = 1;
}
#endif

static const struct flash_parameters *flash_disk_get_parameters(const struct device *dev)
{
LOG_DBG("%s called", __func__);
const struct flash_disk_dev_config *cfg = dev->config;

return &cfg->flash_parameters;
}

static const struct flash_driver_api disk_flash_api = {
.read = flash_disk_read,
.write = flash_disk_write,
.erase = flash_disk_erase,
.get_parameters = flash_disk_get_parameters,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
.page_layout = flash_disk_pages_layout,
#endif
#if defined(CONFIG_FLASH_JESD216_API)
.sfdp_read = NULL,
.read_jedec_id = NULL,
#endif
#if defined(CONFIG_FLASH_EX_OP_ENABLED)
.ex_op = NULL,
#endif
};

static void acquire_device(const struct device *dev)
{
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
struct flash_disk_dev_data *const data = dev->data;

k_sem_take(&data->sem, K_FOREVER);
}
}

static void release_device(const struct device *dev)
{
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
struct flash_disk_dev_data *const data = dev->data;

k_sem_give(&data->sem);
}
}

/* clang-format off */
#define DISK_FLASH_DEFINE(index) \
char buf_##index[DT_INST_PROP(index, disk_sector_size)]; \
static struct flash_disk_dev_data disk_flash_data_##index = { \
.buf = buf_##index, \
.bufsize = sizeof(buf_##index), \
}; \
\
BUILD_ASSERT(DT_INST_PROP(index, size) > 0, "flash_size needs to be > 0"); \
BUILD_ASSERT(DT_INST_PROP(index, write_block_size) > 0, \
"write_block_size needs to be > 0"); \
BUILD_ASSERT(DT_INST_PROP(index, erase_value) > 0 && \
DT_INST_PROP(index, erase_value) <= 0xFF, \
"erase_value has to be between 0x00 and 0xFF"); \
BUILD_ASSERT(DT_INST_PROP(index, disk_offset) >= 0, "disk_offset needs to be >= 0"); \
static const struct flash_disk_dev_config disk_flash_config_##index = { \
.flash_size = DT_INST_PROP(index, size), \
IF_ENABLED( \
CONFIG_FLASH_PAGE_LAYOUT, \
(.flash_parameters = \
{ \
.write_block_size = DT_INST_PROP(index, write_block_size), \
.erase_value = DT_INST_PROP(index, erase_value) \
}, \
) \
) \
.disk_name = DT_INST_PROP(index, disk_name), \
.disk_offset = DT_INST_PROP(index, disk_offset), \
}; \
\
BUILD_ASSERT(DT_INST_PROP(index, init_priority) >= 0, "init_priority needs to be >= 0"); \
DEVICE_DT_INST_DEFINE(index, &flash_disk_init, NULL, &disk_flash_data_##index, \
&disk_flash_config_##index, \
DT_STRING_TOKEN(DT_DRV_INST(index), init_level), \
DT_INST_PROP(index, init_priority), &disk_flash_api);
/* clang-format on */

DT_INST_FOREACH_STATUS_OKAY(DISK_FLASH_DEFINE)
51 changes: 51 additions & 0 deletions dts/bindings/flash_controller/zephyr,disk-flash.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SPDX-License-Identifier: Apache-2.0

description: Emulates a flash using a disk

compatible: "zephyr,disk-flash"

include: base.yaml

properties:
size:
type: int
description: flash capacity in bits
required: true
disk-name:
type: string
description: the name of the underlying disk to be used
required: true
disk-sector-size:
type: int
description: the sector size in byte of the underlying disk to be used
required: true
disk-offset:
type: int
description: >
offset on the disk in bytes. If the offset is 10 the 10th byte will
be the first one used.
default: 0
init-level:
type: string
description: >
The initialization level of this driver. It is important, that the underlying disk has
already been initialized.
default: "APPLICATION"
init-priority:
required: true
type: int
description: >
The initialization priority of this driver. It is important, that the underlying disk has
already been initialized.
write-block-size:
type: int
description: size of a flash write block in bytes
default: 1
erase-value:
type: int
description: byte value which should be used for erasing (range 0-255)
default: 0xFF
page-size:
type: int
description: size of a flash page in bytes
default: 4096
Loading

0 comments on commit f7228a7

Please sign in to comment.