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