diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 03e4c7d27f..d2734ef425 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -435,6 +435,7 @@ PSEUDOMODULES += shell_cmd_iw PSEUDOMODULES += shell_cmd_lwip_netif PSEUDOMODULES += shell_cmd_mci PSEUDOMODULES += shell_cmd_md5sum +PSEUDOMODULES += shell_cmd_mtd PSEUDOMODULES += shell_cmd_nanocoap_vfs PSEUDOMODULES += shell_cmd_netstats_neighbor PSEUDOMODULES += shell_cmd_nice diff --git a/sys/shell/Makefile.dep b/sys/shell/Makefile.dep index 0563d0f1f7..f5fcad333d 100644 --- a/sys/shell/Makefile.dep +++ b/sys/shell/Makefile.dep @@ -212,6 +212,11 @@ endif ifneq (,$(filter shell_cmd_md5sum,$(USEMODULE))) USEMODULE += shell_cmd_vfs endif +ifneq (,$(filter shell_cmd_mtd,$(USEMODULE))) + USEMODULE += fmt + USEMODULE += mtd + USEMODULE += od +endif ifneq (,$(filter shell_cmd_nanocoap_vfs,$(USEMODULE))) USEMODULE += nanocoap_vfs USEMODULE += vfs_util diff --git a/sys/shell/cmds/mtd.c b/sys/shell/cmds/mtd.c new file mode 100644 index 0000000000..3b5abebf24 --- /dev/null +++ b/sys/shell/cmds/mtd.c @@ -0,0 +1,430 @@ +/* + * SPDX-FileCopyrightText: 2025 ML!PA Consulting GmbH + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief Command to low-level access Memory Technology Devices (MTD) + * + * @author Benjamin Valentin + * @author Fabian Hüßler + * + * @} + */ + +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "errno.h" +#include "fmt.h" +#include "macros/units.h" +#include "mtd.h" +#include "od.h" +#include "shell.h" + +static uint64_t _get_size(mtd_dev_t *dev) +{ + return (uint64_t)dev->sector_count + * dev->pages_per_sector + * dev->page_size; +} + +static int _print_read_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_read(mtd_dev_t *dev, int argc, char **argv) +{ + uint32_t addr, len; + + assert(strcmp(*argv, "read") == 0); + if (argc < 3) { + return _print_read_usage(argv[0]); + } + + addr = atoi(argv[1]); + len = atoi(argv[2]); + + void *buffer = malloc(len); + if (buffer == NULL) { + puts("out of memory"); + return -1; + } + + /* don't print random data if read fails */ + memset(buffer, 0x3F, len); + + int res = mtd_read(dev, buffer, addr, len); + + if (res) { + printf("error: %i\n", res); + } + else { + od_hex_dump_ext(buffer, len, 0, addr); + } + + free(buffer); + + return res; +} + +static int _print_read_page_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_read_page(mtd_dev_t *dev, int argc, char **argv) +{ + uint32_t page, offset, len; + + assert(strcmp(*argv, "read_page") == 0); + if (argc < 4) { + return _print_read_page_usage(argv[0]); + } + + page = atoi(argv[1]); + offset = atoi(argv[2]); + len = atoi(argv[3]); + + void *buffer = malloc(len); + if (buffer == NULL) { + puts("out of memory"); + return -1; + } + + int res = mtd_read_page(dev, buffer, page, offset, len); + + if (res) { + printf("error: %i\n", res); + } + else { + od_hex_dump_ext(buffer, len, 0, page * dev->page_size + offset); + } + + free(buffer); + + return res; +} + +static int _print_write_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_write(mtd_dev_t *dev, int argc, char **argv) +{ + uint32_t addr, len; + + assert(strcmp(*argv, "write") == 0); + if (argc < 3) { + return _print_write_usage(argv[0]); + } + + addr = atoi(argv[1]); + len = strlen(argv[2]); + + int res = mtd_write(dev, argv[2], addr, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int _print_write_page_raw_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_write_page_raw(mtd_dev_t *dev, int argc, char **argv) +{ + uint32_t page, offset, len; + + assert(strcmp(*argv, "write_page_raw") == 0); + if (argc < 4) { + return _print_write_page_raw_usage(argv[0]); + } + + page = atoi(argv[1]); + offset = atoi(argv[2]); + len = strlen(argv[3]); + + int res = mtd_write_page_raw(dev, argv[3], page, offset, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int _print_write_page_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_write_page(mtd_dev_t *dev, int argc, char **argv) +{ +#if IS_USED(MODULE_MTD_WRITE_PAGE) + uint32_t page, offset, len; + + assert(strcmp(*argv, "write_page") == 0); + if (argc < 4) { + return _print_write_page_usage(argv[0]); + } + + page = atoi(argv[1]); + offset = atoi(argv[2]); + len = strlen(argv[3]); + + int res = mtd_write_page(dev, argv[3], page, offset, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +#else + (void)dev; + (void)argc; + (void)argv; + printf("error: write_page not supported, missing module mtd_write_page\n"); + return -ENOTSUP; +#endif +} + +static int _print_erase_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_erase(mtd_dev_t *dev, int argc, char **argv) +{ + uint32_t addr; + uint32_t len; + + assert(strcmp(*argv, "erase") == 0); + if (argc < 3) { + return _print_erase_usage(argv[0]); + } + + addr = atoi(argv[1]); + len = atoi(argv[2]); + + int res = mtd_erase(dev, addr, len); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static int _print_erase_sector_usage(const char *progname) +{ + printf("usage: %s [count]\n", progname); + return -1; +} + +static int cmd_erase_sector(mtd_dev_t *dev, int argc, char **argv) +{ + uint32_t sector, count = 1; + + assert(strcmp(*argv, "erase_sector") == 0); + if (argc < 2) { + return _print_erase_sector_usage(argv[0]); + } + + sector = atoi(argv[1]); + if (argc > 2) { + count = atoi(argv[2]); + } + + int res = mtd_erase_sector(dev, sector, count); + + if (res) { + printf("error: %i\n", res); + } + + return res; +} + +static void _print_size(uint64_t size) +{ + unsigned long len; + const char *unit; + + if (size == 0) { + len = 0; + unit = "byte"; + } + else if ((size & (GiB(1) - 1)) == 0) { + len = size / GiB(1); + unit = "GiB"; + } + else if ((size & (MiB(1) - 1)) == 0) { + len = size / MiB(1); + unit = "MiB"; + } + else if ((size & (KiB(1) - 1)) == 0) { + len = size / KiB(1); + unit = "kiB"; + } + else { + len = size; + unit = "byte"; + } + + printf("%lu %s", len, unit); +} + +static void _print_info(mtd_dev_t *dev) +{ + assert(dev); + printf("sectors: %"PRIu32"\n", dev->sector_count); + printf("pages per sector: %"PRIu32"\n", dev->pages_per_sector); + printf("page size: %"PRIu32"\n", dev->page_size); + printf("total: "); + _print_size(_get_size(dev)); + puts(""); +} + +static int cmd_info(mtd_dev_t *dev, int argc, char **argv) +{ + (void)argc; + assert(strcmp(*argv, "info") == 0); + + if (dev) { + _print_info(dev); + } + else { + printf("mtd devices: %u\n", (unsigned)MTD_NUMOF); + for (unsigned i = 0; i < MTD_NUMOF; ++i) { + printf(" -=[ MTD_%d ]=-\n", i); + _print_info(mtd_dev_get(i)); + } + } + + return 0; +} + +static int _print_power_usage(const char *progname) +{ + printf("usage: %s \n", progname); + return -1; +} + +static int cmd_power(mtd_dev_t *dev, int argc, char **argv) +{ + enum mtd_power_state state; + + assert(strcmp(*argv, "power") == 0); + if (argc < 2) { + return _print_power_usage(argv[0]); + } + + if (strcmp(argv[1], "off") == 0) { + state = MTD_POWER_DOWN; + } + else if (strcmp(argv[1], "on") == 0) { + state = MTD_POWER_UP; + } + else { + return _print_power_usage(argv[0]); + } + + mtd_power(dev, state); + + return 0; +} + +static int _cmd_mtd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s [dev] [args]\n", argv[0]); + printf("commands:\n" + " read: Read a region of memory on the MTD\n" + " read_page: Read a region of memory on the MTD (pagewise addressing)\n" + " write: Write a region of memory on the MTD\n" + " write_page_raw: Write a region of memory on the MTD (pagewise addressing)\n" + " write_page: Write a region of memory on the MTD (pagewise addressing, read-modify-write)\n" + " erase: Erase a region of memory on the MTD\n" + " erase_sector: Erase a sector of memory on the MTD\n" + " info: Print properties of the MTD device\n" + " power: Turn the MTD device on/off\n" + ); + return -1; + } + + if (argc == 2) { + if (!strcmp(argv[1], "info")) { + argc -= 1; + argv += 1; + return cmd_info(NULL, argc, argv); + } + printf("unknown command: %s\n", argv[1]); + return -1; + } + + unsigned idx = atoi(argv[1]); + if (idx > MTD_NUMOF) { + printf("%s: invalid device: %s\n", argv[0], argv[1]); + return -1; + } + argc -= 2; + argv += 2; + + mtd_dev_t *dev = mtd_dev_get(idx); + if (dev == NULL) { + return -1; + } + + if (!strcmp(argv[0], "read")) { + return cmd_read(dev, argc, argv); + } + else if (!strcmp(argv[0], "read_page")) { + return cmd_read_page(dev, argc, argv); + } + else if (!strcmp(argv[0], "write")) { + return cmd_write(dev, argc, argv); + } + else if (!strcmp(argv[0], "write_page_raw")) { + return cmd_write_page_raw(dev, argc, argv); + } + else if (!strcmp(argv[0], "write_page")) { + return cmd_write_page(dev, argc, argv); + } + else if (!strcmp(argv[0], "erase")) { + return cmd_erase(dev, argc, argv); + } + else if (!strcmp(argv[0], "erase_sector")) { + return cmd_erase_sector(dev, argc, argv); + } + else if (!strcmp(argv[0], "info")) { + return cmd_info(dev, argc, argv); + } + else if (!strcmp(argv[0], "power")) { + return cmd_power(dev, argc, argv); + } + printf("unknown command: %s\n", argv[0]); + return -1; +} + +SHELL_COMMAND(mtd, "Read and write raw data to an MTD", _cmd_mtd); diff --git a/tests/drivers/mtd_raw/Makefile b/tests/drivers/mtd_raw/Makefile index b8efa79003..4a13e184e0 100644 --- a/tests/drivers/mtd_raw/Makefile +++ b/tests/drivers/mtd_raw/Makefile @@ -2,9 +2,8 @@ include ../Makefile.drivers_common USEMODULE += shell USEMODULE += shell_cmds_default +USEMODULE += shell_cmd_mtd -USEMODULE += od -USEMODULE += mtd USEMODULE += mtd_write_page # enable true erase if MTD is an SD card diff --git a/tests/drivers/mtd_raw/main.c b/tests/drivers/mtd_raw/main.c index 8a7a03873f..3f9a0c68a7 100644 --- a/tests/drivers/mtd_raw/main.c +++ b/tests/drivers/mtd_raw/main.c @@ -24,11 +24,9 @@ #include #include -#include "od.h" +#include "board.h" #include "mtd.h" #include "shell.h" -#include "board.h" -#include "macros/units.h" #include "test_utils/expect.h" static mtd_dev_t *_get_dev(int argc, char **argv) @@ -48,285 +46,6 @@ static mtd_dev_t *_get_dev(int argc, char **argv) return mtd_dev_get(idx); } -static uint64_t _get_size(mtd_dev_t *dev) -{ - return (uint64_t)dev->sector_count - * dev->pages_per_sector - * dev->page_size; -} - -static int cmd_read(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t addr, len; - - if (argc < 4 || dev == NULL) { - printf("usage: %s \n", argv[0]); - return -1; - } - - addr = atoi(argv[2]); - len = atoi(argv[3]); - - void *buffer = malloc(len); - if (buffer == NULL) { - puts("out of memory"); - return -1; - } - - /* don't print random data if read fails */ - memset(buffer, 0x3F, len); - - int res = mtd_read(dev, buffer, addr, len); - - if (res) { - printf("error: %i\n", res); - } else { - od_hex_dump_ext(buffer, len, 0, addr); - } - - free(buffer); - - return res; -} - -static int cmd_read_page(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t page, offset, len; - - if (argc < 5 || dev == NULL) { - printf("usage: %s \n", argv[0]); - return -1; - } - - page = atoi(argv[2]); - offset = atoi(argv[3]); - len = atoi(argv[4]); - - void *buffer = malloc(len); - if (buffer == NULL) { - puts("out of memory"); - return -1; - } - - int res = mtd_read_page(dev, buffer, page, offset, len); - - if (res) { - printf("error: %i\n", res); - } else { - od_hex_dump_ext(buffer, len, 0, page * dev->page_size + offset); - } - - free(buffer); - - return res; -} - -static int cmd_write(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t addr, len; - - if (argc < 4 || dev == NULL) { - printf("usage: %s \n", argv[0]); - return -1; - } - - addr = atoi(argv[2]); - len = strlen(argv[3]); - - int res = mtd_write(dev, argv[3], addr, len); - - if (res) { - printf("error: %i\n", res); - } - - return res; -} - -static int cmd_write_page_raw(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t page, offset, len; - - if (argc < 5 || dev == NULL) { - printf("usage: %s \n", argv[0]); - return -1; - } - - page = atoi(argv[2]); - offset = atoi(argv[3]); - len = strlen(argv[4]); - - int res = mtd_write_page_raw(dev, argv[4], page, offset, len); - - if (res) { - printf("error: %i\n", res); - } - - return res; -} - -static int cmd_write_page(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t page, offset, len; - - if (argc < 5 || dev == NULL) { - printf("usage: %s \n", argv[0]); - return -1; - } - - page = atoi(argv[2]); - offset = atoi(argv[3]); - len = strlen(argv[4]); - - int res = mtd_write_page(dev, argv[4], page, offset, len); - - if (res) { - printf("error: %i\n", res); - } - - return res; -} - -static int cmd_erase(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t addr; - uint32_t len; - - if (argc < 4 || dev == NULL) { - printf("usage: %s \n", argv[0]); - return -1; - } - - addr = atoi(argv[2]); - len = atoi(argv[3]); - - int res = mtd_erase(dev, addr, len); - - if (res) { - printf("error: %i\n", res); - } - - return res; -} - -static int cmd_erase_sector(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - uint32_t sector, count = 1; - - if (argc < 3 || dev == NULL) { - printf("usage: %s [count]\n", argv[0]); - return -1; - } - - sector = atoi(argv[2]); - - if (argc > 3) { - count = atoi(argv[3]); - } - - int res = mtd_erase_sector(dev, sector, count); - - if (res) { - printf("error: %i\n", res); - } - - return res; -} - -static void _print_size(uint64_t size) -{ - unsigned long len; - const char *unit; - - if (size == 0) { - len = 0; - unit = "byte"; - } else if ((size & (GiB(1) - 1)) == 0) { - len = size / GiB(1); - unit = "GiB"; - } - else if ((size & (MiB(1) - 1)) == 0) { - len = size / MiB(1); - unit = "MiB"; - } - else if ((size & (KiB(1) - 1)) == 0) { - len = size / KiB(1); - unit = "kiB"; - } else { - len = size; - unit = "byte"; - } - - printf("%lu %s", len, unit); -} - -static void _print_info(mtd_dev_t *dev) -{ - printf("sectors: %"PRIu32"\n", dev->sector_count); - printf("pages per sector: %"PRIu32"\n", dev->pages_per_sector); - printf("page size: %"PRIu32"\n", dev->page_size); - printf("total: "); - _print_size(_get_size(dev)); - puts(""); -} - -static int cmd_info(int argc, char **argv) -{ - if (argc < 2) { - printf("mtd devices: %d\n", (unsigned)MTD_NUMOF); - - for (unsigned i = 0; i < MTD_NUMOF; ++i) { - printf(" -=[ MTD_%d ]=-\n", i); - _print_info(mtd_dev_get(i)); - } - return 0; - } - - mtd_dev_t *dev = _get_dev(argc, argv); - - if (dev == NULL) { - return -1; - } - - _print_info(dev); - - return 0; -} - -static inline int _print_power_usage(const char *progname) -{ - printf("usage: %s \n", progname); - return -1; -} - -static int cmd_power(int argc, char **argv) -{ - mtd_dev_t *dev = _get_dev(argc, argv); - enum mtd_power_state state; - - if (argc < 3 || dev == NULL) { - return _print_power_usage(argv[0]); - } - - if (strcmp(argv[2], "off") == 0) { - state = MTD_POWER_DOWN; - } else if (strcmp(argv[2], "on") == 0) { - state = MTD_POWER_UP; - } else { - return _print_power_usage(argv[0]); - } - - mtd_power(dev, state); - - return 0; -} - static bool mem_is_all_set(const uint8_t *buf, uint8_t c, size_t n) { for (const uint8_t *end = buf + n; buf != end; ++buf) { @@ -424,23 +143,7 @@ static int cmd_test(int argc, char **argv) return 0; } -static const shell_command_t shell_commands[] = { - { "info", "Print properties of the MTD device", cmd_info }, - { "power", "Turn the MTD device on/off", cmd_power }, - { "read", "Read a region of memory on the MTD device", cmd_read }, - { "read_page", "Read a region of memory on the MTD device (pagewise addressing)", cmd_read_page }, - { "write", "Write a region of memory on the MTD device", cmd_write }, - { "write_page_raw", - "Write a region of memory on the MTD device (pagewise addressing)", - cmd_write_page_raw }, - { "write_page", - "Write a region of memory on the MTD device (pagewise addressing, read-modify-write)", - cmd_write_page }, - { "erase", "Erase a region of memory on the MTD device", cmd_erase }, - { "erase_sector", "Erase a sector of memory on the MTD device", cmd_erase_sector }, - { "test", "Erase & write test data to the last two sectors", cmd_test }, - { NULL, NULL, NULL } -}; +SHELL_COMMAND(test, "Erase & write test data to the last two sectors", cmd_test); int main(void) { @@ -460,15 +163,12 @@ int main(void) continue; } - printf("OK ("); - _print_size(_get_size(dev)); - puts(")"); mtd_power(dev, MTD_POWER_UP); } /* run the shell */ char line_buf[SHELL_DEFAULT_BUFSIZE]; - shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); return 0; } diff --git a/tests/drivers/mtd_raw/tests/01-run.py b/tests/drivers/mtd_raw/tests/01-run.py index ee1ce5e3ce..e01c1671e8 100755 --- a/tests/drivers/mtd_raw/tests/01-run.py +++ b/tests/drivers/mtd_raw/tests/01-run.py @@ -11,7 +11,7 @@ from testrunner import run def testfunc(child): - child.sendline("info") + child.sendline("mtd info") child.expect(r'mtd devices: (\d+)') mtd_numof = int(child.match.group(1)) for dev in range(mtd_numof):