1 /*
2 * Copyright (c) 2018 Prevas A/S
3 * Copyright (c) 2022 Intel Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <zephyr/sys/slist.h>
11 #include <zephyr/drivers/smbus.h>
12 #include <zephyr/shell/shell.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(smbus_shell, CONFIG_LOG_DEFAULT_LEVEL);
16
17 /**
18 * smbus_shell is a highly modified version from i2c_shell. Basically only scan
19 * logic remains from i2c_shell
20 */
21
22 /**
23 * Simplify argument parsing, smbus arguments always go in this order:
24 * smbus <shell command> <device> <peripheral address> <command byte>
25 */
26 #define ARGV_DEV 1
27 #define ARGV_ADDR 2
28 #define ARGV_CMD 3
29
30 /**
31 * This sends SMBUS messages without any data (i.e. stop condition after
32 * sending just the address). If there is an ACK for the address, it
33 * is assumed there is a device present.
34 *
35 * WARNING: As there is no standard SMBUS detection command, this code
36 * uses arbitrary SMBus commands (namely SMBus quick write to probe for
37 * devices.
38 * This operation can confuse your SMBUS bus, cause data loss, and is
39 * known to corrupt the Atmel AT24RF08 EEPROM found on many IBM
40 * Thinkpad laptops.
41 *
42 * https://manpages.debian.org/buster/i2c-tools/i2cdetect.8.en.html
43 */
44
45 /* smbus scan <device> */
cmd_smbus_scan(const struct shell * sh,size_t argc,char ** argv)46 static int cmd_smbus_scan(const struct shell *sh, size_t argc, char **argv)
47 {
48 const struct device *dev;
49 uint8_t cnt = 0, first = 0x04, last = 0x77;
50
51 dev = shell_device_get_binding(argv[ARGV_DEV]);
52 if (!dev) {
53 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
54 return -ENODEV;
55 }
56
57 shell_print(sh, " 0 1 2 3 4 5 6 7 8 9 a b c d e f");
58 for (uint8_t i = 0; i <= last; i += 16) {
59 shell_fprintf(sh, SHELL_NORMAL, "%02x: ", i);
60
61 for (uint8_t j = 0; j < 16; j++) {
62 if (i + j < first || i + j > last) {
63 shell_fprintf(sh, SHELL_NORMAL, " ");
64 continue;
65 }
66
67 if (smbus_quick(dev, i + j, SMBUS_MSG_WRITE) == 0) {
68 shell_fprintf(sh, SHELL_NORMAL, "%02x ", i + j);
69 ++cnt;
70 } else {
71 shell_fprintf(sh, SHELL_NORMAL, "-- ");
72 }
73 }
74
75 shell_print(sh, "");
76 }
77
78 shell_print(sh, "%u devices found on %s", cnt, argv[ARGV_DEV]);
79
80 return 0;
81 }
82
83 /* smbus quick <device> <dev_addr> */
cmd_smbus_quick(const struct shell * sh,size_t argc,char ** argv)84 static int cmd_smbus_quick(const struct shell *sh, size_t argc, char **argv)
85 {
86 const struct device *dev;
87 uint8_t addr;
88 int ret;
89
90 dev = shell_device_get_binding(argv[ARGV_DEV]);
91 if (!dev) {
92 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
93 return -ENODEV;
94 }
95
96 addr = strtol(argv[ARGV_ADDR], NULL, 16);
97
98 ret = smbus_quick(dev, addr, SMBUS_MSG_WRITE);
99 if (ret < 0) {
100 shell_error(sh, "SMBus: Failed quick cmd, perip: 0x%02x", addr);
101 }
102
103 return ret;
104 }
105
106 /* smbus byte_read <device> <dev_addr> */
cmd_smbus_byte_read(const struct shell * sh,size_t argc,char ** argv)107 static int cmd_smbus_byte_read(const struct shell *sh, size_t argc, char **argv)
108 {
109 const struct device *dev;
110 uint8_t addr;
111 uint8_t out;
112 int ret;
113
114 dev = shell_device_get_binding(argv[ARGV_DEV]);
115 if (!dev) {
116 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
117 return -ENODEV;
118 }
119
120 addr = strtol(argv[ARGV_ADDR], NULL, 16);
121
122 ret = smbus_byte_read(dev, addr, &out);
123 if (ret < 0) {
124 shell_error(sh, "SMBus: Failed to read from periph: 0x%02x",
125 addr);
126 return -EIO;
127 }
128
129 shell_print(sh, "Output: 0x%x", out);
130
131 return 0;
132 }
133
134 /* smbus byte_write <device> <dev_addr> <value> */
cmd_smbus_byte_write(const struct shell * sh,size_t argc,char ** argv)135 static int cmd_smbus_byte_write(const struct shell *sh,
136 size_t argc, char **argv)
137 {
138 const struct device *dev;
139 uint8_t addr;
140 uint8_t value;
141 int ret;
142
143 dev = shell_device_get_binding(argv[ARGV_DEV]);
144 if (!dev) {
145 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
146 return -ENODEV;
147 }
148
149 addr = strtol(argv[ARGV_ADDR], NULL, 16);
150 /* First byte is command */
151 value = strtol(argv[ARGV_CMD], NULL, 16);
152
153 ret = smbus_byte_write(dev, addr, value);
154 if (ret < 0) {
155 shell_error(sh, "SMBus: Failed to write to periph: 0x%02x",
156 addr);
157 return -EIO;
158 }
159
160 return 0;
161 }
162
163 /* smbus byte_data_read <device> <dev_addr> <cmd> */
cmd_smbus_byte_data_read(const struct shell * sh,size_t argc,char ** argv)164 static int cmd_smbus_byte_data_read(const struct shell *sh,
165 size_t argc, char **argv)
166 {
167 const struct device *dev;
168 uint8_t addr, command;
169 uint8_t out;
170 int ret;
171
172 dev = shell_device_get_binding(argv[ARGV_DEV]);
173 if (!dev) {
174 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
175 return -ENODEV;
176 }
177
178 addr = strtol(argv[ARGV_ADDR], NULL, 16);
179 command = strtol(argv[ARGV_CMD], NULL, 16);
180
181 ret = smbus_byte_data_read(dev, addr, command, &out);
182 if (ret < 0) {
183 shell_error(sh, "SMBus: Failed to read from periph: 0x%02x",
184 addr);
185 return -EIO;
186 }
187
188 shell_print(sh, "Output: 0x%x", out);
189
190 return 0;
191 }
192
193 /* smbus byte_data_write <device> <dev_addr> <cmd> <value> */
cmd_smbus_byte_data_write(const struct shell * sh,size_t argc,char ** argv)194 static int cmd_smbus_byte_data_write(const struct shell *sh,
195 size_t argc, char **argv)
196 {
197 const struct device *dev;
198 uint8_t addr, command;
199 uint8_t value;
200 int ret;
201
202 dev = shell_device_get_binding(argv[ARGV_DEV]);
203 if (!dev) {
204 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
205 return -ENODEV;
206 }
207
208 addr = strtol(argv[ARGV_ADDR], NULL, 16);
209 command = strtol(argv[ARGV_CMD], NULL, 16);
210 value = strtol(argv[4], NULL, 16);
211
212 ret = smbus_byte_data_write(dev, addr, command, value);
213 if (ret < 0) {
214 shell_error(sh, "SMBus: Failed to write to periph: 0x%02x",
215 addr);
216 return -EIO;
217 }
218
219 return 0;
220 }
221
222 /* smbus word_data_read <device> <dev_addr> <cmd> */
cmd_smbus_word_data_read(const struct shell * sh,size_t argc,char ** argv)223 static int cmd_smbus_word_data_read(const struct shell *sh,
224 size_t argc, char **argv)
225 {
226 const struct device *dev;
227 uint8_t addr, command;
228 uint16_t out;
229 int ret;
230
231 dev = shell_device_get_binding(argv[ARGV_DEV]);
232 if (!dev) {
233 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
234 return -ENODEV;
235 }
236
237 addr = strtol(argv[ARGV_ADDR], NULL, 16);
238 command = strtol(argv[ARGV_CMD], NULL, 16);
239
240 ret = smbus_word_data_read(dev, addr, command, &out);
241 if (ret < 0) {
242 shell_error(sh, "SMBus: Failed to read from periph: 0x%02x",
243 addr);
244 return -EIO;
245 }
246
247 shell_print(sh, "Output: 0x%04x", out);
248
249 return 0;
250 }
251
252 /* smbus word_data_write <device> <dev_addr> <cmd> <value> */
cmd_smbus_word_data_write(const struct shell * sh,size_t argc,char ** argv)253 static int cmd_smbus_word_data_write(const struct shell *sh,
254 size_t argc, char **argv)
255 {
256 const struct device *dev;
257 uint8_t addr, command;
258 uint16_t value;
259 int ret;
260
261 dev = shell_device_get_binding(argv[ARGV_DEV]);
262 if (!dev) {
263 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
264 return -ENODEV;
265 }
266
267 addr = strtol(argv[ARGV_ADDR], NULL, 16);
268 command = strtol(argv[ARGV_CMD], NULL, 16);
269 value = strtol(argv[4], NULL, 16);
270
271 ret = smbus_word_data_write(dev, addr, command, value);
272 if (ret < 0) {
273 shell_error(sh, "SMBus: Failed to write to periph: 0x%02x",
274 addr);
275 return -EIO;
276 }
277
278 return 0;
279 }
280
281 /* smbus block_write <device> <dev_addr> <cmd> <bytes ... > */
cmd_smbus_block_write(const struct shell * sh,size_t argc,char ** argv)282 static int cmd_smbus_block_write(const struct shell *sh,
283 size_t argc, char **argv)
284 {
285 const struct device *dev;
286 uint8_t addr, command;
287 uint8_t count = argc - 4;
288 char **p = &argv[4]; /* start data bytes */
289 uint8_t buf[32]; /* max block count */
290 int ret;
291
292 if (count == 0 || count > sizeof(buf)) {
293 return -EINVAL;
294 }
295
296 dev = shell_device_get_binding(argv[ARGV_DEV]);
297 if (!dev) {
298 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
299 return -ENODEV;
300 }
301
302 addr = strtol(argv[ARGV_ADDR], NULL, 16);
303 command = strtol(argv[ARGV_CMD], NULL, 16);
304
305 for (int i = 0; i < count; i++) {
306 buf[i] = (uint8_t)strtoul(p[i], NULL, 16);
307 }
308
309 LOG_HEXDUMP_DBG(buf, count, "Constructed block buffer");
310
311 ret = smbus_block_write(dev, addr, command, count, buf);
312 if (ret < 0) {
313 shell_error(sh, "Failed block write to periph: 0x%02x",
314 addr);
315 return ret;
316 }
317
318 return 0;
319 }
320
321 /* smbus block_read <device> <dev_addr> <cmd> */
cmd_smbus_block_read(const struct shell * sh,size_t argc,char ** argv)322 static int cmd_smbus_block_read(const struct shell *sh,
323 size_t argc, char **argv)
324 {
325 const struct device *dev;
326 uint8_t addr, command;
327 uint8_t buf[32]; /* max block count */
328 uint8_t count;
329 int ret;
330
331 dev = shell_device_get_binding(argv[ARGV_DEV]);
332 if (!dev) {
333 shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
334 return -ENODEV;
335 }
336
337 addr = strtol(argv[ARGV_ADDR], NULL, 16);
338 command = strtol(argv[ARGV_CMD], NULL, 16);
339
340 ret = smbus_block_read(dev, addr, command, &count, buf);
341 if (ret < 0) {
342 shell_error(sh, "Failed block read from periph: 0x%02x",
343 addr);
344 return ret;
345 }
346
347 if (count == 0 || count > sizeof(buf)) {
348 shell_error(sh, "Returned count %u", count);
349 return -ENODATA;
350 }
351
352 shell_hexdump(sh, buf, count);
353
354 return 0;
355 }
356
357 /* Device name autocompletion support */
device_name_get(size_t idx,struct shell_static_entry * entry)358 static void device_name_get(size_t idx, struct shell_static_entry *entry)
359 {
360 const struct device *dev = shell_device_lookup(idx, "smbus");
361
362 entry->syntax = (dev != NULL) ? dev->name : NULL;
363 entry->handler = NULL;
364 entry->help = NULL;
365 entry->subcmd = NULL;
366 }
367
368 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
369
370 SHELL_STATIC_SUBCMD_SET_CREATE(sub_smbus_cmds,
371 SHELL_CMD_ARG(quick, &dsub_device_name,
372 "SMBus Quick command\n"
373 "Usage: quick <device> <addr>",
374 cmd_smbus_quick, 3, 0),
375 SHELL_CMD_ARG(scan, &dsub_device_name,
376 "Scan SMBus peripheral devices command\n"
377 "Usage: scan <device>",
378 cmd_smbus_scan, 2, 0),
379 SHELL_CMD_ARG(byte_read, &dsub_device_name,
380 "SMBus: byte read command\n"
381 "Usage: byte_read <device> <addr>",
382 cmd_smbus_byte_read, 3, 0),
383 SHELL_CMD_ARG(byte_write, &dsub_device_name,
384 "SMBus: byte write command\n"
385 "Usage: byte_write <device> <addr> <value>",
386 cmd_smbus_byte_write, 4, 0),
387 SHELL_CMD_ARG(byte_data_read, &dsub_device_name,
388 "SMBus: byte data read command\n"
389 "Usage: byte_data_read <device> <addr> <cmd>",
390 cmd_smbus_byte_data_read, 4, 0),
391 SHELL_CMD_ARG(byte_data_write, &dsub_device_name,
392 "SMBus: byte data write command\n"
393 "Usage: byte_data_write <device> <addr> <cmd> <value>",
394 cmd_smbus_byte_data_write, 5, 0),
395 SHELL_CMD_ARG(word_data_read, &dsub_device_name,
396 "SMBus: word data read command\n"
397 "Usage: word_data_read <device> <addr> <cmd>",
398 cmd_smbus_word_data_read, 4, 0),
399 SHELL_CMD_ARG(word_data_write, &dsub_device_name,
400 "SMBus: word data write command\n"
401 "Usage: word_data_write <device> <addr> <cmd> <value>",
402 cmd_smbus_word_data_write, 5, 0),
403 SHELL_CMD_ARG(block_write, &dsub_device_name,
404 "SMBus: Block Write command\n"
405 "Usage: block_write <device> <addr> <cmd> [<byte1>, ...]",
406 cmd_smbus_block_write, 4, 32),
407 SHELL_CMD_ARG(block_read, &dsub_device_name,
408 "SMBus: Block Read command\n"
409 "Usage: block_read <device> <addr> <cmd>",
410 cmd_smbus_block_read, 4, 0),
411 SHELL_SUBCMD_SET_END /* Array terminated. */
412 );
413
414 SHELL_CMD_REGISTER(smbus, &sub_smbus_cmds, "smbus commands", NULL);
415