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
stream_flash_bytes_buffered(const struct stream_flash_ctx * ctx)310 size_t stream_flash_bytes_buffered(const struct stream_flash_ctx *ctx)
311 {
312 return ctx->buf_bytes;
313 }
314
315 #ifdef CONFIG_STREAM_FLASH_INSPECT
316 struct _inspect_flash {
317 size_t buf_len;
318 size_t total_size;
319 };
320
find_flash_total_size(const struct flash_pages_info * info,void * data)321 static bool find_flash_total_size(const struct flash_pages_info *info,
322 void *data)
323 {
324 struct _inspect_flash *ctx = (struct _inspect_flash *) data;
325
326 if (ctx->buf_len > info->size) {
327 LOG_ERR("Buffer size is bigger than page");
328 ctx->total_size = 0;
329 return false;
330 }
331
332 ctx->total_size += info->size;
333
334 return true;
335 }
336
337 /* Internal function make sure *ctx is not NULL, no redundant check here */
inspect_device(const struct stream_flash_ctx * ctx)338 static inline int inspect_device(const struct stream_flash_ctx *ctx)
339 {
340 struct _inspect_flash inspect_flash_ctx = {
341 .buf_len = ctx->buf_len,
342 .total_size = 0
343 };
344
345 /* Calculate the total size of the flash device, and inspect pages
346 * while doing so.
347 */
348 flash_page_foreach(ctx->fdev, find_flash_total_size, &inspect_flash_ctx);
349
350 if (inspect_flash_ctx.total_size == 0) {
351 LOG_ERR("Device seems to have 0 size");
352 return -EFAULT;
353 } else if (inspect_flash_ctx.total_size < (ctx->offset + ctx->available)) {
354 LOG_ERR("Requested range overflows device size");
355 return -EFAULT;
356 }
357
358 return 0;
359 }
360 #else
inspect_device(const struct stream_flash_ctx * ctx)361 static inline int inspect_device(const struct stream_flash_ctx *ctx)
362 {
363 ARG_UNUSED(ctx);
364 return 0;
365 }
366 #endif
367
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)368 int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
369 uint8_t *buf, size_t buf_len, size_t offset, size_t size,
370 stream_flash_callback_t cb)
371 {
372 const struct flash_parameters *params;
373
374 if (!ctx || !fdev || !buf) {
375 return -EFAULT;
376 }
377
378 params = flash_get_parameters(fdev);
379
380 if (buf_len % params->write_block_size) {
381 LOG_ERR("Buffer size is not aligned to minimal write-block-size");
382 return -EFAULT;
383 }
384
385 if (offset % params->write_block_size) {
386 LOG_ERR("Incorrect parameter");
387 return -EFAULT;
388 }
389
390 if (size == 0 || size % params->write_block_size) {
391 LOG_ERR("Size is incorrect");
392 return -EFAULT;
393 }
394
395 ctx->fdev = fdev;
396 ctx->buf = buf;
397 ctx->buf_len = buf_len;
398 ctx->bytes_written = 0;
399 ctx->buf_bytes = 0U;
400 ctx->offset = offset;
401 ctx->available = size;
402 ctx->write_block_size = params->write_block_size;
403
404 #if !defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
405 ARG_UNUSED(cb);
406 #else
407 ctx->callback = cb;
408 #endif
409
410
411 #ifdef CONFIG_STREAM_FLASH_ERASE
412 ctx->erased_up_to = 0;
413 #endif
414 ctx->erase_value = params->erase_value;
415
416 /* Inspection is deliberately done once context has been filled in */
417 if (IS_ENABLED(CONFIG_STREAM_FLASH_INSPECT)) {
418 int ret = inspect_device(ctx);
419
420 if (ret != 0) {
421 /* No log here, the inspect_device already does logging */
422 return ret;
423 }
424 }
425
426
427 return 0;
428 }
429
430 #ifdef CONFIG_STREAM_FLASH_PROGRESS
stream_flash_settings_init(void)431 static int stream_flash_settings_init(void)
432 {
433 int rc = settings_subsys_init();
434
435 if (rc != 0) {
436 LOG_ERR("Error %d initializing settings subsystem", rc);
437 }
438 return rc;
439 }
440
stream_flash_progress_load(struct stream_flash_ctx * ctx,const char * settings_key)441 int stream_flash_progress_load(struct stream_flash_ctx *ctx,
442 const char *settings_key)
443 {
444 if (!ctx || !settings_key) {
445 return -EFAULT;
446 }
447
448 int rc = stream_flash_settings_init();
449
450 if (rc == 0) {
451 rc = settings_load_subtree_direct(settings_key, settings_direct_loader,
452 (void *)ctx);
453 }
454
455 if (rc != 0) {
456 LOG_ERR("Error %d while loading progress for \"%s\"",
457 rc, settings_key);
458 }
459
460 return rc;
461 }
462
stream_flash_progress_save(const struct stream_flash_ctx * ctx,const char * settings_key)463 int stream_flash_progress_save(const struct stream_flash_ctx *ctx,
464 const char *settings_key)
465 {
466 if (!ctx || !settings_key) {
467 return -EFAULT;
468 }
469
470 int rc = stream_flash_settings_init();
471
472 if (rc == 0) {
473 rc = settings_save_one(settings_key, &ctx->bytes_written,
474 sizeof(ctx->bytes_written));
475 }
476
477 if (rc != 0) {
478 LOG_ERR("Error %d while storing progress for \"%s\"",
479 rc, settings_key);
480 }
481
482 return rc;
483 }
484
stream_flash_progress_clear(const struct stream_flash_ctx * ctx,const char * settings_key)485 int stream_flash_progress_clear(const struct stream_flash_ctx *ctx,
486 const char *settings_key)
487 {
488 if (!ctx || !settings_key) {
489 return -EFAULT;
490 }
491
492 int rc = stream_flash_settings_init();
493
494 if (rc == 0) {
495 rc = settings_delete(settings_key);
496 }
497
498 if (rc != 0) {
499 LOG_ERR("Error %d while deleting progress for \"%s\"",
500 rc, settings_key);
501 }
502
503 return rc;
504 }
505
506 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
507