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 
erase_device_block(const struct flash_area * fa,off_t start,size_t size)59 static inline int erase_device_block(const struct flash_area *fa, off_t start, size_t size)
60 {
61 	/* If there are no devices requiring erase, then there is nothing to do */
62 	if (IS_ENABLED(CONFIG_BT_MESH_BLOB_IO_FLASH_WITH_ERASE)) {
63 		const struct device *fdev = flash_area_get_device(fa);
64 
65 		if (!fdev) {
66 			return -ENODEV;
67 		}
68 
69 		/* We have a mix of devices in system */
70 		if (IS_ENABLED(CONFIG_BT_MESH_BLOB_IO_FLASH_WITHOUT_ERASE)) {
71 			const struct flash_parameters *fparam = flash_get_parameters(fdev);
72 
73 			/* If device has no erase requirement then do nothing */
74 			if (!(flash_params_get_erase_cap(fparam) & FLASH_ERASE_C_EXPLICIT)) {
75 				return 0;
76 			}
77 		}
78 
79 		if (IS_ENABLED(CONFIG_FLASH_PAGE_LAYOUT)) {
80 			struct flash_pages_info page;
81 			int err;
82 
83 			err = flash_get_page_info_by_offs(fdev, start, &page);
84 			if (err) {
85 				return err;
86 			}
87 
88 			size = page.size * DIV_ROUND_UP(size, page.size);
89 			start = page.start_offset;
90 		}
91 		return flash_area_erase(fa, start, size);
92 	}
93 
94 	return 0;
95 }
96 
block_start(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block)97 static int block_start(const struct bt_mesh_blob_io *io,
98 		       const struct bt_mesh_blob_xfer *xfer,
99 		       const struct bt_mesh_blob_block *block)
100 {
101 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
102 
103 	if (flash->mode == BT_MESH_BLOB_READ) {
104 		return 0;
105 	}
106 
107 	return erase_device_block(flash->area, flash->offset + block->offset, block->size);
108 }
109 
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)110 static int rd_chunk(const struct bt_mesh_blob_io *io,
111 		    const struct bt_mesh_blob_xfer *xfer,
112 		    const struct bt_mesh_blob_block *block,
113 		    const struct bt_mesh_blob_chunk *chunk)
114 {
115 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
116 
117 	return flash_area_read(flash->area,
118 			       flash->offset + block->offset + chunk->offset,
119 			       chunk->data, chunk->size);
120 }
121 
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)122 static int wr_chunk(const struct bt_mesh_blob_io *io,
123 		    const struct bt_mesh_blob_xfer *xfer,
124 		    const struct bt_mesh_blob_block *block,
125 		    const struct bt_mesh_blob_chunk *chunk)
126 {
127 	struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
128 
129 	if (IS_ENABLED(CONFIG_SOC_FLASH_NRF_RRAM)) {
130 		return flash_area_write(flash->area,
131 					flash->offset + block->offset + chunk->offset,
132 					chunk->data, chunk->size);
133 	}
134 
135 	uint8_t buf[ROUND_UP(BLOB_RX_CHUNK_SIZE, WRITE_BLOCK_SIZE)];
136 	off_t area_offset = flash->offset + block->offset + chunk->offset;
137 	int i = 0;
138 
139 	/* Write block align the chunk data */
140 	memset(&buf[i], 0xff, area_offset % WRITE_BLOCK_SIZE);
141 	i += area_offset % WRITE_BLOCK_SIZE;
142 
143 	memcpy(&buf[i], chunk->data, chunk->size);
144 	i += chunk->size;
145 
146 	memset(&buf[i], 0xff, ROUND_UP(i, WRITE_BLOCK_SIZE) - i);
147 	i = ROUND_UP(i, WRITE_BLOCK_SIZE);
148 
149 	return flash_area_write(flash->area,
150 				ROUND_DOWN(area_offset, WRITE_BLOCK_SIZE),
151 				buf, i);
152 }
153 
bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash * flash,uint8_t area_id,off_t offset)154 int bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash *flash,
155 			       uint8_t area_id, off_t offset)
156 {
157 	int err;
158 
159 	err = test_flash_area(area_id);
160 	if (err) {
161 		return err;
162 	}
163 
164 	flash->area_id = area_id;
165 	flash->offset = offset;
166 	flash->io.open = io_open;
167 	flash->io.close = io_close;
168 	flash->io.block_start = block_start;
169 	flash->io.block_end = NULL;
170 	flash->io.rd = rd_chunk;
171 	flash->io.wr = wr_chunk;
172 
173 	return 0;
174 }
175