xref: /aosp_15_r20/external/toybox/toys/other/i2ctools.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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  * TODO: I2C_M_TEN bit addressing (-t, larger range in probe...)
14 
15 // note: confirm() needs "y" to be in same place for all commands
16 USE_I2CDETECT(NEWTOY(i2cdetect, ">3aF#<0>63lqry[!qr][!Fl]", TOYFLAG_USR|TOYFLAG_SBIN))
17 USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_SBIN))
18 USE_I2CGET(NEWTOY(i2cget, "<2>3fy", TOYFLAG_USR|TOYFLAG_SBIN))
19 USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_SBIN))
20 USE_I2CTRANSFER(NEWTOY(i2ctransfer, "<2vfy", TOYFLAG_USR|TOYFLAG_SBIN))
21 
22 config I2CDETECT
23   bool "i2cdetect"
24   default y
25   help
26     usage: i2cdetect [-aqry] BUS [FIRST LAST]
27     usage: i2cdetect -F BUS
28     usage: i2cdetect -l
29 
30     Detect i2c devices.
31 
32     -a	All addresses (0x00-0x7f rather than 0x03-0x77 or FIRST-LAST)
33     -F	Show functionality
34     -l	List available buses
35     -q	Probe with SMBus Quick Write (default)
36     -r	Probe with SMBus Read Byte
37     -y	Skip confirmation prompts (yes to all)
38 
39 config I2CDUMP
40   bool "i2cdump"
41   default y
42   help
43     usage: i2cdump [-fy] BUS CHIP
44 
45     Dump i2c registers.
46 
47     -f	Force access to busy devices
48     -y	Skip confirmation prompts (yes to all)
49 
50 config I2CGET
51   bool "i2cget"
52   default y
53   help
54     usage: i2cget [-fy] BUS CHIP [ADDR]
55 
56     Read an i2c register.
57 
58     -f	Force access to busy devices
59     -y	Skip confirmation prompts (yes to all)
60 
61 config I2CSET
62   bool "i2cset"
63   default y
64   help
65     usage: i2cset [-fy] BUS CHIP ADDR VALUE... MODE
66 
67     Write an i2c register. MODE is b for byte, w for 16-bit word, i for I2C block.
68 
69     -f	Force access to busy devices
70     -y	Skip confirmation prompts (yes to all)
71 
72 config I2CTRANSFER
73   bool "i2ctransfer"
74   default y
75   help
76     usage: i2ctransfer [-fy] BUS DESC [DATA...]...
77 
78     Make i2c transfers. DESC is 'r' for read or 'w' for write, followed by
79     the number of bytes to read or write, followed by '@' and a 7-bit address.
80     For any message after the first, the '@' and address can be omitted to
81     reuse the previous address. A 'w' DESC must be followed by the number of
82     DATA bytes that was specified in the DESC.
83 
84     -f	Force access to busy devices
85     -v	Verbose (show messages sent, not just received)
86     -y	Skip confirmation prompts (yes to all)
87 */
88 
89 #define FOR_i2cdetect
90 #define FORCE_FLAGS
91 #include "toys.h"
92 
GLOBALS(long F;)93 GLOBALS(
94   long F;
95 )
96 
97 #include <linux/i2c.h>
98 #include <linux/i2c-dev.h>
99 
100 printf_format static void confirm(const char *fmt, ...)
101 {
102   va_list va;
103 
104   if (FLAG(y)) return;
105 
106   va_start(va, fmt);
107   vfprintf(stderr, fmt, va);
108   va_end(va);
109   if (!yesno(1)) error_exit("Exiting");
110 }
111 
i2c_open(int bus,int slave,long chip)112 static int i2c_open(int bus, int slave, long chip)
113 {
114   int fd;
115 
116   sprintf(toybuf, "/dev/i2c-%d", bus);
117   fd = xopen(toybuf, O_RDONLY);
118   if (slave) xioctl(fd, slave, (void *)chip);
119 
120   return fd;
121 }
122 
i2c_get_funcs(int bus)123 static unsigned long i2c_get_funcs(int bus)
124 {
125   int fd = i2c_open(bus, 0, 0);
126   unsigned long result;
127 
128   xioctl(fd, I2C_FUNCS, &result);
129   close(fd);
130 
131   return result;
132 }
133 
i2c_read_byte(int fd,int addr,char * byte)134 static int i2c_read_byte(int fd, int addr, char *byte)
135 {
136   union i2c_smbus_data data;
137   struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_READ,
138     .size = I2C_SMBUS_BYTE_DATA, .command = addr, .data = &data };
139 
140   memset(&data, 0, sizeof(data));
141   if (ioctl(fd, I2C_SMBUS, &ioctl_data)==-1) return -1;
142   *byte = data.byte;
143 
144   return 0;
145 }
146 
i2c_quick_write(int fd,int addr)147 static int i2c_quick_write(int fd, int addr)
148 {
149   struct i2c_smbus_ioctl_data ioctl_data = { .read_write = I2C_SMBUS_QUICK,
150     .command = addr };
151 
152   return ioctl(fd, I2C_SMBUS, &ioctl_data);
153 }
154 
i2cdetect_dash_l(struct dirtree * node)155 static int i2cdetect_dash_l(struct dirtree *node)
156 {
157   char *suffix = "/name", *fname, *p;
158   int suffix_len = strlen(suffix), bus;
159   unsigned long funcs;
160 
161   if (!node->parent) return DIRTREE_RECURSE; // Skip the directory itself.
162 
163   if (sscanf(node->name, "i2c-%d", &bus)!=1) return 0;
164   funcs = i2c_get_funcs(bus) & I2C_FUNC_I2C;
165 
166   fname = dirtree_path(node, &suffix_len);
167   strcat(fname, suffix);
168   xreadfile(fname, toybuf, sizeof(toybuf));
169   free(fname);
170   if ((p = strchr(toybuf, '\n'))) *p = 0;
171 
172   // "i2c-1	i2c	Synopsys DesignWare I2C adapter		I2C adapter"
173   printf("%s\t%-10s\t%-32s\t%s\n", node->name, funcs ? "i2c" : "?", toybuf,
174          funcs ? "I2C Adapter" : "?");
175 
176   return 0;
177 }
178 
i2cdetect_main(void)179 void i2cdetect_main(void)
180 {
181   int bus, first, last, fd, addr = 0;
182   char byte;
183 
184   if (FLAG(l)|FLAG(F)) {
185     if (toys.optc) error_exit("bad '%s'", *toys.optargs);
186     if (FLAG(l))
187       dirtree_flagread("/sys/class/i2c-dev", DIRTREE_SHUTUP, i2cdetect_dash_l);
188     else {
189       unsigned sup = i2c_get_funcs(TT.F), i;
190       char *funcs[] = {
191         "I2C", "10 bit", 0, "SMBus PEC", 0, 0, "SMBus Block Process Call",
192         "SMBus Quick Command", "SMBus Receive Byte", "SMBus Send Byte",
193         "SMBus Read Byte", "SMBus Write Byte", "SMBus Read Word",
194         "SMBus Write Word", "SMBus Process Call", "SMBus Read Block",
195         "SMBus Write Block", "I2C Read Block", "I2C Write Block" };
196 
197       printf("Functionalities implemented by %s:\n", toybuf);
198       for (i = 0; i<ARRAY_LEN(funcs); i++)
199         if (funcs[i])
200           printf("%-32s %s\n", funcs[i], (sup&(1<<i)) ? "yes" : "no");
201     }
202 
203     return;
204   }
205 
206   if (!(toys.optc&1)) help_exit("Needs 1 or 3 arguments");
207   bus = atolx_range(*toys.optargs, 0, 0x3f);
208   if (toys.optc==3) {
209     first = atolx_range(toys.optargs[1], 0, 0x7f);
210     last = atolx_range(toys.optargs[2], 0, 0x7f);
211     if (first > last) error_exit("first > last");
212   } else {
213     first = FLAG(a) ? 0 : 3;
214     last = FLAG(a) ? 0x7f : 0x77;
215   }
216 
217   confirm("Probe chips 0x%02x-0x%02x on bus %d?", first, last, bus);
218 
219   fd = i2c_open(bus, 0, 0);
220   printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
221   while (addr < 0x80) {
222     if (!(addr&0xf)) xprintf("%02x:", addr);
223     if (addr<first || addr>last) printf("   ");
224     else if (ioctl(fd, I2C_SLAVE, addr) == -1) {
225       if (errno == EBUSY) xprintf(" UU");
226       else perror_exit("ioctl(I2C_SLAVE)");
227     } else if ((FLAG(r) ? i2c_read_byte(fd, addr, &byte)
228                         : i2c_quick_write(fd, addr)) == -1) xprintf(" --");
229     else xprintf(" %02x", addr);
230     if (!(++addr&0xf)) putchar('\n');
231   }
232   close(fd);
233 }
234 
235 #define FOR_i2cdump
236 #include "generated/flags.h"
237 
i2cdump_main(void)238 void i2cdump_main(void)
239 {
240   int fd, row, addr, bus = atolx_range(toys.optargs[0], 0, 0x3f),
241       chip = atolx_range(toys.optargs[1], 0, 0x7f);
242   char byte;
243 
244   confirm("Dump chip 0x%02x on bus %d?", chip, bus);
245 
246   fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
247   printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef\n");
248   for (row = 0; row<0x100; row += 16) {
249     xprintf("%02x:", row & 0xf0);
250     for (addr = row; addr<row+16; ++addr) {
251       if (!i2c_read_byte(fd, addr, &byte)) printf(" %02x", byte);
252       else {
253         printf(" XX");
254         byte = 'X';
255       }
256       toybuf[addr-row] = isprint(byte) ? byte : byte ? '?' : '.';
257     }
258     printf("    %16.16s\n", toybuf);
259   }
260   close(fd);
261 }
262 
263 #define FOR_i2cget
264 #include "generated/flags.h"
265 
i2cget_main(void)266 void i2cget_main(void)
267 {
268   int fd, bus = atolx_range(toys.optargs[0], 0, 0x3f),
269       chip = atolx_range(toys.optargs[1], 0, 0x7f),
270       addr = (toys.optc == 3) ? atolx_range(toys.optargs[2], 0, 0xff) : -1;
271   char 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 (toys.optc == 3) {
277     if (i2c_read_byte(fd, addr, &byte)==-1) perror_exit("i2c_read_byte");
278   } else if (read(fd, &byte, 1) != 1) perror_exit("i2c_read");
279 
280   printf("0x%02x\n", byte);
281   close(fd);
282 }
283 
284 #define FOR_i2cset
285 #include "generated/flags.h"
286 
i2cset_main(void)287 void i2cset_main(void)
288 {
289   int fd, i, bus = atolx_range(toys.optargs[0], 0, 0x3f),
290       chip = atolx_range(toys.optargs[1], 0, 0x7f),
291       addr = atolx_range(toys.optargs[2], 0, 0xff);
292   char *mode = toys.optargs[toys.optc-1];
293   struct i2c_smbus_ioctl_data ioctl_data;
294   union i2c_smbus_data data;
295 
296   memset(&data, 0, sizeof(data));
297   if (strlen(mode)!=1) help_exit("mode too long");
298   if (*mode=='b' && toys.optc==5) {
299     ioctl_data.size = I2C_SMBUS_BYTE_DATA;
300     data.byte = atolx_range(toys.optargs[3], 0, 0xff);
301   } else if (*mode=='w' && toys.optc==5) {
302     ioctl_data.size = I2C_SMBUS_WORD_DATA;
303     data.word = atolx_range(toys.optargs[3], 0, 0xffff);
304   } else if (*mode=='i' && toys.optc>=5) {
305     if (toys.optc-4>I2C_SMBUS_BLOCK_MAX) error_exit("too much data");
306     ioctl_data.size = I2C_SMBUS_I2C_BLOCK_DATA;
307     data.block[0] = toys.optc-4;
308     for (i = 0; i<toys.optc-4; i++)
309       data.block[i+1] = atolx_range(toys.optargs[3+i], 0, 0xff);
310   } else help_exit("syntax error");
311 
312   confirm("Write register 0x%02x from chip 0x%02x on bus %d?", addr, chip, bus);
313 
314   // We open the device read-only and the write command works?
315   fd = i2c_open(bus, FLAG(f) ? I2C_SLAVE_FORCE : I2C_SLAVE, chip);
316   ioctl_data.read_write = I2C_SMBUS_WRITE;
317   ioctl_data.command = addr;
318   ioctl_data.data = &data;
319   xioctl(fd, I2C_SMBUS, &ioctl_data);
320   close(fd);
321 }
322 
323 #define FOR_i2ctransfer
324 #include "generated/flags.h"
325 
show_msgs(FILE * fp,struct i2c_rdwr_ioctl_data * data,int before)326 static void show_msgs(FILE *fp, struct i2c_rdwr_ioctl_data *data, int before)
327 {
328   int i;
329 
330   for (i = 0; i < data->nmsgs; i++) {
331     struct i2c_msg *msg = &data->msgs[i];
332     int j, write = !msg->flags, hexdump;
333 
334     // Even with -v we can't show read data before it's read!
335     hexdump = (before && write) || (!before && !write) || (!before && FLAG(v));
336     if (!before && !FLAG(v) && !hexdump) continue;
337 
338     if (before || FLAG(v)) {
339       fprintf(fp, "msg %d: addr 0x%02x, %s, length %u%s", i, msg->addr,
340               write ? "write" : "read", msg->len, hexdump ? ", data " : "");
341     }
342     if (hexdump) {
343       for (j = 0; j < msg->len; j++) fprintf(fp, "0x%02x ", msg->buf[j]);
344     }
345     fprintf(fp, "\n");
346   }
347 }
348 
i2ctransfer_main(void)349 void i2ctransfer_main(void)
350 {
351   int fd, bus = atolx_range(toys.optargs[0], 0, 0x3f), i = 1, j;
352   char *arg, *addr_str;
353   struct i2c_rdwr_ioctl_data ioctl_data;
354   struct i2c_msg msgs[I2C_RDWR_IOCTL_MAX_MSGS], *msg;
355 
356   ioctl_data.msgs = msgs;
357   ioctl_data.nmsgs = 0;
358 
359   while ((arg = toys.optargs[i++])) {
360     if (ioctl_data.nmsgs >= I2C_RDWR_IOCTL_MAX_MSGS) error_exit("too much!");
361 
362     msg = &msgs[ioctl_data.nmsgs];
363     if (*arg == 'r') {
364       msg->flags = I2C_M_RD;
365     } else if (*arg == 'w') {
366       msg->flags = 0;
367     } else error_exit("expected read or write: %s", arg);
368 
369     addr_str = strchr(arg, '@');
370     if (addr_str) {
371       msg->addr = atolx_range(addr_str + 1, 0, 0x7f);
372       *addr_str = '\0';
373     } else {
374       if (ioctl_data.nmsgs == 0) error_exit("missing address: %s", arg);
375       msg->addr = msgs[ioctl_data.nmsgs - 1].addr;
376     }
377 
378     // The struct field is 16 bits, but the kernel (as of 6.4) limits each
379     // message to 8KiB. Either is far larger than you're likely to see in
380     // practice.
381     msg->len = atolx_range(arg + 1, 0, 0xffff);
382     msg->buf = xzalloc(msg->len);
383     if (*arg == 'w') {
384       for (j = 0; j < msg->len; j++) {
385         arg = toys.optargs[i++];
386         if (!arg) error_exit("expected %d data bytes", msg->len);
387         msg->buf[j] = atolx_range(arg, 0, 0xff);
388       }
389     }
390 
391     ioctl_data.nmsgs++;
392   }
393 
394   fprintf(stderr, "Will send following messages on bus %d...\n", bus);
395   show_msgs(stderr, &ioctl_data, 1);
396   confirm("Send transfers on bus %d?", bus);
397 
398   fd = i2c_open(bus, 0, 0);
399   xioctl(fd, I2C_RDWR, &ioctl_data);
400   close(fd);
401 
402   show_msgs(stdout, &ioctl_data, 0);
403 
404   for (i = 0; i < ioctl_data.nmsgs; i++) free(msgs[i].buf);
405 }
406