|  | /* | 
|  | * Copyright (c) 2020 Noioic Semiconductor ASA | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <zephyr/bluetooth/mesh.h> | 
|  | #include <zephyr/storage/flash_map.h> | 
|  | #include <zephyr/drivers/flash.h> | 
|  | #include <assert.h> | 
|  | #include "blob.h" | 
|  | #include "net.h" | 
|  | #include "transport.h" | 
|  |  | 
|  | #define WRITE_BLOCK_SIZE 4 | 
|  |  | 
|  | #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_area *area; | 
|  | uint8_t align; | 
|  | int err; | 
|  |  | 
|  | err = flash_area_open(area_id, &area); | 
|  | if (err) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | align = flash_area_align(area); | 
|  | flash_area_close(area); | 
|  |  | 
|  | if (align > WRITE_BLOCK_SIZE) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int io_open(const struct bt_mesh_blob_io *io, | 
|  | const struct bt_mesh_blob_xfer *xfer, | 
|  | enum bt_mesh_blob_io_mode mode) | 
|  | { | 
|  | struct bt_mesh_blob_io_flash *flash = FLASH_IO(io); | 
|  |  | 
|  | flash->mode = mode; | 
|  |  | 
|  | return flash_area_open(flash->area_id, &flash->area); | 
|  | } | 
|  |  | 
|  | static void io_close(const struct bt_mesh_blob_io *io, | 
|  | const struct bt_mesh_blob_xfer *xfer) | 
|  | { | 
|  | struct bt_mesh_blob_io_flash *flash = FLASH_IO(io); | 
|  |  | 
|  | flash_area_close(flash->area); | 
|  | } | 
|  |  | 
|  | static int block_start(const struct bt_mesh_blob_io *io, | 
|  | const struct bt_mesh_blob_xfer *xfer, | 
|  | const struct bt_mesh_blob_block *block) | 
|  | { | 
|  | struct bt_mesh_blob_io_flash *flash = FLASH_IO(io); | 
|  | size_t erase_size; | 
|  |  | 
|  | if (flash->mode == BT_MESH_BLOB_READ) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_FLASH_PAGE_LAYOUT) | 
|  | struct flash_pages_info page; | 
|  | const struct device *flash_dev; | 
|  | int err; | 
|  |  | 
|  | flash_dev = flash_area_get_device(flash->area); | 
|  | if (!flash_dev) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | err = flash_get_page_info_by_offs(flash_dev, | 
|  | flash->offset + block->offset, &page); | 
|  | if (err) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | erase_size = page.size * DIV_ROUND_UP(block->size, page.size); | 
|  | #else | 
|  | erase_size = block->size; | 
|  | #endif | 
|  |  | 
|  | return flash_area_erase(flash->area, flash->offset + block->offset, | 
|  | erase_size); | 
|  | } | 
|  |  | 
|  | static int rd_chunk(const struct bt_mesh_blob_io *io, | 
|  | const struct bt_mesh_blob_xfer *xfer, | 
|  | const struct bt_mesh_blob_block *block, | 
|  | const struct bt_mesh_blob_chunk *chunk) | 
|  | { | 
|  | struct bt_mesh_blob_io_flash *flash = FLASH_IO(io); | 
|  |  | 
|  | return flash_area_read(flash->area, | 
|  | flash->offset + block->offset + chunk->offset, | 
|  | chunk->data, chunk->size); | 
|  | } | 
|  |  | 
|  | static int wr_chunk(const struct bt_mesh_blob_io *io, | 
|  | const struct bt_mesh_blob_xfer *xfer, | 
|  | const struct bt_mesh_blob_block *block, | 
|  | const struct bt_mesh_blob_chunk *chunk) | 
|  | { | 
|  | struct bt_mesh_blob_io_flash *flash = FLASH_IO(io); | 
|  | uint8_t buf[ROUND_UP(BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX), | 
|  | WRITE_BLOCK_SIZE)]; | 
|  | 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; | 
|  |  | 
|  | memcpy(&buf[i], chunk->data, chunk->size); | 
|  | i += chunk->size; | 
|  |  | 
|  | memset(&buf[i], 0xff, ROUND_UP(i, WRITE_BLOCK_SIZE) - i); | 
|  | i = ROUND_UP(i, WRITE_BLOCK_SIZE); | 
|  |  | 
|  | return flash_area_write(flash->area, | 
|  | ROUND_DOWN(area_offset, WRITE_BLOCK_SIZE), | 
|  | buf, i); | 
|  | } | 
|  |  | 
|  | int bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash *flash, | 
|  | uint8_t area_id, off_t offset) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | err = test_flash_area(area_id); | 
|  | if (err) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | flash->area_id = area_id; | 
|  | flash->offset = offset; | 
|  | flash->io.open = io_open; | 
|  | flash->io.close = io_close; | 
|  | flash->io.block_start = block_start; | 
|  | flash->io.block_end = NULL; | 
|  | flash->io.rd = rd_chunk; | 
|  | flash->io.wr = wr_chunk; | 
|  |  | 
|  | return 0; | 
|  | } |