diff --git a/samples/subsys/shell/devmem_load/CMakeLists.txt b/samples/subsys/shell/devmem_load/CMakeLists.txt new file mode 100644 index 00000000000..c5041b6404d --- /dev/null +++ b/samples/subsys/shell/devmem_load/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2021 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(devmem_load) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/subsys/shell/devmem_load/README.md b/samples/subsys/shell/devmem_load/README.md new file mode 100644 index 00000000000..65d3b443b60 --- /dev/null +++ b/samples/subsys/shell/devmem_load/README.md @@ -0,0 +1,53 @@ +# Zephyr devmem load command +This module add a `devmem load` command that allows data to be loaded into device memory. +The `devmem load` command is supported by every transport the shell can run on. + +After using a command in the Zephyr shell, the device reads all transferred data and writes to an address in the memory. +The transfer ends when the user presses `ctrl-x + ctrl-q`. + +## Usage +Note: when using the devmem load command over UART it is recommended to use interrupts whenever possible. +If this is not possible, reduce the baud rate to 9600. + +If you use poll you should also use `prj_poll.conf` instead of `prj.conf`. +## Building + +The sample can be built for several platforms, the following commands build and run the application with a shell for the FRDM-K64F board. +```bash +west build -b frdm_k64f samples/subsys/shell/devmem_load +west flash +``` + +Building for boards without UART interrupt support: +```bash +west build -b quick_feather -- -DOVERLAY_CONFIG=prj_poll.conf samples/subsys/shell/devmem_load +west flash +``` +## Running +After connecting to the UART console you should see the following output: +```bash +uart:~$ +``` +The `devmem load` command can now be used (`devmem load [option] [address]`): +```bash +uart:~$ devmem load 0x20020000 +Loading... +press ctrl-x ctrl-q to escape +``` + +Now, the `devmem load` is waiting for data. +You can either type it directly from the console or send it from the host PC (replace `ttyX` with the appropriate one for your UART console): +```bash +xxd -p data > /dev/ttyX +``` +(It is important to use plain-style hex dump) +Once the data is transferred, use `ctrl-x + ctrl-q` to quit the loader. +It will print the number of the bytes read and return to the shell: +```bash +Number of bytes read: 3442 +uart:~$ +``` + +## Options +Currently, the `devmem load` command supports the following argument: +* `-e` little endian parse e.g. `0xDEADBEFF -> 0xFFBEADDE` diff --git a/samples/subsys/shell/devmem_load/prj.conf b/samples/subsys/shell/devmem_load/prj.conf new file mode 100644 index 00000000000..7b9128344e7 --- /dev/null +++ b/samples/subsys/shell/devmem_load/prj.conf @@ -0,0 +1,2 @@ +CONFIG_BOOT_BANNER=n +CONFIG_SHELL=y diff --git a/samples/subsys/shell/devmem_load/prj_poll.conf b/samples/subsys/shell/devmem_load/prj_poll.conf new file mode 100644 index 00000000000..e36289a67f8 --- /dev/null +++ b/samples/subsys/shell/devmem_load/prj_poll.conf @@ -0,0 +1,4 @@ +CONFIG_SHELL=y +CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n +CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD=1 +CONFIG_BOOT_BANNER=n diff --git a/samples/subsys/shell/devmem_load/src/main.c b/samples/subsys/shell/devmem_load/src/main.c new file mode 100644 index 00000000000..f635275657a --- /dev/null +++ b/samples/subsys/shell/devmem_load/src/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void main(void) +{ + +}; diff --git a/subsys/shell/modules/devmem_service.c b/subsys/shell/modules/devmem_service.c index b77467f3708..3e304818ef7 100644 --- a/subsys/shell/modules/devmem_service.c +++ b/subsys/shell/modules/devmem_service.c @@ -1,12 +1,137 @@ /* * Copyright (c) 2020 Intel Corporation + * Copyright (c) 2021 Antmicro * * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include -#include +#include + +static inline bool is_ascii(uint8_t data) +{ + return (data >= 0x30 && data <= 0x39) || (data >= 0x61 && data <= 0x66) || + (data >= 0x41 && data <= 0x46); +} + +static unsigned char *bytes; +static uint32_t *data; +static int sum; +static int chunk_element; +static char chunk[2]; +static bool littleendian; + +#define CHAR_CAN 0x18 +#define CHAR_DC1 0x11 + +static int set_bypass(const struct shell *sh, shell_bypass_cb_t bypass) +{ + static bool in_use; + + if (bypass && in_use) { + shell_error(sh, "devmem load supports setting bypass on a single instance."); + + return -EBUSY; + } + + in_use = !in_use; + if (in_use) { + shell_print(sh, "Loading...\npress ctrl-x ctrl-q to escape"); + in_use = true; + } + + shell_set_bypass(sh, bypass); + + return 0; +} + +static void bypass_cb(const struct shell *sh, uint8_t *recv, size_t len) +{ + bool escape = false; + static uint8_t tail; + uint8_t byte; + + if (tail == CHAR_CAN && recv[0] == CHAR_DC1) { + escape = true; + } else { + for (int i = 0; i < (len - 1); i++) { + if (recv[i] == CHAR_CAN && recv[i + 1] == CHAR_DC1) { + escape = true; + break; + } + } + } + + if (escape) { + shell_print(sh, "Number of bytes read: %d", sum); + set_bypass(sh, NULL); + + if (!littleendian) { + while (sum > 4) { + *data = __bswap_32(*data); + data++; + sum = sum - 4; + } + if (sum % 4 == 0) { + *data = __bswap_32(*data); + } else if (sum % 4 == 2) { + *data = __bswap_16(*data); + } else if (sum % 4 == 3) { + *data = __bswap_24(*data); + } + } + return; + } + + tail = recv[len - 1]; + + if (is_ascii(*recv)) { + chunk[chunk_element] = *recv; + chunk_element++; + } + + if (chunk_element == 2) { + byte = (uint8_t)strtoul(chunk, NULL, 16); + *bytes = byte; + bytes++; + sum++; + chunk_element = 0; + } +} + +static int cmd_load(const struct shell *sh, size_t argc, char **argv) +{ + littleendian = false; + char *arg; + + chunk_element = 0; + sum = 0; + + while (argc >= 2) { + arg = argv[1] + (!strncmp(argv[1], "--", 2) && argv[1][2]); + if (!strncmp(arg, "-e", 2)) { + littleendian = true; + } else if (!strcmp(arg, "--")) { + argv++; + argc--; + break; + } else if (arg[0] == '-' && arg[1]) { + shell_print(sh, "Unknown option \"%s\"", arg); + } else { + break; + } + argv++; + argc--; + } + + bytes = (unsigned char *)strtol(argv[1], NULL, 0); + data = (uint32_t *)strtol(argv[1], NULL, 0); + + set_bypass(sh, bypass_cb); + return 0; +} static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width) { @@ -36,8 +161,7 @@ static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width) return err; } -static int memory_write(const struct shell *sh, mem_addr_t addr, - uint8_t width, uint64_t value) +static int memory_write(const struct shell *sh, mem_addr_t addr, uint8_t width, uint64_t value) { int err = 0; @@ -104,5 +228,16 @@ static int cmd_devmem(const struct shell *sh, size_t argc, char **argv) return memory_write(sh, addr, width, value); } -SHELL_CMD_REGISTER(devmem, NULL, "Read/write physical memory\"" - "devmem address [width [value]]", cmd_devmem); +SHELL_STATIC_SUBCMD_SET_CREATE(sub_devmem, + SHELL_CMD_ARG(load, NULL, + "Usage:\n" + "devmem load [options] [address]\n" + "Options:\n" + "-e\tlittle-endian parse", + cmd_load, 2, 1), + SHELL_SUBCMD_SET_END); + +SHELL_CMD_REGISTER(devmem, &sub_devmem, + "Read/write physical memory\"" + "devmem address [width [value]]", + cmd_devmem);