1 /*
2 * Copyright (c) 2017, 2020 Nordic Semiconductor ASA
3 * Copyright (c) 2017 Linaro Limited
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define LOG_MODULE_NAME STREAM_FLASH
9 #define LOG_LEVEL CONFIG_STREAM_FLASH_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_STREAM_FLASH_LOG_LEVEL);
12
13 #include <zephyr/types.h>
14 #include <string.h>
15 #include <zephyr/drivers/flash.h>
16
17 #include <zephyr/storage/stream_flash.h>
18
19 #ifdef CONFIG_STREAM_FLASH_PROGRESS
20 #include <zephyr/settings/settings.h>
21
settings_direct_loader(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)22 static int settings_direct_loader(const char *key, size_t len,
23 settings_read_cb read_cb, void *cb_arg,
24 void *param)
25 {
26 struct stream_flash_ctx *ctx = (struct stream_flash_ctx *) param;
27
28 /* Handle the subtree if it is an exact key match. */
29 if (settings_name_next(key, NULL) == 0) {
30 size_t bytes_written = 0;
31 ssize_t len = read_cb(cb_arg, &bytes_written,
32 sizeof(bytes_written));
33
34 if (len != sizeof(ctx->bytes_written)) {
35 LOG_ERR("Unable to read bytes_written from storage");
36 return len;
37 }
38
39 /* Check that loaded progress is not outdated. */
40 if (bytes_written >= ctx->bytes_written) {
41 ctx->bytes_written = bytes_written;
42 } else {
43 LOG_WRN("Loaded outdated bytes_written %zu < %zu",
44 bytes_written, ctx->bytes_written);
45 return 0;
46 }
47
48 #ifdef CONFIG_STREAM_FLASH_ERASE
49 int rc;
50 struct flash_pages_info page;
51 off_t offset = (off_t) (ctx->offset + ctx->bytes_written) - 1;
52
53 /* Update the last erased page to avoid deleting already
54 * written data.
55 */
56 if (ctx->bytes_written > 0) {
57 rc = flash_get_page_info_by_offs(ctx->fdev, offset,
58 &page);
59 if (rc != 0) {
60 LOG_ERR("Error %d while getting page info", rc);
61 return rc;
62 }
63 ctx->last_erased_page_start_offset = page.start_offset;
64 } else {
65 ctx->last_erased_page_start_offset = -1;
66 }
67 #endif /* CONFIG_STREAM_FLASH_ERASE */
68 }
69
70 return 0;
71 }
72
73 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
74
75 #ifdef CONFIG_STREAM_FLASH_ERASE
76
stream_flash_erase_page(struct stream_flash_ctx * ctx,off_t off)77 int stream_flash_erase_page(struct stream_flash_ctx *ctx, off_t off)
78 {
79 int rc;
80 struct flash_pages_info page;
81
82 rc = flash_get_page_info_by_offs(ctx->fdev, off, &page);
83 if (rc != 0) {
84 LOG_ERR("Error %d while getting page info", rc);
85 return rc;
86 }
87
88 if (ctx->last_erased_page_start_offset == page.start_offset) {
89 return 0;
90 }
91
92 LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
93
94 rc = flash_erase(ctx->fdev, page.start_offset, page.size);
95
96 if (rc != 0) {
97 LOG_ERR("Error %d while erasing page", rc);
98 } else {
99 ctx->last_erased_page_start_offset = page.start_offset;
100 }
101
102 return rc;
103 }
104
105 #endif /* CONFIG_STREAM_FLASH_ERASE */
106
flash_sync(struct stream_flash_ctx * ctx)107 static int flash_sync(struct stream_flash_ctx *ctx)
108 {
109 int rc = 0;
110 size_t write_addr = ctx->offset + ctx->bytes_written;
111 size_t buf_bytes_aligned;
112 size_t fill_length;
113 uint8_t filler;
114
115
116 if (ctx->buf_bytes == 0) {
117 return 0;
118 }
119
120 if (IS_ENABLED(CONFIG_STREAM_FLASH_ERASE)) {
121
122 rc = stream_flash_erase_page(ctx,
123 write_addr + ctx->buf_bytes - 1);
124 if (rc < 0) {
125 LOG_ERR("stream_flash_erase_page err %d offset=0x%08zx",
126 rc, write_addr);
127 return rc;
128 }
129 }
130
131 fill_length = flash_get_write_block_size(ctx->fdev);
132 if (ctx->buf_bytes % fill_length) {
133 fill_length -= ctx->buf_bytes % fill_length;
134 filler = flash_get_parameters(ctx->fdev)->erase_value;
135
136 memset(ctx->buf + ctx->buf_bytes, filler, fill_length);
137 } else {
138 fill_length = 0;
139 }
140
141 buf_bytes_aligned = ctx->buf_bytes + fill_length;
142 rc = flash_write(ctx->fdev, write_addr, ctx->buf, buf_bytes_aligned);
143
144 if (rc != 0) {
145 LOG_ERR("flash_write error %d offset=0x%08zx", rc,
146 write_addr);
147 return rc;
148 }
149
150 if (ctx->callback) {
151 /* Invert to ensure that caller is able to discover a faulty
152 * flash_read() even if no error code is returned.
153 */
154 for (int i = 0; i < ctx->buf_bytes; i++) {
155 ctx->buf[i] = ~ctx->buf[i];
156 }
157
158 rc = flash_read(ctx->fdev, write_addr, ctx->buf,
159 ctx->buf_bytes);
160 if (rc != 0) {
161 LOG_ERR("flash read failed: %d", rc);
162 return rc;
163 }
164
165 rc = ctx->callback(ctx->buf, ctx->buf_bytes, write_addr);
166 if (rc != 0) {
167 LOG_ERR("callback failed: %d", rc);
168 return rc;
169 }
170 }
171
172 ctx->bytes_written += ctx->buf_bytes;
173 ctx->buf_bytes = 0U;
174
175 return rc;
176 }
177
stream_flash_buffered_write(struct stream_flash_ctx * ctx,const uint8_t * data,size_t len,bool flush)178 int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const uint8_t *data,
179 size_t len, bool flush)
180 {
181 int processed = 0;
182 int rc = 0;
183 int buf_empty_bytes;
184
185 if (!ctx) {
186 return -EFAULT;
187 }
188
189 if (ctx->bytes_written + ctx->buf_bytes + len > ctx->available) {
190 return -ENOMEM;
191 }
192
193 while ((len - processed) >=
194 (buf_empty_bytes = ctx->buf_len - ctx->buf_bytes)) {
195 memcpy(ctx->buf + ctx->buf_bytes, data + processed,
196 buf_empty_bytes);
197
198 ctx->buf_bytes = ctx->buf_len;
199 rc = flash_sync(ctx);
200
201 if (rc != 0) {
202 return rc;
203 }
204
205 processed += buf_empty_bytes;
206 }
207
208 /* place rest of the data into ctx->buf */
209 if (processed < len) {
210 memcpy(ctx->buf + ctx->buf_bytes,
211 data + processed, len - processed);
212 ctx->buf_bytes += len - processed;
213 }
214
215 if (flush && ctx->buf_bytes > 0) {
216 rc = flash_sync(ctx);
217 }
218
219 return rc;
220 }
221
stream_flash_bytes_written(struct stream_flash_ctx * ctx)222 size_t stream_flash_bytes_written(struct stream_flash_ctx *ctx)
223 {
224 return ctx->bytes_written;
225 }
226
227 struct _inspect_flash {
228 size_t buf_len;
229 size_t total_size;
230 };
231
find_flash_total_size(const struct flash_pages_info * info,void * data)232 static bool find_flash_total_size(const struct flash_pages_info *info,
233 void *data)
234 {
235 struct _inspect_flash *ctx = (struct _inspect_flash *) data;
236
237 if (ctx->buf_len > info->size) {
238 LOG_ERR("Buffer size is bigger than page");
239 ctx->total_size = 0;
240 return false;
241 }
242
243 ctx->total_size += info->size;
244
245 return true;
246 }
247
stream_flash_init(struct stream_flash_ctx * ctx,const struct device * fdev,uint8_t * buf,size_t buf_len,size_t offset,size_t size,stream_flash_callback_t cb)248 int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
249 uint8_t *buf, size_t buf_len, size_t offset, size_t size,
250 stream_flash_callback_t cb)
251 {
252 if (!ctx || !fdev || !buf) {
253 return -EFAULT;
254 }
255
256 #ifdef CONFIG_STREAM_FLASH_PROGRESS
257 int rc = settings_subsys_init();
258
259 if (rc != 0) {
260 LOG_ERR("Error %d initializing settings subsystem", rc);
261 return rc;
262 }
263 #endif
264
265 struct _inspect_flash inspect_flash_ctx = {
266 .buf_len = buf_len,
267 .total_size = 0
268 };
269
270 if (buf_len % flash_get_write_block_size(fdev)) {
271 LOG_ERR("Buffer size is not aligned to minimal write-block-size");
272 return -EFAULT;
273 }
274
275 /* Calculate the total size of the flash device */
276 flash_page_foreach(fdev, find_flash_total_size, &inspect_flash_ctx);
277
278 /* The flash size counted should never be equal zero */
279 if (inspect_flash_ctx.total_size == 0) {
280 return -EFAULT;
281 }
282
283 if ((offset + size) > inspect_flash_ctx.total_size ||
284 offset % flash_get_write_block_size(fdev)) {
285 LOG_ERR("Incorrect parameter");
286 return -EFAULT;
287 }
288
289 ctx->fdev = fdev;
290 ctx->buf = buf;
291 ctx->buf_len = buf_len;
292 ctx->bytes_written = 0;
293 ctx->buf_bytes = 0U;
294 ctx->offset = offset;
295 ctx->available = (size == 0 ? inspect_flash_ctx.total_size - offset :
296 size);
297 ctx->callback = cb;
298
299 #ifdef CONFIG_STREAM_FLASH_ERASE
300 ctx->last_erased_page_start_offset = -1;
301 #endif
302
303 return 0;
304 }
305
306 #ifdef CONFIG_STREAM_FLASH_PROGRESS
307
stream_flash_progress_load(struct stream_flash_ctx * ctx,const char * settings_key)308 int stream_flash_progress_load(struct stream_flash_ctx *ctx,
309 const char *settings_key)
310 {
311 if (!ctx || !settings_key) {
312 return -EFAULT;
313 }
314
315 int rc = settings_load_subtree_direct(settings_key,
316 settings_direct_loader,
317 (void *) ctx);
318
319 if (rc != 0) {
320 LOG_ERR("Error %d while loading progress for \"%s\"",
321 rc, settings_key);
322 }
323
324 return rc;
325 }
326
stream_flash_progress_save(struct stream_flash_ctx * ctx,const char * settings_key)327 int stream_flash_progress_save(struct stream_flash_ctx *ctx,
328 const char *settings_key)
329 {
330 if (!ctx || !settings_key) {
331 return -EFAULT;
332 }
333
334 int rc = settings_save_one(settings_key,
335 &ctx->bytes_written,
336 sizeof(ctx->bytes_written));
337
338 if (rc != 0) {
339 LOG_ERR("Error %d while storing progress for \"%s\"",
340 rc, settings_key);
341 }
342
343 return rc;
344 }
345
stream_flash_progress_clear(struct stream_flash_ctx * ctx,const char * settings_key)346 int stream_flash_progress_clear(struct stream_flash_ctx *ctx,
347 const char *settings_key)
348 {
349 if (!ctx || !settings_key) {
350 return -EFAULT;
351 }
352
353 int rc = settings_delete(settings_key);
354
355 if (rc != 0) {
356 LOG_ERR("Error %d while deleting progress for \"%s\"",
357 rc, settings_key);
358 }
359
360 return rc;
361 }
362
363 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
364