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