• 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, ">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