xref: /third_party/toybox/toys/other/i2ctools.c (revision 0f66f451)
1/* i2ctools.c - i2c tools
2 *
3 * Copyright 2018 The Android Open Source Project
4 *
5 * https://www.kernel.org/doc/Documentation/i2c/
6 *
7 * Note: -y must have the same value in each toy for `confirm`.
8 *
9 * TODO: i2cdetect -q/-r and the "auto" mode?
10 * TODO: i2cdump non-byte modes, -r FIRST-LAST?
11 * TODO: i2cget non-byte modes? default to current read address?
12 * TODO: i2cset -r? -m MASK? c/s modes, p mode modifier?
13
14USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_BIN))
15USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_BIN))
16USE_I2CGET(NEWTOY(i2cget, "<3>3fy", TOYFLAG_USR|TOYFLAG_BIN))
17USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_BIN))
18
19config I2CDETECT
20  bool "i2cdetect"
21  default y
22  help
23    usage: i2cdetect [-aqry] BUS [FIRST LAST]
24    usage: i2cdetect -F BUS
25    usage: i2cdetect -l
26
27    Detect i2c devices.
28
29    -a	All addresses (0x00-0x7f rather than 0x03-0x77 or FIRST-LAST)
30    -F	Show functionality
31    -l	List available buses
32    -q	Probe with SMBus Quick Write (default)
33    -r	Probe with SMBus Read Byte
34    -y	Answer "yes" to confirmation prompts (for script use)
35
36config I2CDUMP
37  bool "i2cdump"
38  default y
39  help
40    usage: i2cdump [-fy] BUS CHIP
41
42    Dump i2c registers.
43
44    -f	Force access to busy devices
45    -y	Answer "yes" to confirmation prompts (for script use)
46
47config I2CGET
48  bool "i2cget"
49  default y
50  help
51    usage: i2cget [-fy] BUS CHIP ADDR
52
53    Read an i2c register.
54
55    -f	Force access to busy devices
56    -y	Answer "yes" to confirmation prompts (for script use)
57
58config I2CSET
59  bool "i2cset"
60  default y
61  help
62    usage: i2cset [-fy] BUS CHIP ADDR VALUE... MODE
63
64    Write an i2c register. MODE is b for byte, w for 16-bit word, i for I2C block.
65
66    -f	Force access to busy devices
67    -y	Answer "yes" to confirmation prompts (for script use)
68*/
69
70#define FOR_i2cdetect
71#define TT this.i2ctools
72#include "toys.h"
73
74#include <linux/i2c.h>
75#include <linux/i2c-dev.h>
76
77printf_format static void confirm(const char *fmt, ...)
78{
79  va_list va;
80
81  if (FLAG(y)) return;
82
83  va_start(va, fmt);
84  vfprintf(stderr, fmt, va);
85  va_end(va);
86  if (!yesno(1)) error_exit("Exiting");
87}
88
89static int i2c_open(int bus, int slave, int chip)
90{
91  int fd;
92
93  snprintf(toybuf, sizeof(toybuf), "/dev/i2c-%d", bus);
94  fd = xopen(toybuf, O_RDONLY);
95  if (slave) xioctl(fd, slave, (void *)(long)chip);
96  return fd;
97}
98
99static unsigned long i2c_get_funcs(int bus)
100{
101  int fd = i2c_open(bus, 0, 0);
102  unsigned long result;
103
104  xioctl(fd, I2C_FUNCS, &result);
105  close(fd);
106  return result;
107}
108
109static int i2c_read_byte(int fd, int addr, int *byte)
110{
111  union i2c_smbus_data data;
112  struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_READ,
113    .size = I2C_SMBUS_BYTE_DATA, .command = addr, .data = &data };
114
115  memset(&data, 0, sizeof(data));
116  if (ioctl(fd, I2C_SMBUS, &ioctl_data)==-1) return -1;
117  *byte = data.byte;
118  return 0;
119}
120
121static int i2c_quick_write(int fd, int addr)
122{
123  struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_QUICK,
124    .size = 0, .command = addr };
125
126  return ioctl(fd, I2C_SMBUS, &ioctl_data);
127}
128
129static void i2cdetect_dash_F(int bus)
130{
131  struct { int mask; const char *name; } funcs[] = {
132    {I2C_FUNC_I2C, "I2C"},
133    {I2C_FUNC_SMBUS_QUICK, "SMBus Quick Command"},
134    {I2C_FUNC_SMBUS_WRITE_BYTE, "SMBus Send Byte"},
135    {I2C_FUNC_SMBUS_READ_BYTE, "SMBus Receive Byte"},
136    {I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "SMBus Write Byte"},
137    {I2C_FUNC_SMBUS_READ_BYTE_DATA, "SMBus Read Byte"},
138    {I2C_FUNC_SMBUS_WRITE_WORD_DATA, "SMBus Write Word"},
139    {I2C_FUNC_SMBUS_READ_WORD_DATA, "SMBus Read Word"},
140    {I2C_FUNC_SMBUS_PROC_CALL, "SMBus Process Call"},
141    {I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, "SMBus Write Block"},
142    {I2C_FUNC_SMBUS_READ_BLOCK_DATA, "SMBus Read Block"},
143    {I2C_FUNC_SMBUS_BLOCK_PROC_CALL, "SMBus Block Process Call"},
144    {I2C_FUNC_SMBUS_PEC, "SMBus PEC"},
145    {I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, "I2C Write Block"},
146    {I2C_FUNC_SMBUS_READ_I2C_BLOCK, "I2C Read Block"},
147  };
148  unsigned long sup = i2c_get_funcs(bus);
149  int i;
150
151  printf("Functionalities implemented by %s:\n", toybuf);
152  for (i = 0; i < ARRAY_LEN(funcs); ++i)
153    printf("%-32s %s\n", funcs[i].name, (sup & funcs[i].mask) ? "yes" : "no");
154}
155
156static int i2cdetect_dash_l(struct dirtree *node)
157{
158  int suffix_len = strlen("/name");
159  int bus;
160  char *fname, *p;
161  unsigned long funcs;
162
163  if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself.
164
165  if (sscanf(node->name, "i2c-%d", &bus)!=1) return 0;
166  funcs = i2c_get_funcs(bus);
167
168  fname = dirtree_path(node, &suffix_len);
169  strcat(fname, "/name");
170  xreadfile(fname, toybuf, sizeof(toybuf));
171  free(fname);
172  if ((p = strchr(toybuf, '\n'))) *p = 0;
173
174  // "i2c-1	i2c	Synopsys DesignWare I2C adapter		I2C adapter"
175  printf("%s\t%-10s\t%-32s\t%s\n", node->name,
176         (funcs & I2C_FUNC_I2C) ? "i2c" : "?", toybuf,
177         (funcs & I2C_FUNC_I2C) ? "I2C Adapter" : "?");
178
179  return 0;
180}
181
182void i2cdetect_main(void)
183{
184  if (FLAG(l)) {
185    if (toys.optc) error_exit("-l doesn't take arguments");
186    dirtree_flagread("/sys/class/i2c-dev", DIRTREE_SHUTUP, i2cdetect_dash_l);
187  } else if (FLAG(F)) {
188    if (toys.optc != 1) error_exit("-F BUS");
189    i2cdetect_dash_F(atolx_range(*toys.optargs, 0, INT_MAX));
190  } else {
191    int bus, first = 0x03, last = 0x77, fd, row, addr, byte;
192
193    if (FLAG(a)) {
194      first = 0x00;
195      last = 0x7f;
196    }
197
198    if (toys.optc!=1 && toys.optc!=3) help_exit("Needs 1 or 3 arguments");
199    bus = atolx_range(*toys.optargs, 0, INT_MAX);
200    if (toys.optc==3) {
201      first = atolx_range(toys.optargs[1], 0, 0x7f);
202      last = atolx_range(toys.optargs[2], 0, 0x7f);
203      if (first > last) error_exit("first > last");
204    }
205
206    confirm("Probe chips 0x%02x-0x%02x on bus %d?", first, last, bus);
207
208    fd = i2c_open(bus, 0, 0);
209    printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
210    for (row = 0; row <= 0x70; row += 16) {
211      xprintf("%02x:", row & 0xf0);
212      for (addr = row; addr<row+16; ++addr) {
213        if (addr<first || addr>last) printf("   ");
214        else {
215          if (ioctl(fd, I2C_SLAVE, addr) == -1) {
216            if (errno == EBUSY) {
217              xprintf(" UU");
218              continue;
219            }
220            perror_exit("ioctl(I2C_SLAVE)");
221          }
222          if ((FLAG(r) ? i2c_read_byte(fd, addr, &byte)
223                       : i2c_quick_write(fd, addr)) == -1) xprintf(" --");
224          else xprintf(" %02x", addr);
225        }
226      }
227      putchar('\n');
228    }
229    close(fd);
230  }
231}
232
233#define CLEANUP_i2cdetect
234#define FOR_i2cdump
235#include "generated/flags.h"
236
237void i2cdump_main(void)
238{
239  int bus = atolx_range(toys.optargs[0], 0, INT_MAX);
240  int chip = atolx_range(toys.optargs[1], 0, 0x7f);
241  int fd, row, addr, byte;
242
243  confirm("Dump chip 0x%02x on bus %d?", chip, bus);
244
245  fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
246  printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef\n");
247  for (row = 0; row<=0xf0; row += 16) {
248    xprintf("%02x:", row & 0xf0);
249    for (addr = row; addr<row+16; ++addr) {
250      if (!i2c_read_byte(fd, addr, &byte)) printf(" %02x", byte);
251      else {
252        printf(" XX");
253        byte = 'X';
254      }
255      toybuf[addr-row] = isprint(byte) ? byte : (byte ? '?' : '.');
256    }
257    printf("    %16.16s\n", toybuf);
258  }
259  close(fd);
260}
261
262#define CLEANUP_i2cdump
263#define FOR_i2cget
264#include "generated/flags.h"
265
266void i2cget_main(void)
267{
268  int bus = atolx_range(toys.optargs[0], 0, INT_MAX);
269  int chip = atolx_range(toys.optargs[1], 0, 0x7f);
270  int addr = atolx_range(toys.optargs[2], 0, 0xff);
271  int fd, byte;
272
273  confirm("Read register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
274
275  fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
276  if (i2c_read_byte(fd, addr, &byte)==-1) perror_exit("i2c_read_byte");
277  printf("0x%02x\n", byte);
278  close(fd);
279}
280
281#define CLEANUP_i2cget
282#define FOR_i2cset
283#include "generated/flags.h"
284
285void i2cset_main(void)
286{
287  int bus = atolx_range(toys.optargs[0], 0, INT_MAX);
288  int chip = atolx_range(toys.optargs[1], 0, 0x7f);
289  int addr = atolx_range(toys.optargs[2], 0, 0xff);
290  char *mode = toys.optargs[toys.optc-1];
291  int fd, i;
292  struct i2c_smbus_ioctl_data ioctl_data;
293  union i2c_smbus_data data;
294
295  memset(&data, 0, sizeof(data));
296  if (strlen(mode)!=1) help_exit("mode too long");
297  if (*mode=='b' && toys.optc==5) {
298    ioctl_data.size = I2C_SMBUS_BYTE_DATA;
299    data.byte = atolx_range(toys.optargs[3], 0, 0xff);
300  } else if (*mode=='w' && toys.optc==5) {
301    ioctl_data.size = I2C_SMBUS_WORD_DATA;
302    data.word = atolx_range(toys.optargs[3], 0, 0xffff);
303  } else if (*mode=='i' && toys.optc>=5) {
304    if (toys.optc-4>I2C_SMBUS_BLOCK_MAX) error_exit("too much data");
305    ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA;
306    for (i = 0; i<toys.optc-4; ++i)
307      data.block[i+1] = atolx_range(toys.optargs[3+i], 0, 0xff);
308    data.block[0] = toys.optc-4;
309  } else help_exit("syntax error");
310
311  confirm("Write register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
312
313  fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
314  ioctl_data.read_write = I2C_SMBUS_WRITE;
315  ioctl_data.command = addr;
316  ioctl_data.data = &data;
317  xioctl(fd, I2C_SMBUS, &ioctl_data);
318  close(fd);
319}
320