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