1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/mesh.h>
8 #include <zephyr/storage/flash_map.h>
9 #include <zephyr/drivers/flash.h>
10 #include <assert.h>
11 #include "blob.h"
12 #include "net.h"
13 #include "transport.h"
14 
15 #define WRITE_BLOCK_SIZE DT_PROP(DT_INST(0, soc_nv_flash), write_block_size)
16 
17 #define FLASH_IO(_io) CONTAINER_OF(_io, struct bt_mesh_blob_io_flash, io)
18 
test_flash_area(uint8_t area_id)19 static int test_flash_area(uint8_t area_id)
20 {
21 	const struct flash_area *area;
22 	uint8_t align;
23 	int err;
24 
25 	err = flash_area_open(area_id, &area);
26 	if (err) {
27 		return err;
28 	}
29 
30 	align = flash_area_align(area);
31 	flash_area_close(area);
32 
33 	if (align > WRITE_BLOCK_SIZE) {
34 		return -EINVAL;
35 	}
36 
37 	return 0;
38 }
39 
io_open(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,enum bt_mesh_blob_io_mode mode)40 static int io_open(const struct bt_mesh_blob_io *io,
41 		   const struct bt_mesh_blob_xfer *xfer,
42 		   enum bt_mesh_blob_io_mode mode)
43 {
44 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
45 
46 	flash->mode = mode;
47 
48 	return flash_area_open(flash->area_id, &flash->area);
49 }
50 
io_close(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer)51 static void io_close(const struct bt_mesh_blob_io *io,
52 		     const struct bt_mesh_blob_xfer *xfer)
53 {
54 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
55 
56 	flash_area_close(flash->area);
57 }
58 
block_start(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block)59 static int block_start(const struct bt_mesh_blob_io *io,
60 		       const struct bt_mesh_blob_xfer *xfer,
61 		       const struct bt_mesh_blob_block *block)
62 {
63 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
64 	size_t erase_size;
65 
66 	if (flash->mode == BT_MESH_BLOB_READ) {
67 		return 0;
68 	}
69 
70 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
71 	struct flash_pages_info page;
72 	const struct device *flash_dev;
73 	int err;
74 
75 	flash_dev = flash_area_get_device(flash->area);
76 	if (!flash_dev) {
77 		return -ENODEV;
78 	}
79 
80 	err = flash_get_page_info_by_offs(flash_dev,
81 					  flash->offset + block->offset, &page);
82 	if (err) {
83 		return err;
84 	}
85 
86 	erase_size = page.size * DIV_ROUND_UP(block->size, page.size);
87 #else
88 	erase_size = block->size;
89 #endif
90 
91 	return flash_area_flatten(flash->area, flash->offset + block->offset,
92 				  erase_size);
93 }
94 
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)95 static int rd_chunk(const struct bt_mesh_blob_io *io,
96 		    const struct bt_mesh_blob_xfer *xfer,
97 		    const struct bt_mesh_blob_block *block,
98 		    const struct bt_mesh_blob_chunk *chunk)
99 {
100 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
101 
102 	return flash_area_read(flash->area,
103 			       flash->offset + block->offset + chunk->offset,
104 			       chunk->data, chunk->size);
105 }
106 
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)107 static int wr_chunk(const struct bt_mesh_blob_io *io,
108 		    const struct bt_mesh_blob_xfer *xfer,
109 		    const struct bt_mesh_blob_block *block,
110 		    const struct bt_mesh_blob_chunk *chunk)
111 {
112 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
113 
114 	if (IS_ENABLED(CONFIG_SOC_FLASH_NRF_RRAM)) {
115 		return flash_area_write(flash->area,
116 					flash->offset + block->offset + chunk->offset,
117 					chunk->data, chunk->size);
118 	}
119 
120 	uint8_t buf[ROUND_UP(BLOB_RX_CHUNK_SIZE, WRITE_BLOCK_SIZE)];
121 	off_t area_offset = flash->offset + block->offset + chunk->offset;
122 	int i = 0;
123 
124 	/* Write block align the chunk data */
125 	memset(&buf[i], 0xff, area_offset % WRITE_BLOCK_SIZE);
126 	i += area_offset % WRITE_BLOCK_SIZE;
127 
128 	memcpy(&buf[i], chunk->data, chunk->size);
129 	i += chunk->size;
130 
131 	memset(&buf[i], 0xff, ROUND_UP(i, WRITE_BLOCK_SIZE) - i);
132 	i = ROUND_UP(i, WRITE_BLOCK_SIZE);
133 
134 	return flash_area_write(flash->area,
135 				ROUND_DOWN(area_offset, WRITE_BLOCK_SIZE),
136 				buf, i);
137 }
138 
bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash * flash,uint8_t area_id,off_t offset)139 int bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash *flash,
140 			       uint8_t area_id, off_t offset)
141 {
142 	int err;
143 
144 	err = test_flash_area(area_id);
145 	if (err) {
146 		return err;
147 	}
148 
149 	flash->area_id = area_id;
150 	flash->offset = offset;
151 	flash->io.open = io_open;
152 	flash->io.close = io_close;
153 	flash->io.block_start = block_start;
154 	flash->io.block_end = NULL;
155 	flash->io.rd = rd_chunk;
156 	flash->io.wr = wr_chunk;
157 
158 	return 0;
159 }
160