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