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