Skip to content

Commit 9d62f8d

Browse files
msierszulskicfriedt
authored andcommitted
shell: add devmem load command
This commit adds a devmem load command for shell that allows users to easily load arbitrary data into the device memory. Signed-off-by: Mateusz Sierszulski <[email protected]> Signed-off-by: Tomasz Gorochowik <[email protected]>
1 parent 374aab1 commit 9d62f8d

File tree

6 files changed

+217
-5
lines changed

6 files changed

+217
-5
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2021 Antmicro <www.antmicro.com>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
cmake_minimum_required(VERSION 3.13.1)
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(devmem_load)
7+
8+
target_sources(app PRIVATE src/main.c)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Zephyr devmem load command
2+
This module add a `devmem load` command that allows data to be loaded into device memory.
3+
The `devmem load` command is supported by every transport the shell can run on.
4+
5+
After using a command in the Zephyr shell, the device reads all transferred data and writes to an address in the memory.
6+
The transfer ends when the user presses `ctrl-x + ctrl-q`.
7+
8+
## Usage
9+
Note: when using the devmem load command over UART it is recommended to use interrupts whenever possible.
10+
If this is not possible, reduce the baud rate to 9600.
11+
12+
If you use poll you should also use `prj_poll.conf` instead of `prj.conf`.
13+
## Building
14+
15+
The sample can be built for several platforms, the following commands build and run the application with a shell for the FRDM-K64F board.
16+
```bash
17+
west build -b frdm_k64f samples/subsys/shell/devmem_load
18+
west flash
19+
```
20+
21+
Building for boards without UART interrupt support:
22+
```bash
23+
west build -b quick_feather -- -DOVERLAY_CONFIG=prj_poll.conf samples/subsys/shell/devmem_load
24+
west flash
25+
```
26+
## Running
27+
After connecting to the UART console you should see the following output:
28+
```bash
29+
uart:~$
30+
```
31+
The `devmem load` command can now be used (`devmem load [option] [address]`):
32+
```bash
33+
uart:~$ devmem load 0x20020000
34+
Loading...
35+
press ctrl-x ctrl-q to escape
36+
```
37+
38+
Now, the `devmem load` is waiting for data.
39+
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):
40+
```bash
41+
xxd -p data > /dev/ttyX
42+
```
43+
(It is important to use plain-style hex dump)
44+
Once the data is transferred, use `ctrl-x + ctrl-q` to quit the loader.
45+
It will print the number of the bytes read and return to the shell:
46+
```bash
47+
Number of bytes read: 3442
48+
uart:~$
49+
```
50+
51+
## Options
52+
Currently, the `devmem load` command supports the following argument:
53+
* `-e` little endian parse e.g. `0xDEADBEFF -> 0xFFBEADDE`
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CONFIG_BOOT_BANNER=n
2+
CONFIG_SHELL=y
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CONFIG_SHELL=y
2+
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
3+
CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD=1
4+
CONFIG_BOOT_BANNER=n
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) 2021 Antmicro <www.antmicro.com>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
void main(void)
8+
{
9+
10+
};

subsys/shell/modules/devmem_service.c

Lines changed: 140 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,137 @@
11
/*
22
* Copyright (c) 2020 Intel Corporation
3+
* Copyright (c) 2021 Antmicro <www.antmicro.com>
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
67

8+
#include <stdlib.h>
79
#include <device.h>
810
#include <shell/shell.h>
9-
#include <stdlib.h>
11+
#include <sys/byteorder.h>
12+
13+
static inline bool is_ascii(uint8_t data)
14+
{
15+
return (data >= 0x30 && data <= 0x39) || (data >= 0x61 && data <= 0x66) ||
16+
(data >= 0x41 && data <= 0x46);
17+
}
18+
19+
static unsigned char *bytes;
20+
static uint32_t *data;
21+
static int sum;
22+
static int chunk_element;
23+
static char chunk[2];
24+
static bool littleendian;
25+
26+
#define CHAR_CAN 0x18
27+
#define CHAR_DC1 0x11
28+
29+
static int set_bypass(const struct shell *sh, shell_bypass_cb_t bypass)
30+
{
31+
static bool in_use;
32+
33+
if (bypass && in_use) {
34+
shell_error(sh, "devmem load supports setting bypass on a single instance.");
35+
36+
return -EBUSY;
37+
}
38+
39+
in_use = !in_use;
40+
if (in_use) {
41+
shell_print(sh, "Loading...\npress ctrl-x ctrl-q to escape");
42+
in_use = true;
43+
}
44+
45+
shell_set_bypass(sh, bypass);
46+
47+
return 0;
48+
}
49+
50+
static void bypass_cb(const struct shell *sh, uint8_t *recv, size_t len)
51+
{
52+
bool escape = false;
53+
static uint8_t tail;
54+
uint8_t byte;
55+
56+
if (tail == CHAR_CAN && recv[0] == CHAR_DC1) {
57+
escape = true;
58+
} else {
59+
for (int i = 0; i < (len - 1); i++) {
60+
if (recv[i] == CHAR_CAN && recv[i + 1] == CHAR_DC1) {
61+
escape = true;
62+
break;
63+
}
64+
}
65+
}
66+
67+
if (escape) {
68+
shell_print(sh, "Number of bytes read: %d", sum);
69+
set_bypass(sh, NULL);
70+
71+
if (!littleendian) {
72+
while (sum > 4) {
73+
*data = __bswap_32(*data);
74+
data++;
75+
sum = sum - 4;
76+
}
77+
if (sum % 4 == 0) {
78+
*data = __bswap_32(*data);
79+
} else if (sum % 4 == 2) {
80+
*data = __bswap_16(*data);
81+
} else if (sum % 4 == 3) {
82+
*data = __bswap_24(*data);
83+
}
84+
}
85+
return;
86+
}
87+
88+
tail = recv[len - 1];
89+
90+
if (is_ascii(*recv)) {
91+
chunk[chunk_element] = *recv;
92+
chunk_element++;
93+
}
94+
95+
if (chunk_element == 2) {
96+
byte = (uint8_t)strtoul(chunk, NULL, 16);
97+
*bytes = byte;
98+
bytes++;
99+
sum++;
100+
chunk_element = 0;
101+
}
102+
}
103+
104+
static int cmd_load(const struct shell *sh, size_t argc, char **argv)
105+
{
106+
littleendian = false;
107+
char *arg;
108+
109+
chunk_element = 0;
110+
sum = 0;
111+
112+
while (argc >= 2) {
113+
arg = argv[1] + (!strncmp(argv[1], "--", 2) && argv[1][2]);
114+
if (!strncmp(arg, "-e", 2)) {
115+
littleendian = true;
116+
} else if (!strcmp(arg, "--")) {
117+
argv++;
118+
argc--;
119+
break;
120+
} else if (arg[0] == '-' && arg[1]) {
121+
shell_print(sh, "Unknown option \"%s\"", arg);
122+
} else {
123+
break;
124+
}
125+
argv++;
126+
argc--;
127+
}
128+
129+
bytes = (unsigned char *)strtol(argv[1], NULL, 0);
130+
data = (uint32_t *)strtol(argv[1], NULL, 0);
131+
132+
set_bypass(sh, bypass_cb);
133+
return 0;
134+
}
10135

11136
static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width)
12137
{
@@ -36,8 +161,7 @@ static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width)
36161
return err;
37162
}
38163

39-
static int memory_write(const struct shell *sh, mem_addr_t addr,
40-
uint8_t width, uint64_t value)
164+
static int memory_write(const struct shell *sh, mem_addr_t addr, uint8_t width, uint64_t value)
41165
{
42166
int err = 0;
43167

@@ -104,5 +228,16 @@ static int cmd_devmem(const struct shell *sh, size_t argc, char **argv)
104228
return memory_write(sh, addr, width, value);
105229
}
106230

107-
SHELL_CMD_REGISTER(devmem, NULL, "Read/write physical memory\""
108-
"devmem address [width [value]]", cmd_devmem);
231+
SHELL_STATIC_SUBCMD_SET_CREATE(sub_devmem,
232+
SHELL_CMD_ARG(load, NULL,
233+
"Usage:\n"
234+
"devmem load [options] [address]\n"
235+
"Options:\n"
236+
"-e\tlittle-endian parse",
237+
cmd_load, 2, 1),
238+
SHELL_SUBCMD_SET_END);
239+
240+
SHELL_CMD_REGISTER(devmem, &sub_devmem,
241+
"Read/write physical memory\""
242+
"devmem address [width [value]]",
243+
cmd_devmem);

0 commit comments

Comments
 (0)