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->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 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
80 int rc;
81 struct flash_pages_info page;
82
83 if (off < ctx->offset || (off - ctx->offset) >= ctx->available) {
84 LOG_ERR("Offset out of designated range");
85 return -ERANGE;
86 }
87
88 #if defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE)
89 /* There are both types of devices */
90 const struct flash_parameters *fparams = flash_get_parameters(ctx->fdev);
91
92 /* Stream flash does not rely on erase, it does it when device needs it */
93 if (!(flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT)) {
94 return 0;
95 }
96 #endif
97 rc = flash_get_page_info_by_offs(ctx->fdev, off, &page);
98 if (rc != 0) {
99 LOG_ERR("Error %d while getting page info", rc);
100 return rc;
101 }
102
103 if (ctx->last_erased_page_start_offset == page.start_offset) {
104 return 0;
105 }
106
107 LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
108
109 rc = flash_erase(ctx->fdev, page.start_offset, page.size);
110
111 if (rc != 0) {
112 LOG_ERR("Error %d while erasing page", rc);
113 } else {
114 ctx->last_erased_page_start_offset = page.start_offset;
115 }
116
117 return rc;
118 #else
119 return 0;
120 #endif
121 }
122
123 #endif /* CONFIG_STREAM_FLASH_ERASE */
124
flash_sync(struct stream_flash_ctx * ctx)125 static int flash_sync(struct stream_flash_ctx *ctx)
126 {
127 int rc = 0;
128 size_t write_addr = ctx->offset + ctx->bytes_written;
129 size_t buf_bytes_aligned;
130 size_t fill_length;
131 uint8_t filler;
132
133
134 if (ctx->buf_bytes == 0) {
135 return 0;
136 }
137
138 if (IS_ENABLED(CONFIG_STREAM_FLASH_ERASE)) {
139
140 rc = stream_flash_erase_page(ctx,
141 write_addr + ctx->buf_bytes - 1);
142 if (rc < 0) {
143 LOG_ERR("stream_flash_erase_page err %d offset=0x%08zx",
144 rc, write_addr);
145 return rc;
146 }
147 }
148
149 fill_length = ctx->write_block_size;
150 if (ctx->buf_bytes % fill_length) {
151 fill_length -= ctx->buf_bytes % fill_length;
152 filler = ctx->erase_value;
153
154 memset(ctx->buf + ctx->buf_bytes, filler, fill_length);
155 } else {
156 fill_length = 0;
157 }
158
159 buf_bytes_aligned = ctx->buf_bytes + fill_length;
160 rc = flash_write(ctx->fdev, write_addr, ctx->buf, buf_bytes_aligned);
161
162 if (rc != 0) {
163 LOG_ERR("flash_write error %d offset=0x%08zx", rc,
164 write_addr);
165 return rc;
166 }
167
168 #if defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
169
170 if (ctx->callback) {
171 /* Invert to ensure that caller is able to discover a faulty
172 * flash_read() even if no error code is returned.
173 */
174 for (int i = 0; i < ctx->buf_bytes; i++) {
175 ctx->buf[i] = ~ctx->buf[i];
176 }
177
178 rc = flash_read(ctx->fdev, write_addr, ctx->buf,
179 ctx->buf_bytes);
180 if (rc != 0) {
181 LOG_ERR("flash read failed: %d", rc);
182 return rc;
183 }
184
185 rc = ctx->callback(ctx->buf, ctx->buf_bytes, write_addr);
186 if (rc != 0) {
187 LOG_ERR("callback failed: %d", rc);
188 return rc;
189 }
190 }
191
192 #endif
193
194 ctx->bytes_written += ctx->buf_bytes;
195 ctx->buf_bytes = 0U;
196
197 return rc;
198 }
199
stream_flash_buffered_write(struct stream_flash_ctx * ctx,const uint8_t * data,size_t len,bool flush)200 int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const uint8_t *data,
201 size_t len, bool flush)
202 {
203 int processed = 0;
204 int rc = 0;
205 int buf_empty_bytes;
206
207 if (!ctx) {
208 return -EFAULT;
209 }
210
211 if (ctx->bytes_written + ctx->buf_bytes + len > ctx->available) {
212 return -ENOMEM;
213 }
214
215 while ((len - processed) >=
216 (buf_empty_bytes = ctx->buf_len - ctx->buf_bytes)) {
217 memcpy(ctx->buf + ctx->buf_bytes, data + processed,
218 buf_empty_bytes);
219
220 ctx->buf_bytes = ctx->buf_len;
221 rc = flash_sync(ctx);
222
223 if (rc != 0) {
224 return rc;
225 }
226
227 processed += buf_empty_bytes;
228 }
229
230 /* place rest of the data into ctx->buf */
231 if (processed < len) {
232 memcpy(ctx->buf + ctx->buf_bytes,
233 data + processed, len - processed);
234 ctx->buf_bytes += len - processed;
235 }
236
237 if (flush && ctx->buf_bytes > 0) {
238 rc = flash_sync(ctx);
239 }
240
241 return rc;
242 }
243
stream_flash_bytes_written(const struct stream_flash_ctx * ctx)244 size_t stream_flash_bytes_written(const struct stream_flash_ctx *ctx)
245 {
246 return ctx->bytes_written;
247 }
248
249 struct _inspect_flash {
250 size_t buf_len;
251 size_t total_size;
252 };
253
find_flash_total_size(const struct flash_pages_info * info,void * data)254 static bool find_flash_total_size(const struct flash_pages_info *info,
255 void *data)
256 {
257 struct _inspect_flash *ctx = (struct _inspect_flash *) data;
258
259 if (ctx->buf_len > info->size) {
260 LOG_ERR("Buffer size is bigger than page");
261 ctx->total_size = 0;
262 return false;
263 }
264
265 ctx->total_size += info->size;
266
267 return true;
268 }
269
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)270 int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
271 uint8_t *buf, size_t buf_len, size_t offset, size_t size,
272 stream_flash_callback_t cb)
273 {
274 const struct flash_parameters *params;
275 if (!ctx || !fdev || !buf) {
276 return -EFAULT;
277 }
278
279 #ifdef CONFIG_STREAM_FLASH_PROGRESS
280 int rc = settings_subsys_init();
281
282 if (rc != 0) {
283 LOG_ERR("Error %d initializing settings subsystem", rc);
284 return rc;
285 }
286 #endif
287
288 struct _inspect_flash inspect_flash_ctx = {
289 .buf_len = buf_len,
290 .total_size = 0
291 };
292
293 params = flash_get_parameters(fdev);
294 ctx->write_block_size = params->write_block_size;
295
296 if (buf_len % ctx->write_block_size) {
297 LOG_ERR("Buffer size is not aligned to minimal write-block-size");
298 return -EFAULT;
299 }
300
301 /* Calculate the total size of the flash device */
302 flash_page_foreach(fdev, find_flash_total_size, &inspect_flash_ctx);
303
304 /* The flash size counted should never be equal zero */
305 if (inspect_flash_ctx.total_size == 0) {
306 return -EFAULT;
307 }
308
309 if ((offset + size) > inspect_flash_ctx.total_size ||
310 offset % ctx->write_block_size) {
311 LOG_ERR("Incorrect parameter");
312 return -EFAULT;
313 }
314
315
316 ctx->fdev = fdev;
317 ctx->buf = buf;
318 ctx->buf_len = buf_len;
319 ctx->bytes_written = 0;
320 ctx->buf_bytes = 0U;
321 ctx->offset = offset;
322 ctx->available = (size == 0 ? inspect_flash_ctx.total_size - offset :
323 size);
324
325 #if !defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
326 ARG_UNUSED(cb);
327 #else
328 ctx->callback = cb;
329 #endif
330
331 #ifdef CONFIG_STREAM_FLASH_ERASE
332 ctx->last_erased_page_start_offset = -1;
333 #endif
334 ctx->erase_value = params->erase_value;
335
336 return 0;
337 }
338
339 #ifdef CONFIG_STREAM_FLASH_PROGRESS
340
stream_flash_progress_load(struct stream_flash_ctx * ctx,const char * settings_key)341 int stream_flash_progress_load(struct stream_flash_ctx *ctx,
342 const char *settings_key)
343 {
344 if (!ctx || !settings_key) {
345 return -EFAULT;
346 }
347
348 int rc = settings_load_subtree_direct(settings_key,
349 settings_direct_loader,
350 (void *) ctx);
351
352 if (rc != 0) {
353 LOG_ERR("Error %d while loading progress for \"%s\"",
354 rc, settings_key);
355 }
356
357 return rc;
358 }
359
stream_flash_progress_save(const struct stream_flash_ctx * ctx,const char * settings_key)360 int stream_flash_progress_save(const struct stream_flash_ctx *ctx,
361 const char *settings_key)
362 {
363 if (!ctx || !settings_key) {
364 return -EFAULT;
365 }
366
367 int rc = settings_save_one(settings_key,
368 &ctx->bytes_written,
369 sizeof(ctx->bytes_written));
370
371 if (rc != 0) {
372 LOG_ERR("Error %d while storing progress for \"%s\"",
373 rc, settings_key);
374 }
375
376 return rc;
377 }
378
stream_flash_progress_clear(const struct stream_flash_ctx * ctx,const char * settings_key)379 int stream_flash_progress_clear(const struct stream_flash_ctx *ctx,
380 const char *settings_key)
381 {
382 if (!ctx || !settings_key) {
383 return -EFAULT;
384 }
385
386 int rc = settings_delete(settings_key);
387
388 if (rc != 0) {
389 LOG_ERR("Error %d while deleting progress for \"%s\"",
390 rc, settings_key);
391 }
392
393 return rc;
394 }
395
396 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
397