• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, ">3aFlqry[!qr]", 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 [-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 
36 config 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 
47 config 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 
58 config 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 
confirm(const char * fmt,...)77 printf_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 
i2c_open(int bus,int slave,int chip)89 static 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 
i2c_get_funcs(int bus)99 static 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 
i2c_read_byte(int fd,int addr,int * byte)109 static 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 
i2c_quick_write(int fd,int addr)121 static 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 
i2cdetect_dash_F(int bus)129 static 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 
i2cdetect_dash_l(struct dirtree * node)156 static 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 
i2cdetect_main(void)182 void 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 
i2cdump_main(void)237 void 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 
i2cget_main(void)266 void 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 
i2cset_main(void)285 void 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