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