diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 80846310c63..297da4e8020 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -1053,6 +1053,7 @@ config BT_MESH_BLOB_IO_FLASH default y depends on BT_MESH_BLOB_SRV || BT_MESH_BLOB_CLI depends on FLASH_MAP + depends on FLASH_PAGE_LAYOUT help Enable the BLOB flash stream for reading and writing BLOBs directly to and from flash. @@ -1060,24 +1061,28 @@ config BT_MESH_BLOB_IO_FLASH if BT_MESH_BLOB_IO_FLASH config BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE - bool "BLOB flash support for devices without erase" - default y if FLASH_HAS_NO_EXPLICIT_ERASE + bool "BLOB flash support for devices without erase [DEPRECATED]" + default n depends on FLASH_HAS_NO_EXPLICIT_ERASE + select DEPRECATED help - Enable path supporting devices without erase. This option appears only - if there are devices without explicit erase requirements in the system - and may be disabled to reduce code size in case when no operations - are intended on such type of devices. + This option is deprecated and is no longer used by the BLOB IO Flash module. config BT_MESH_BLOB_IO_FLASH_WITH_ERASE - bool "BLOB flash support for devices with erase" - default y if FLASH_HAS_EXPLICIT_ERASE + bool "BLOB flash support for devices with erase [DEPRECATED]" + default n depends on FLASH_HAS_EXPLICIT_ERASE + depends on FLASH_PAGE_LAYOUT + select DEPRECATED help - Enable path supporting devices with erase. This option appears only - if there are devices requiring erase, before write, in the system - and may be disabled to reduce code size in case when no operations - are intended on such type of devices. + This option is deprecated and is no longer used by the BLOB IO Flash module. + +config BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX + int "Maximum supported write block size" + default 4 + help + The BLOB IO Flash module will support flash devices with explicit erase + when this value is set to a multiple of the device write block size. endif # BT_MESH_BLOB_IO_FLASH diff --git a/subsys/bluetooth/mesh/blob_io_flash.c b/subsys/bluetooth/mesh/blob_io_flash.c index 8d989a6feca..792c53d4a0a 100644 --- a/subsys/bluetooth/mesh/blob_io_flash.c +++ b/subsys/bluetooth/mesh/blob_io_flash.c @@ -12,13 +12,17 @@ #include "net.h" #include "transport.h" -#define WRITE_BLOCK_SIZE DT_PROP(DT_INST(0, soc_nv_flash), write_block_size) +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_blob_io_flash); #define FLASH_IO(_io) CONTAINER_OF(_io, struct bt_mesh_blob_io_flash, io) static int test_flash_area(uint8_t area_id) { + const struct flash_parameters *fparam; const struct flash_area *area; + const struct device *fdev; uint8_t align; int err; @@ -28,9 +32,19 @@ static int test_flash_area(uint8_t area_id) } align = flash_area_align(area); + fdev = flash_area_get_device(area); + fparam = flash_get_parameters(fdev); + flash_area_close(area); - if (align > WRITE_BLOCK_SIZE) { + if (!fdev) { + return -ENODEV; + } + + if ((flash_params_get_erase_cap(fparam) & FLASH_ERASE_C_EXPLICIT) && + CONFIG_BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX % align) { + LOG_ERR("CONFIG_BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX must be set to a\n" + "multiple of the write block size for the flash deviced used."); return -EINVAL; } @@ -58,40 +72,35 @@ static void io_close(const struct bt_mesh_blob_io *io, static inline int erase_device_block(const struct flash_area *fa, off_t start, size_t size) { - /* If there are no devices requiring erase, then there is nothing to do */ - if (IS_ENABLED(CONFIG_BT_MESH_BLOB_IO_FLASH_WITH_ERASE)) { - const struct device *fdev = flash_area_get_device(fa); - - if (!fdev) { - return -ENODEV; - } - - /* We have a mix of devices in system */ - if (IS_ENABLED(CONFIG_BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE)) { - const struct flash_parameters *fparam = flash_get_parameters(fdev); - - /* If device has no erase requirement then do nothing */ - if (!(flash_params_get_erase_cap(fparam) & FLASH_ERASE_C_EXPLICIT)) { - return 0; - } - } - - if (IS_ENABLED(CONFIG_FLASH_PAGE_LAYOUT)) { - struct flash_pages_info page; - int err; - - err = flash_get_page_info_by_offs(fdev, start, &page); - if (err) { - return err; - } - - size = page.size * DIV_ROUND_UP(size, page.size); - start = page.start_offset; - } - return flash_area_erase(fa, start, size); + const struct device *fdev = flash_area_get_device(fa); + struct flash_pages_info page; + int err; + + if (!fdev) { + return -ENODEV; } - return 0; + const struct flash_parameters *fparam = flash_get_parameters(fdev); + + /* If device has no erase requirement then do nothing */ + if (!(flash_params_get_erase_cap(fparam) & FLASH_ERASE_C_EXPLICIT)) { + return 0; + } + + err = flash_get_page_info_by_offs(fdev, start, &page); + if (err) { + return err; + } + + if (start != page.start_offset) { + /* Only need to erase when starting the first block on the page. */ + return 0; + } + + /* Align to page boundary. */ + size = page.size * DIV_ROUND_UP(size, page.size); + + return flash_area_erase(fa, start, size); } static int block_start(const struct bt_mesh_blob_io *io, @@ -125,30 +134,49 @@ static int wr_chunk(const struct bt_mesh_blob_io *io, const struct bt_mesh_blob_chunk *chunk) { struct bt_mesh_blob_io_flash *flash = FLASH_IO(io); + const struct device *fdev = flash_area_get_device(flash->area); + + if (!fdev) { + return -ENODEV; + } - if (IS_ENABLED(CONFIG_SOC_FLASH_NRF_RRAM)) { + const struct flash_parameters *fparam = flash_get_parameters(fdev); + + /* + * If device has no erase requirement then write directly. + * This is required since trick with padding using the erase value will + * not work in this case. + */ + if (!(flash_params_get_erase_cap(fparam) & FLASH_ERASE_C_EXPLICIT)) { return flash_area_write(flash->area, flash->offset + block->offset + chunk->offset, chunk->data, chunk->size); } - uint8_t buf[ROUND_UP(BLOB_RX_CHUNK_SIZE, WRITE_BLOCK_SIZE)]; + /* + * Allocate one additional write block for the case where a chunk will need + * an extra write block on both sides to fit. + */ + uint8_t buf[ROUND_UP(BLOB_RX_CHUNK_SIZE, CONFIG_BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX) + + CONFIG_BT_MESH_BLOB_IO_FLASH_WRITE_BLOCK_SIZE_MAX]; + uint32_t write_block_size = flash_area_align(flash->area); off_t area_offset = flash->offset + block->offset + chunk->offset; - int i = 0; - - /* Write block align the chunk data */ - memset(&buf[i], 0xff, area_offset % WRITE_BLOCK_SIZE); - i += area_offset % WRITE_BLOCK_SIZE; + int start_pad = area_offset % write_block_size; - memcpy(&buf[i], chunk->data, chunk->size); - i += chunk->size; + /* + * Fill buffer with erase value, to make sure only the part of the + * buffer with chunk data will overwrite flash. + * (Because chunks can arrive in random order, this is required unless + * the entire block is cached in RAM). + */ + memset(buf, fparam->erase_value, sizeof(buf)); - memset(&buf[i], 0xff, ROUND_UP(i, WRITE_BLOCK_SIZE) - i); - i = ROUND_UP(i, WRITE_BLOCK_SIZE); + memcpy(&buf[start_pad], chunk->data, chunk->size); return flash_area_write(flash->area, - ROUND_DOWN(area_offset, WRITE_BLOCK_SIZE), - buf, i); + ROUND_DOWN(area_offset, write_block_size), + buf, + ROUND_UP(start_pad + chunk->size, write_block_size)); } int bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash *flash,