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