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 cb_len = read_cb(cb_arg, &bytes_written,
32 sizeof(bytes_written));
33
34 if (cb_len != sizeof(ctx->bytes_written)) {
35 LOG_ERR("Unable to read bytes_written from storage");
36 return cb_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->erased_up_to = page.start_offset + page.size - ctx->offset;
64 } else {
65 ctx->erased_up_to = 0;
66 }
67 #endif /* CONFIG_STREAM_FLASH_ERASE */
68 }
69
70 return 0;
71 }
72
73 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
74
75 /* Will erase at most what is required to append given size, If already
76 * erased space can accommodate requested size, then no new page will
77 * be erased.
78 * Note that this function is supposed to fulfill hardware requirements
79 * for erase prior to write or allow faster writes when hardware supports
80 * erase as means to speed up writes, and will do nothing on devices that
81 * that not require erase before write.
82 */
stream_flash_erase_to_append(struct stream_flash_ctx * ctx,size_t size)83 static int stream_flash_erase_to_append(struct stream_flash_ctx *ctx, size_t size)
84 {
85 int rc = 0;
86 #if defined(CONFIG_STREAM_FLASH_ERASE)
87 struct flash_pages_info page;
88 #if defined(CONFIG_STREAM_FLASH_ERASE_ONLY_WHEN_SUPPORTED)
89 const struct flash_parameters *fparams = flash_get_parameters(ctx->fdev);
90 #endif
91
92 /* ctx->erased_up_to points to first offset that has not yet been erased,
93 * relative to ctx->offset.
94 */
95 if (ctx->bytes_written + size <= ctx->erased_up_to) {
96 return 0;
97 }
98
99 /* Trying to append beyond available range? */
100 if (ctx->bytes_written + size > ctx->available) {
101 return -ERANGE;
102 }
103
104 #if defined(CONFIG_STREAM_FLASH_ERASE_ONLY_WHEN_SUPPORTED)
105 /* Stream flash does not rely on erase, it does it when device needs it */
106 if (!(flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT)) {
107 return 0;
108 }
109 #endif
110 /* Note that ctx->erased_up_to is offset relative to ctx->offset that
111 * points to first byte not yet erased.
112 */
113 rc = flash_get_page_info_by_offs(ctx->fdev, ctx->offset + ctx->erased_up_to, &page);
114 if (rc != 0) {
115 LOG_ERR("Error %d while getting page info", rc);
116 return rc;
117 }
118
119 LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
120
121 rc = flash_erase(ctx->fdev, page.start_offset, page.size);
122
123 if (rc != 0) {
124 LOG_ERR("Error %d while erasing page", rc);
125 } else {
126 ctx->erased_up_to += page.size;
127 }
128 #endif
129 return rc;
130 }
131
132 #if defined(CONFIG_STREAM_FLASH_ERASE)
133
stream_flash_erase_page(struct stream_flash_ctx * ctx,off_t off)134 int stream_flash_erase_page(struct stream_flash_ctx *ctx, off_t off)
135 {
136 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
137 int rc;
138 struct flash_pages_info page;
139
140 if (off < ctx->offset || (off - ctx->offset) >= ctx->available) {
141 LOG_ERR("Offset out of designated range");
142 return -ERANGE;
143 }
144
145 /* Do not allow pages that have already been erased */
146 if ((off - ctx->offset) < ctx->erased_up_to) {
147 return -EINVAL;
148 }
149
150 #if defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE)
151 /* There are both types of devices */
152 const struct flash_parameters *fparams = flash_get_parameters(ctx->fdev);
153
154 /* Stream flash does not rely on erase, it does it when device needs it */
155 if (!(flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT)) {
156 return 0;
157 }
158 #endif
159 rc = flash_get_page_info_by_offs(ctx->fdev, off, &page);
160 if (rc != 0) {
161 LOG_ERR("Error %d while getting page info", rc);
162 return rc;
163 }
164
165 if (ctx->erased_up_to >= page.start_offset + page.size) {
166 return 0;
167 }
168
169 LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
170
171 rc = flash_erase(ctx->fdev, page.start_offset, page.size);
172
173 if (rc != 0) {
174 LOG_ERR("Error %d while erasing page", rc);
175 } else {
176 ctx->erased_up_to = page.start_offset + page.size;
177 }
178
179 return rc;
180 #else
181 return 0;
182 #endif
183 }
184
185 #endif /* CONFIG_STREAM_FLASH_ERASE */
186
flash_sync(struct stream_flash_ctx * ctx)187 static int flash_sync(struct stream_flash_ctx *ctx)
188 {
189 int rc = 0;
190 size_t write_addr = ctx->offset + ctx->bytes_written;
191 size_t buf_bytes_aligned;
192 size_t fill_length;
193 uint8_t filler;
194
195
196 if (ctx->buf_bytes == 0) {
197 return 0;
198 }
199
200 if (IS_ENABLED(CONFIG_STREAM_FLASH_ERASE)) {
201
202 rc = stream_flash_erase_to_append(ctx, ctx->buf_bytes);
203 if (rc < 0) {
204 LOG_ERR("stream_flash_forward_erase %d range=0x%08zx",
205 rc, ctx->buf_bytes);
206 return rc;
207 }
208 }
209
210 fill_length = ctx->write_block_size;
211 if (ctx->buf_bytes % fill_length) {
212 fill_length -= ctx->buf_bytes % fill_length;
213 filler = ctx->erase_value;
214
215 memset(ctx->buf + ctx->buf_bytes, filler, fill_length);
216 } else {
217 fill_length = 0;
218 }
219
220 buf_bytes_aligned = ctx->buf_bytes + fill_length;
221 rc = flash_write(ctx->fdev, write_addr, ctx->buf, buf_bytes_aligned);
222
223 if (rc != 0) {
224 LOG_ERR("flash_write error %d offset=0x%08zx", rc,
225 write_addr);
226 return rc;
227 }
228
229 #if defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
230
231 if (ctx->callback) {
232 /* Invert to ensure that caller is able to discover a faulty
233 * flash_read() even if no error code is returned.
234 */
235 for (int i = 0; i < ctx->buf_bytes; i++) {
236 ctx->buf[i] = ~ctx->buf[i];
237 }
238
239 rc = flash_read(ctx->fdev, write_addr, ctx->buf,
240 ctx->buf_bytes);
241 if (rc != 0) {
242 LOG_ERR("flash read failed: %d", rc);
243 return rc;
244 }
245
246 rc = ctx->callback(ctx->buf, ctx->buf_bytes, write_addr);
247 if (rc != 0) {
248 LOG_ERR("callback failed: %d", rc);
249 return rc;
250 }
251 }
252
253 #endif
254
255 ctx->bytes_written += ctx->buf_bytes;
256 ctx->buf_bytes = 0U;
257
258 return rc;
259 }
260
stream_flash_buffered_write(struct stream_flash_ctx * ctx,const uint8_t * data,size_t len,bool flush)261 int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const uint8_t *data,
262 size_t len, bool flush)
263 {
264 int processed = 0;
265 int rc = 0;
266 int buf_empty_bytes;
267
268 if (!ctx) {
269 return -EFAULT;
270 }
271
272 if (ctx->bytes_written + ctx->buf_bytes + len > ctx->available) {
273 return -ENOMEM;
274 }
275
276 while ((len - processed) >=
277 (buf_empty_bytes = ctx->buf_len - ctx->buf_bytes)) {
278 memcpy(ctx->buf + ctx->buf_bytes, data + processed,
279 buf_empty_bytes);
280
281 ctx->buf_bytes = ctx->buf_len;
282 rc = flash_sync(ctx);
283
284 if (rc != 0) {
285 return rc;
286 }
287
288 processed += buf_empty_bytes;
289 }
290
291 /* place rest of the data into ctx->buf */
292 if (processed < len) {
293 memcpy(ctx->buf + ctx->buf_bytes,
294 data + processed, len - processed);
295 ctx->buf_bytes += len - processed;
296 }
297
298 if (flush && ctx->buf_bytes > 0) {
299 rc = flash_sync(ctx);
300 }
301
302 return rc;
303 }
304
stream_flash_bytes_written(const struct stream_flash_ctx * ctx)305 size_t stream_flash_bytes_written(const struct stream_flash_ctx *ctx)
306 {
307 return ctx->bytes_written;
308 }
309
310 #ifdef CONFIG_STREAM_FLASH_INSPECT
311 struct _inspect_flash {
312 size_t buf_len;
313 size_t total_size;
314 };
315
find_flash_total_size(const struct flash_pages_info * info,void * data)316 static bool find_flash_total_size(const struct flash_pages_info *info,
317 void *data)
318 {
319 struct _inspect_flash *ctx = (struct _inspect_flash *) data;
320
321 if (ctx->buf_len > info->size) {
322 LOG_ERR("Buffer size is bigger than page");
323 ctx->total_size = 0;
324 return false;
325 }
326
327 ctx->total_size += info->size;
328
329 return true;
330 }
331
332 /* Internal function make sure *ctx is not NULL, no redundant check here */
inspect_device(const struct stream_flash_ctx * ctx)333 static inline int inspect_device(const struct stream_flash_ctx *ctx)
334 {
335 struct _inspect_flash inspect_flash_ctx = {
336 .buf_len = ctx->buf_len,
337 .total_size = 0
338 };
339
340 /* Calculate the total size of the flash device, and inspect pages
341 * while doing so.
342 */
343 flash_page_foreach(ctx->fdev, find_flash_total_size, &inspect_flash_ctx);
344
345 if (inspect_flash_ctx.total_size == 0) {
346 LOG_ERR("Device seems to have 0 size");
347 return -EFAULT;
348 } else if (inspect_flash_ctx.total_size < (ctx->offset + ctx->available)) {
349 LOG_ERR("Requested range overflows device size");
350 return -EFAULT;
351 }
352
353 return 0;
354 }
355 #else
inspect_device(const struct stream_flash_ctx * ctx)356 static inline int inspect_device(const struct stream_flash_ctx *ctx)
357 {
358 ARG_UNUSED(ctx);
359 return 0;
360 }
361 #endif
362
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)363 int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
364 uint8_t *buf, size_t buf_len, size_t offset, size_t size,
365 stream_flash_callback_t cb)
366 {
367 const struct flash_parameters *params;
368
369 if (!ctx || !fdev || !buf) {
370 return -EFAULT;
371 }
372
373 params = flash_get_parameters(fdev);
374
375 if (buf_len % params->write_block_size) {
376 LOG_ERR("Buffer size is not aligned to minimal write-block-size");
377 return -EFAULT;
378 }
379
380 if (offset % params->write_block_size) {
381 LOG_ERR("Incorrect parameter");
382 return -EFAULT;
383 }
384
385 if (size == 0 || size % params->write_block_size) {
386 LOG_ERR("Size is incorrect");
387 return -EFAULT;
388 }
389
390 ctx->fdev = fdev;
391 ctx->buf = buf;
392 ctx->buf_len = buf_len;
393 ctx->bytes_written = 0;
394 ctx->buf_bytes = 0U;
395 ctx->offset = offset;
396 ctx->available = size;
397 ctx->write_block_size = params->write_block_size;
398
399 #if !defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
400 ARG_UNUSED(cb);
401 #else
402 ctx->callback = cb;
403 #endif
404
405
406 #ifdef CONFIG_STREAM_FLASH_ERASE
407 ctx->erased_up_to = 0;
408 #endif
409 ctx->erase_value = params->erase_value;
410
411 /* Inspection is deliberately done once context has been filled in */
412 if (IS_ENABLED(CONFIG_STREAM_FLASH_INSPECT)) {
413 int ret = inspect_device(ctx);
414
415 if (ret != 0) {
416 /* No log here, the inspect_device already does logging */
417 return ret;
418 }
419 }
420
421
422 return 0;
423 }
424
425 #ifdef CONFIG_STREAM_FLASH_PROGRESS
stream_flash_settings_init(void)426 static int stream_flash_settings_init(void)
427 {
428 int rc = settings_subsys_init();
429
430 if (rc != 0) {
431 LOG_ERR("Error %d initializing settings subsystem", rc);
432 }
433 return rc;
434 }
435
stream_flash_progress_load(struct stream_flash_ctx * ctx,const char * settings_key)436 int stream_flash_progress_load(struct stream_flash_ctx *ctx,
437 const char *settings_key)
438 {
439 if (!ctx || !settings_key) {
440 return -EFAULT;
441 }
442
443 int rc = stream_flash_settings_init();
444
445 if (rc == 0) {
446 rc = settings_load_subtree_direct(settings_key, settings_direct_loader,
447 (void *)ctx);
448 }
449
450 if (rc != 0) {
451 LOG_ERR("Error %d while loading progress for \"%s\"",
452 rc, settings_key);
453 }
454
455 return rc;
456 }
457
stream_flash_progress_save(const struct stream_flash_ctx * ctx,const char * settings_key)458 int stream_flash_progress_save(const struct stream_flash_ctx *ctx,
459 const char *settings_key)
460 {
461 if (!ctx || !settings_key) {
462 return -EFAULT;
463 }
464
465 int rc = stream_flash_settings_init();
466
467 if (rc == 0) {
468 rc = settings_save_one(settings_key, &ctx->bytes_written,
469 sizeof(ctx->bytes_written));
470 }
471
472 if (rc != 0) {
473 LOG_ERR("Error %d while storing progress for \"%s\"",
474 rc, settings_key);
475 }
476
477 return rc;
478 }
479
stream_flash_progress_clear(const struct stream_flash_ctx * ctx,const char * settings_key)480 int stream_flash_progress_clear(const struct stream_flash_ctx *ctx,
481 const char *settings_key)
482 {
483 if (!ctx || !settings_key) {
484 return -EFAULT;
485 }
486
487 int rc = stream_flash_settings_init();
488
489 if (rc == 0) {
490 rc = settings_delete(settings_key);
491 }
492
493 if (rc != 0) {
494 LOG_ERR("Error %d while deleting progress for \"%s\"",
495 rc, settings_key);
496 }
497
498 return rc;
499 }
500
501 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
502