• 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  * 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