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