1 /*
2  * Copyright (c) 2017-2023 Nordic Semiconductor ASA
3  * Copyright (c) 2018 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <zephyr/kernel.h>
15 #include <zephyr/devicetree.h>
16 #include <zephyr/drivers/flash.h>
17 #include <zephyr/shell/shell.h>
18 #include <zephyr/sys/util.h>
19 
20 /* Buffer is only needed for bytes that follow command and offset */
21 #define BUF_ARRAY_CNT (CONFIG_SHELL_ARGC_MAX - 2)
22 
23 #define FLASH_LOAD_BUF_MAX 256
24 
25 static const struct device *flash_load_dev;
26 static uint32_t flash_load_buf_size;
27 static uint32_t flash_load_addr;
28 static uint32_t flash_load_total;
29 static uint32_t flash_load_written;
30 static uint32_t flash_load_chunk;
31 
32 static uint32_t flash_load_boff;
33 static uint8_t flash_load_buf[FLASH_LOAD_BUF_MAX];
34 
35 /* This only issues compilation error when it would not be possible
36  * to extract at least one byte from command line arguments, yet
37  * it does not warrant successful writes if BUF_ARRAY_CNT
38  * is smaller than flash write alignment.
39  */
40 BUILD_ASSERT(BUF_ARRAY_CNT >= 1);
41 
42 static const struct device *const zephyr_flash_controller =
43 	DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_flash_controller));
44 
45 static uint8_t __aligned(4) test_arr[CONFIG_FLASH_SHELL_BUFFER_SIZE];
46 
parse_helper(const struct shell * sh,size_t * argc,char ** argv[],const struct device ** flash_dev,uint32_t * addr)47 static int parse_helper(const struct shell *sh, size_t *argc,
48 		char **argv[], const struct device * *flash_dev,
49 		uint32_t *addr)
50 {
51 	char *endptr;
52 
53 	*addr = strtoul((*argv)[1], &endptr, 16);
54 
55 	if (*endptr != '\0') {
56 		/* flash controller from user input */
57 		*flash_dev = shell_device_get_binding((*argv)[1]);
58 		if (!*flash_dev) {
59 			shell_error(sh, "Given flash device was not found");
60 			return -ENODEV;
61 		}
62 	} else if (zephyr_flash_controller != NULL) {
63 		/* default to zephyr,flash-controller */
64 		if (!device_is_ready(zephyr_flash_controller)) {
65 			shell_error(sh, "Default flash driver not ready");
66 			return -ENODEV;
67 		}
68 		*flash_dev = zephyr_flash_controller;
69 	} else {
70 		/* no flash controller given, no default available */
71 		shell_error(sh, "No flash device specified (required)");
72 		return -ENODEV;
73 	}
74 
75 	if (*endptr == '\0') {
76 		return 0;
77 	}
78 	if (*argc < 3) {
79 		shell_error(sh, "Missing address.");
80 		return -EINVAL;
81 	}
82 	*addr = strtoul((*argv)[2], &endptr, 16);
83 	(*argc)--;
84 	(*argv)++;
85 	return 0;
86 }
87 
cmd_erase(const struct shell * sh,size_t argc,char * argv[])88 static int cmd_erase(const struct shell *sh, size_t argc, char *argv[])
89 {
90 	int result = -ENOTSUP;
91 
92 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
93 	const struct device *flash_dev;
94 	uint32_t page_addr;
95 	uint32_t size;
96 
97 	result = parse_helper(sh, &argc, &argv, &flash_dev, &page_addr);
98 	if (result) {
99 		return result;
100 	}
101 	if (argc > 2) {
102 		size = strtoul(argv[2], NULL, 16);
103 	} else {
104 		struct flash_pages_info info;
105 
106 		result = flash_get_page_info_by_offs(flash_dev, page_addr,
107 						     &info);
108 
109 		if (result != 0) {
110 			shell_error(sh, "Could not determine page size, "
111 				    "code %d.", result);
112 			return -EINVAL;
113 		}
114 
115 		size = info.size;
116 	}
117 
118 	result = flash_erase(flash_dev, page_addr, size);
119 
120 	if (result) {
121 		shell_error(sh, "Erase Failed, code %d.", result);
122 	} else {
123 		shell_print(sh, "Erase success.");
124 	}
125 #endif
126 
127 	return result;
128 }
129 
cmd_write(const struct shell * sh,size_t argc,char * argv[])130 static int cmd_write(const struct shell *sh, size_t argc, char *argv[])
131 {
132 	uint32_t __aligned(4) check_array[BUF_ARRAY_CNT];
133 	uint32_t __aligned(4) buf_array[BUF_ARRAY_CNT];
134 	const struct device *flash_dev;
135 	uint32_t w_addr;
136 	int ret;
137 	size_t op_size;
138 
139 	ret = parse_helper(sh, &argc, &argv, &flash_dev, &w_addr);
140 	if (ret) {
141 		return ret;
142 	}
143 
144 	if (argc <= 2) {
145 		shell_error(sh, "Missing data to be written.");
146 		return -EINVAL;
147 	}
148 
149 	op_size = 0;
150 
151 	for (int i = 2; i < argc; i++) {
152 		int j = i - 2;
153 
154 		buf_array[j] = strtoul(argv[i], NULL, 16);
155 		check_array[j] = ~buf_array[j];
156 
157 		op_size += sizeof(buf_array[0]);
158 	}
159 
160 	if (flash_write(flash_dev, w_addr, buf_array, op_size) != 0) {
161 		shell_error(sh, "Write internal ERROR!");
162 		return -EIO;
163 	}
164 
165 	shell_print(sh, "Write OK.");
166 
167 	if (flash_read(flash_dev, w_addr, check_array, op_size) < 0) {
168 		shell_print(sh, "Verification read ERROR!");
169 		return -EIO;
170 	}
171 
172 	if (memcmp(buf_array, check_array, op_size) == 0) {
173 		shell_print(sh, "Verified.");
174 	} else {
175 		shell_error(sh, "Verification ERROR!");
176 		return -EIO;
177 	}
178 
179 	return 0;
180 }
181 
cmd_copy(const struct shell * sh,size_t argc,char * argv[])182 static int cmd_copy(const struct shell *sh, size_t argc, char *argv[])
183 {
184 	int ret;
185 	uint32_t size = 0;
186 	uint32_t src_offset = 0;
187 	uint32_t dst_offset = 0;
188 	const struct device *src_dev = NULL;
189 	const struct device *dst_dev = NULL;
190 
191 	if (argc < 5) {
192 		shell_error(sh, "missing parameters");
193 		return -EINVAL;
194 	}
195 
196 	src_dev = shell_device_get_binding(argv[1]);
197 	dst_dev = shell_device_get_binding(argv[2]);
198 	src_offset = strtoul(argv[3], NULL, 0);
199 	dst_offset = strtoul(argv[4], NULL, 0);
200 	/* size will be padded to write_size bytes */
201 	size = strtoul(argv[5], NULL, 0);
202 
203 	ret = flash_copy(src_dev, src_offset, dst_dev, dst_offset, size, flash_load_buf,
204 			 sizeof(flash_load_buf));
205 	if (ret < 0) {
206 		shell_error(sh, "%s failed: %d", "flash_copy()", ret);
207 		return -EIO;
208 	}
209 
210 	shell_print(sh, "Copied %u bytes from %s:%x to %s:%x", size, argv[1], src_offset, argv[2],
211 		    dst_offset);
212 
213 	return 0;
214 }
215 
cmd_read(const struct shell * sh,size_t argc,char * argv[])216 static int cmd_read(const struct shell *sh, size_t argc, char *argv[])
217 {
218 	const struct device *flash_dev;
219 	uint32_t addr;
220 	int todo;
221 	int upto;
222 	int cnt;
223 	int ret;
224 
225 	ret = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
226 	if (ret) {
227 		return ret;
228 	}
229 
230 	if (argc > 2) {
231 		cnt = strtoul(argv[2], NULL, 16);
232 	} else {
233 		cnt = 1;
234 	}
235 
236 	for (upto = 0; upto < cnt; upto += todo) {
237 		uint8_t data[SHELL_HEXDUMP_BYTES_IN_LINE];
238 
239 		todo = MIN(cnt - upto, SHELL_HEXDUMP_BYTES_IN_LINE);
240 		ret = flash_read(flash_dev, addr, data, todo);
241 		if (ret != 0) {
242 			shell_error(sh, "Read ERROR!");
243 			return -EIO;
244 		}
245 		shell_hexdump_line(sh, addr, data, todo);
246 		addr += todo;
247 	}
248 
249 	shell_print(sh, "");
250 
251 	return 0;
252 }
253 
cmd_test(const struct shell * sh,size_t argc,char * argv[])254 static int cmd_test(const struct shell *sh, size_t argc, char *argv[])
255 {
256 	const struct device *flash_dev;
257 	uint32_t repeat;
258 	int result;
259 	uint32_t addr;
260 	uint32_t size;
261 
262 	static uint8_t __aligned(4) check_arr[CONFIG_FLASH_SHELL_BUFFER_SIZE];
263 
264 	result = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
265 	if (result) {
266 		return result;
267 	}
268 
269 	size = strtoul(argv[2], NULL, 16);
270 	repeat = strtoul(argv[3], NULL, 16);
271 	if (size > CONFIG_FLASH_SHELL_BUFFER_SIZE) {
272 		shell_error(sh, "<size> must be at most 0x%x.",
273 			    CONFIG_FLASH_SHELL_BUFFER_SIZE);
274 		return -EINVAL;
275 	}
276 
277 	if (repeat == 0) {
278 		repeat = 1;
279 	}
280 
281 	for (uint32_t i = 0; i < size; i++) {
282 		test_arr[i] = (uint8_t)i;
283 	}
284 
285 	result = 0;
286 
287 	while (repeat--) {
288 		result = flash_erase(flash_dev, addr, size);
289 
290 		if (result) {
291 			shell_error(sh, "Erase Failed, code %d.", result);
292 			break;
293 		}
294 
295 		shell_print(sh, "Erase OK.");
296 
297 		result = flash_write(flash_dev, addr, test_arr, size);
298 
299 		if (result) {
300 			shell_error(sh, "Write failed, code %d", result);
301 			break;
302 		}
303 
304 		shell_print(sh, "Write OK.");
305 
306 		result = flash_read(flash_dev, addr, check_arr, size);
307 
308 		if (result < 0) {
309 			shell_print(sh, "Verification read failed, code: %d", result);
310 			break;
311 		}
312 
313 		if (memcmp(test_arr, check_arr, size) != 0) {
314 			shell_error(sh, "Verification ERROR!");
315 			break;
316 		}
317 
318 		shell_print(sh, "Verified OK.");
319 	}
320 
321 	if (result == 0) {
322 		shell_print(sh, "Erase-Write-Verify test done.");
323 	}
324 
325 	return result;
326 }
327 
328 #ifdef CONFIG_FLASH_SHELL_TEST_COMMANDS
329 const static uint8_t speed_types[][4] = { "B", "KiB", "MiB", "GiB" };
330 const static uint32_t speed_divisor = 1024;
331 
read_write_erase_validate(const struct shell * sh,size_t argc,char * argv[],uint32_t * size,uint32_t * repeat)332 static int read_write_erase_validate(const struct shell *sh, size_t argc, char *argv[],
333 				     uint32_t *size, uint32_t *repeat)
334 {
335 	if (argc < 4) {
336 		shell_error(sh, "Missing parameters: <device> <offset> <size> <repeat>");
337 		return -EINVAL;
338 	}
339 
340 	*size = strtoul(argv[2], NULL, 0);
341 	*repeat = strtoul(argv[3], NULL, 0);
342 
343 	if (*size == 0 || *size > CONFIG_FLASH_SHELL_BUFFER_SIZE) {
344 		shell_error(sh, "<size> must be between 0x1 and 0x%x.",
345 			    CONFIG_FLASH_SHELL_BUFFER_SIZE);
346 		return -EINVAL;
347 	}
348 
349 	if (*repeat == 0 || *repeat > 10) {
350 		shell_error(sh, "<repeat> must be between 1 and 10.");
351 		return -EINVAL;
352 	}
353 
354 	return 0;
355 }
356 
speed_output(const struct shell * sh,uint64_t total_time,double loops,double size)357 static void speed_output(const struct shell *sh, uint64_t total_time, double loops, double size)
358 {
359 	double time_per_loop = (double)total_time / loops;
360 	double throughput = size;
361 	uint8_t speed_index = 0;
362 
363 	if (time_per_loop > 0) {
364 		throughput /= (time_per_loop / 1000.0);
365 	}
366 
367 	while (throughput >= (double)speed_divisor && speed_index < ARRAY_SIZE(speed_types)) {
368 		throughput /= (double)speed_divisor;
369 		++speed_index;
370 	}
371 
372 	shell_print(sh, "Total: %llums, Per loop: ~%.0fms, Speed: ~%.1f%sps",
373 		    total_time, time_per_loop, throughput, speed_types[speed_index]);
374 }
375 
cmd_read_test(const struct shell * sh,size_t argc,char * argv[])376 static int cmd_read_test(const struct shell *sh, size_t argc, char *argv[])
377 {
378 
379 	const struct device *flash_dev;
380 	uint32_t repeat;
381 	int result;
382 	uint32_t addr;
383 	uint32_t size;
384 	uint64_t start_time;
385 	uint64_t loop_time;
386 	uint64_t total_time = 0;
387 	uint32_t loops = 0;
388 
389 	result = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
390 	if (result) {
391 		return result;
392 	}
393 
394 	result = read_write_erase_validate(sh, argc, argv, &size, &repeat);
395 	if (result) {
396 		return result;
397 	}
398 
399 	while (repeat--) {
400 		start_time = k_uptime_get();
401 		result = flash_read(flash_dev, addr, test_arr, size);
402 		loop_time = k_uptime_delta(&start_time);
403 
404 		if (result) {
405 			shell_error(sh, "Read failed: %d", result);
406 			break;
407 		}
408 
409 		++loops;
410 		total_time += loop_time;
411 		shell_print(sh, "Loop #%u done in %llums.", loops, loop_time);
412 	}
413 
414 	if (result == 0) {
415 		speed_output(sh, total_time, (double)loops, (double)size);
416 	}
417 
418 	return result;
419 }
420 
cmd_write_test(const struct shell * sh,size_t argc,char * argv[])421 static int cmd_write_test(const struct shell *sh, size_t argc, char *argv[])
422 {
423 	const struct device *flash_dev;
424 	uint32_t repeat;
425 	int result;
426 	uint32_t addr;
427 	uint32_t size;
428 	uint64_t start_time;
429 	uint64_t loop_time;
430 	uint64_t total_time = 0;
431 	uint32_t loops = 0;
432 
433 	result = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
434 	if (result) {
435 		return result;
436 	}
437 
438 	result = read_write_erase_validate(sh, argc, argv, &size, &repeat);
439 	if (result) {
440 		return result;
441 	}
442 
443 	for (uint32_t i = 0; i < size; i++) {
444 		test_arr[i] = (uint8_t)i;
445 	}
446 
447 	while (repeat--) {
448 		start_time = k_uptime_get();
449 		result = flash_write(flash_dev, addr, test_arr, size);
450 		loop_time = k_uptime_delta(&start_time);
451 
452 		if (result) {
453 			shell_error(sh, "Write failed: %d", result);
454 			break;
455 		}
456 
457 		++loops;
458 		total_time += loop_time;
459 		shell_print(sh, "Loop #%u done in %llu ticks.", loops, loop_time);
460 	}
461 
462 	if (result == 0) {
463 		speed_output(sh, total_time, (double)loops, (double)size);
464 	}
465 
466 	return result;
467 }
468 
cmd_erase_test(const struct shell * sh,size_t argc,char * argv[])469 static int cmd_erase_test(const struct shell *sh, size_t argc, char *argv[])
470 {
471 	const struct device *flash_dev;
472 	uint32_t repeat;
473 	int result;
474 	uint32_t addr;
475 	uint32_t size;
476 	uint64_t start_time;
477 	uint64_t loop_time;
478 	uint64_t total_time = 0;
479 	uint32_t loops = 0;
480 
481 	result = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
482 	if (result) {
483 		return result;
484 	}
485 
486 	result = read_write_erase_validate(sh, argc, argv, &size, &repeat);
487 	if (result) {
488 		return result;
489 	}
490 
491 	for (uint32_t i = 0; i < size; i++) {
492 		test_arr[i] = (uint8_t)i;
493 	}
494 
495 	while (repeat--) {
496 		start_time = k_uptime_get();
497 		result = flash_erase(flash_dev, addr, size);
498 		loop_time = k_uptime_delta(&start_time);
499 
500 		if (result) {
501 			shell_error(sh, "Erase failed: %d", result);
502 			break;
503 		}
504 
505 		++loops;
506 		total_time += loop_time;
507 		shell_print(sh, "Loop #%u done in %llums.", loops, loop_time);
508 	}
509 
510 	if (result == 0) {
511 		speed_output(sh, total_time, (double)loops, (double)size);
512 	}
513 
514 	return result;
515 }
516 
cmd_erase_write_test(const struct shell * sh,size_t argc,char * argv[])517 static int cmd_erase_write_test(const struct shell *sh, size_t argc, char *argv[])
518 {
519 	const struct device *flash_dev;
520 	uint32_t repeat;
521 	int result_erase = 0;
522 	int result_write = 0;
523 	uint32_t addr;
524 	uint32_t size;
525 	uint64_t start_time;
526 	uint64_t loop_time;
527 	uint64_t total_time = 0;
528 	uint32_t loops = 0;
529 
530 	result_erase = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
531 	if (result_erase) {
532 		return result_erase;
533 	}
534 
535 	result_erase = read_write_erase_validate(sh, argc, argv, &size, &repeat);
536 	if (result_erase) {
537 		return result_erase;
538 	}
539 
540 	for (uint32_t i = 0; i < size; i++) {
541 		test_arr[i] = (uint8_t)i;
542 	}
543 
544 	while (repeat--) {
545 		start_time = k_uptime_get();
546 		result_erase = flash_erase(flash_dev, addr, size);
547 		result_write = flash_write(flash_dev, addr, test_arr, size);
548 		loop_time = k_uptime_delta(&start_time);
549 
550 		if (result_erase) {
551 			shell_error(sh, "Erase failed: %d", result_erase);
552 			break;
553 		}
554 
555 		if (result_write) {
556 			shell_error(sh, "Write failed: %d", result_write);
557 			break;
558 		}
559 
560 		++loops;
561 		total_time += loop_time;
562 		shell_print(sh, "Loop #%u done in %llums.", loops, loop_time);
563 	}
564 
565 	if (result_erase == 0 && result_write == 0) {
566 		speed_output(sh, total_time, (double)loops, (double)size);
567 	}
568 
569 	return (result_erase != 0 ? result_erase : result_write);
570 }
571 #endif
572 
set_bypass(const struct shell * sh,shell_bypass_cb_t bypass)573 static int set_bypass(const struct shell *sh, shell_bypass_cb_t bypass)
574 {
575 	static bool in_use;
576 
577 	if (bypass && in_use) {
578 		shell_error(sh, "flash load supports setting bypass on a single instance.");
579 
580 		return -EBUSY;
581 	}
582 
583 	/* Mark that we have set or unset the bypass function */
584 	in_use = bypass != NULL;
585 
586 	if (in_use) {
587 		shell_print(sh, "Loading...");
588 	}
589 
590 	shell_set_bypass(sh, bypass);
591 
592 	return 0;
593 }
594 
bypass_cb(const struct shell * sh,uint8_t * recv,size_t len)595 static void bypass_cb(const struct shell *sh, uint8_t *recv, size_t len)
596 {
597 	uint32_t left_to_read = flash_load_total - flash_load_written - flash_load_boff;
598 	uint32_t to_copy = MIN(len, left_to_read);
599 	uint32_t copied = 0;
600 
601 	while (copied < to_copy) {
602 
603 		uint32_t buf_copy = MIN(to_copy, flash_load_buf_size - flash_load_boff);
604 
605 		memcpy(flash_load_buf + flash_load_boff, recv + copied, buf_copy);
606 
607 		flash_load_boff += buf_copy;
608 		copied += buf_copy;
609 
610 		/* Buffer is full. Write data to memory. */
611 		if (flash_load_boff == flash_load_buf_size) {
612 			uint32_t addr = flash_load_addr + flash_load_written;
613 			int rc = flash_write(flash_load_dev, addr, flash_load_buf,
614 					flash_load_buf_size);
615 
616 			if (rc != 0) {
617 				shell_error(sh, "Write to addr %x on dev %p ERROR!",
618 						addr, flash_load_dev);
619 			}
620 
621 			shell_print(sh, "Written chunk %d", flash_load_chunk);
622 
623 			flash_load_written += flash_load_buf_size;
624 			flash_load_chunk++;
625 			flash_load_boff = 0;
626 		}
627 	}
628 
629 	/* When data is not aligned to flash_load_buf_size there may be partial write
630 	 * at the end.
631 	 */
632 	if (flash_load_written < flash_load_total &&
633 			flash_load_written + flash_load_boff >= flash_load_total) {
634 
635 		uint32_t addr = flash_load_addr + flash_load_written;
636 		int rc = flash_write(flash_load_dev, addr, flash_load_buf, flash_load_boff);
637 
638 		if (rc != 0) {
639 			set_bypass(sh, NULL);
640 			shell_error(sh, "Write to addr %x on dev %p ERROR!",
641 					addr, flash_load_dev);
642 			return;
643 		}
644 
645 		shell_print(sh, "Written chunk %d", flash_load_chunk);
646 		flash_load_written += flash_load_boff;
647 		flash_load_chunk++;
648 	}
649 
650 	if (flash_load_written >= flash_load_total) {
651 		set_bypass(sh, NULL);
652 		shell_print(sh, "Read all");
653 	}
654 }
655 
cmd_load(const struct shell * sh,size_t argc,char * argv[])656 static int cmd_load(const struct shell *sh, size_t argc, char *argv[])
657 {
658 	const struct device *flash_dev;
659 	int result;
660 	uint32_t addr;
661 	uint32_t size;
662 	ssize_t write_block_size;
663 
664 	result = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
665 	if (result) {
666 		return result;
667 	}
668 
669 	size = strtoul(argv[2], NULL, 0);
670 
671 	write_block_size = flash_get_write_block_size(flash_dev);
672 
673 	/* Check if size is aligned */
674 	if (size % write_block_size != 0) {
675 		shell_error(sh, "Size must be %zu bytes aligned", write_block_size);
676 		return -EIO;
677 	}
678 
679 	/* Align buffer size to write_block_size */
680 	flash_load_buf_size = FLASH_LOAD_BUF_MAX;
681 
682 	if (flash_load_buf_size < write_block_size) {
683 		shell_error(sh, "Size of buffer is too small to be aligned to %zu.",
684 				write_block_size);
685 		return -ENOSPC;
686 	}
687 
688 	/* If buffer size is not aligned then change its size. */
689 	if (flash_load_buf_size % write_block_size != 0) {
690 		flash_load_buf_size -= flash_load_buf_size % write_block_size;
691 
692 		shell_warn(sh, "Load buffer was not aligned to %zu.", write_block_size);
693 		shell_warn(sh, "Effective load buffer size was set from %d to %d",
694 				FLASH_LOAD_BUF_MAX, flash_load_buf_size);
695 	}
696 
697 	/* Prepare data for callback. */
698 	flash_load_dev = flash_dev;
699 	flash_load_addr = addr;
700 	flash_load_total = size;
701 	flash_load_written = 0;
702 	flash_load_boff = 0;
703 	flash_load_chunk = 0;
704 
705 	shell_print(sh, "Send %d bytes to complete flash load command", size);
706 
707 	set_bypass(sh, bypass_cb);
708 	return 0;
709 }
710 
cmd_page_info(const struct shell * sh,size_t argc,char * argv[])711 static int cmd_page_info(const struct shell *sh, size_t argc, char *argv[])
712 {
713 	const struct device *flash_dev;
714 	struct flash_pages_info info;
715 	int result;
716 	uint32_t addr;
717 
718 	result = parse_helper(sh, &argc, &argv, &flash_dev, &addr);
719 	if (result) {
720 		return result;
721 	}
722 
723 	result = flash_get_page_info_by_offs(flash_dev, addr, &info);
724 
725 	if (result != 0) {
726 		shell_error(sh, "Could not determine page size, error code %d.", result);
727 		return -EINVAL;
728 	}
729 
730 	shell_print(sh, "Page for address 0x%x:\nstart offset: 0x%lx\nsize: %zu\nindex: %d",
731 			addr, info.start_offset, info.size, info.index);
732 	return 0;
733 }
734 
735 static void device_name_get(size_t idx, struct shell_static_entry *entry);
736 
737 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
738 
device_name_get(size_t idx,struct shell_static_entry * entry)739 static void device_name_get(size_t idx, struct shell_static_entry *entry)
740 {
741 	const struct device *dev = shell_device_lookup(idx, NULL);
742 
743 	entry->syntax = (dev != NULL) ? dev->name : NULL;
744 	entry->handler = NULL;
745 	entry->help  = NULL;
746 	entry->subcmd = &dsub_device_name;
747 }
748 
749 SHELL_STATIC_SUBCMD_SET_CREATE(flash_cmds,
750 	SHELL_CMD_ARG(copy, &dsub_device_name,
751 		"<src_device> <dst_device> <src_offset> <dst_offset> <size>",
752 		cmd_copy, 5, 5),
753 	SHELL_CMD_ARG(erase, &dsub_device_name,
754 		"[<device>] <page address> [<size>]",
755 		cmd_erase, 2, 2),
756 	SHELL_CMD_ARG(read, &dsub_device_name,
757 		"[<device>] <address> [<Dword count>]",
758 		cmd_read, 2, 2),
759 	SHELL_CMD_ARG(test, &dsub_device_name,
760 		"[<device>] <address> <size> <repeat count>",
761 		cmd_test, 4, 1),
762 	SHELL_CMD_ARG(write, &dsub_device_name,
763 		"[<device>] <address> <dword> [<dword>...]",
764 		cmd_write, 3, BUF_ARRAY_CNT),
765 	SHELL_CMD_ARG(load, &dsub_device_name,
766 		"[<device>] <address> <size>",
767 		cmd_load, 3, 1),
768 	SHELL_CMD_ARG(page_info, &dsub_device_name,
769 		"[<device>] <address>",
770 		cmd_page_info, 2, 1),
771 
772 #ifdef CONFIG_FLASH_SHELL_TEST_COMMANDS
773 	SHELL_CMD_ARG(read_test, &dsub_device_name,
774 		"[<device>] <address> <size> <repeat count>",
775 		cmd_read_test, 4, 1),
776 	SHELL_CMD_ARG(write_test, &dsub_device_name,
777 		"[<device>] <address> <size> <repeat count>",
778 		cmd_write_test, 4, 1),
779 	SHELL_CMD_ARG(erase_test, &dsub_device_name,
780 		"[<device>] <address> <size> <repeat count>",
781 		cmd_erase_test, 4, 1),
782 	SHELL_CMD_ARG(erase_write_test, &dsub_device_name,
783 		"[<device>] <address> <size> <repeat count>",
784 		cmd_erase_write_test, 4, 1),
785 #endif
786 
787 	SHELL_SUBCMD_SET_END
788 );
789 
cmd_flash(const struct shell * sh,size_t argc,char ** argv)790 static int cmd_flash(const struct shell *sh, size_t argc, char **argv)
791 {
792 	shell_error(sh, "%s:unknown parameter: %s", argv[0], argv[1]);
793 	return -EINVAL;
794 }
795 
796 SHELL_CMD_ARG_REGISTER(flash, &flash_cmds, "Flash shell commands",
797 		       cmd_flash, 2, 0);
798