1 /*
2 * Copyright (c) 2022 Google Inc
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/bbram.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/shell/shell.h>
14 #include <zephyr/sys/util.h>
15
16 /* Buffer is only needed for bytes that follow command, device and address. */
17 #define BUF_ARRAY_CNT (CONFIG_SHELL_ARGC_MAX - 3)
18
parse_ul(const char * str,unsigned long * result)19 static inline int parse_ul(const char *str, unsigned long *result)
20 {
21 char *end;
22 unsigned long val;
23
24 val = strtoul(str, &end, 0);
25
26 if (*str == '\0' || *end != '\0') {
27 return -EINVAL;
28 }
29
30 *result = val;
31 return 0;
32 }
33
parse_u32(const char * str,uint32_t * result)34 static inline int parse_u32(const char *str, uint32_t *result)
35 {
36 unsigned long val;
37
38 if (parse_ul(str, &val) || val > 0xffffffff) {
39 return -EINVAL;
40 }
41 *result = (uint32_t)val;
42 return 0;
43 }
44
parse_u8(const char * str,uint8_t * result)45 static inline int parse_u8(const char *str, uint8_t *result)
46 {
47 unsigned long val;
48
49 if (parse_ul(str, &val) || val > 0xff) {
50 return -EINVAL;
51 }
52 *result = (uint8_t)val;
53 return 0;
54 }
55
parse_device(const struct shell * sh,size_t argc,char * argv[],const struct device ** bbram_dev)56 static inline int parse_device(const struct shell *sh, size_t argc, char *argv[],
57 const struct device **bbram_dev)
58 {
59 if (argc < 2) {
60 shell_error(sh, "Missing BBRAM device");
61 return -EINVAL;
62 }
63
64 *bbram_dev = device_get_binding(argv[1]);
65 if (!*bbram_dev) {
66 shell_error(sh, "Given BBRAM device was not found");
67 return -ENODEV;
68 }
69
70 return 0;
71 }
72
cmd_read(const struct shell * sh,size_t argc,char * argv[])73 static int cmd_read(const struct shell *sh, size_t argc, char *argv[])
74 {
75 const struct device *bbram_dev;
76 uint32_t addr;
77 size_t size;
78 int part_size, ret;
79
80 ret = parse_device(sh, argc, argv, &bbram_dev);
81 if (ret) {
82 return ret;
83 }
84
85 if (argc < 3) {
86 /* Dump whole BBRAM if address not provided. */
87 addr = 0;
88 ret = bbram_get_size(bbram_dev, &size);
89 if (ret < 0) {
90 shell_error(sh, "Can't get BBRAM size: %d", ret);
91 return -EIO;
92 }
93 } else {
94 /* Parse address if provided. */
95 ret = parse_u32(argv[2], &addr);
96 if (ret) {
97 return ret;
98 }
99
100 /* If size not provided read one byte. */
101 size = 1;
102
103 if (argc >= 4) {
104 /* Parse size if provided. */
105 ret = parse_u32(argv[3], &size);
106 if (ret) {
107 return ret;
108 }
109 }
110 }
111
112 for (int cnt = 0; cnt < size; cnt += part_size) {
113 uint8_t data[SHELL_HEXDUMP_BYTES_IN_LINE];
114
115 part_size = MIN(size - cnt, SHELL_HEXDUMP_BYTES_IN_LINE);
116 ret = bbram_read(bbram_dev, addr, part_size, data);
117 if (ret != 0) {
118 shell_error(sh, "BBRAM read error: %d", ret);
119 return -EIO;
120 }
121 shell_hexdump_line(sh, addr, data, part_size);
122 addr += part_size;
123 }
124
125 shell_print(sh, "");
126 return 0;
127 }
128
cmd_write(const struct shell * sh,size_t argc,char * argv[])129 static int cmd_write(const struct shell *sh, size_t argc, char *argv[])
130 {
131 const struct device *bbram_dev;
132 uint8_t buf[BUF_ARRAY_CNT];
133 uint32_t addr;
134 size_t size = 0;
135 int ret;
136
137 ret = parse_device(sh, argc, argv, &bbram_dev);
138 if (ret) {
139 return ret;
140 }
141
142 if (argc < 3) {
143 shell_error(sh, "Missing address");
144 return -EINVAL;
145 }
146
147 /* Parse address. */
148 ret = parse_u32(argv[2], &addr);
149 if (ret) {
150 return ret;
151 }
152
153 /* Parse bytes and place them in the buffer. */
154 for (int i = 3; i < argc; i++) {
155 ret = parse_u8(argv[i], &buf[i - 3]);
156 if (ret) {
157 return ret;
158 }
159 size++;
160 }
161
162 if (size == 0) {
163 shell_error(sh, "Missing data");
164 return -EINVAL;
165 }
166
167 ret = bbram_write(bbram_dev, addr, size, buf);
168 if (ret < 0) {
169 shell_error(sh, "BBRAM write error: %d", ret);
170 return -EIO;
171 }
172
173 return 0;
174 }
175
device_name_get(size_t idx,struct shell_static_entry * entry)176 static void device_name_get(size_t idx, struct shell_static_entry *entry)
177 {
178 const struct device *dev = shell_device_lookup(idx, NULL);
179
180 entry->syntax = (dev != NULL) ? dev->name : NULL;
181 entry->handler = NULL;
182 entry->help = NULL;
183 entry->subcmd = NULL;
184 }
185
186 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
187
188 SHELL_STATIC_SUBCMD_SET_CREATE(bbram_cmds,
189 SHELL_CMD_ARG(read, &dsub_device_name,
190 "<device> [<address>] [<count>]", cmd_read, 2, 2),
191 SHELL_CMD_ARG(write, &dsub_device_name,
192 "<device> <address> <byte> [<byte>...]", cmd_write, 4,
193 BUF_ARRAY_CNT),
194 SHELL_SUBCMD_SET_END);
195
cmd_bbram(const struct shell * sh,size_t argc,char ** argv)196 static int cmd_bbram(const struct shell *sh, size_t argc, char **argv)
197 {
198 shell_error(sh, "%s: unknown parameter: %s", argv[0], argv[1]);
199 return -EINVAL;
200 }
201
202 SHELL_CMD_ARG_REGISTER(bbram, &bbram_cmds, "Battery-backed RAM shell commands", cmd_bbram, 2, 0);
203