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