1 | /* vi: set sw=4 ts=4: */
|
---|
2 | /*
|
---|
3 | * Minimal i2c-tools implementation for busybox.
|
---|
4 | * Parts of code ported from i2c-tools:
|
---|
5 | * http://www.lm-sensors.org/wiki/I2CTools.
|
---|
6 | *
|
---|
7 | * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com>
|
---|
8 | *
|
---|
9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
---|
10 | */
|
---|
11 |
|
---|
12 | //config:config I2CGET
|
---|
13 | //config: bool "i2cget"
|
---|
14 | //config: default y
|
---|
15 | //config: select PLATFORM_LINUX
|
---|
16 | //config: help
|
---|
17 | //config: Read from I2C/SMBus chip registers.
|
---|
18 | //config:
|
---|
19 | //config:config I2CSET
|
---|
20 | //config: bool "i2cset"
|
---|
21 | //config: default y
|
---|
22 | //config: select PLATFORM_LINUX
|
---|
23 | //config: help
|
---|
24 | //config: Set I2C registers.
|
---|
25 | //config:
|
---|
26 | //config:config I2CDUMP
|
---|
27 | //config: bool "i2cdump"
|
---|
28 | //config: default y
|
---|
29 | //config: select PLATFORM_LINUX
|
---|
30 | //config: help
|
---|
31 | //config: Examine I2C registers.
|
---|
32 | //config:
|
---|
33 | //config:config I2CDETECT
|
---|
34 | //config: bool "i2cdetect"
|
---|
35 | //config: default y
|
---|
36 | //config: select PLATFORM_LINUX
|
---|
37 | //config: help
|
---|
38 | //config: Detect I2C chips.
|
---|
39 | //config:
|
---|
40 |
|
---|
41 | //applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP))
|
---|
42 | //applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP))
|
---|
43 | //applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP))
|
---|
44 | //applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP))
|
---|
45 |
|
---|
46 | //kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o
|
---|
47 | //kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o
|
---|
48 | //kbuild:lib-$(CONFIG_I2CDUMP) += i2c_tools.o
|
---|
49 | //kbuild:lib-$(CONFIG_I2CDETECT) += i2c_tools.o
|
---|
50 |
|
---|
51 | /*
|
---|
52 | * Unsupported stuff:
|
---|
53 | *
|
---|
54 | * - upstream i2c-tools can also look-up i2c busses by name, we only accept
|
---|
55 | * numbers,
|
---|
56 | * - bank and bankreg parameters for i2cdump are not supported because of
|
---|
57 | * their limited usefulness (see i2cdump manual entry for more info),
|
---|
58 | * - i2cdetect doesn't look for bus info in /proc as it does in upstream, but
|
---|
59 | * it shouldn't be a problem in modern kernels.
|
---|
60 | */
|
---|
61 |
|
---|
62 | #include "libbb.h"
|
---|
63 | #include "common_bufsiz.h"
|
---|
64 |
|
---|
65 | #include <linux/i2c.h>
|
---|
66 | #include <linux/i2c-dev.h>
|
---|
67 |
|
---|
68 | #define I2CDUMP_NUM_REGS 256
|
---|
69 |
|
---|
70 | #define I2CDETECT_MODE_AUTO 0
|
---|
71 | #define I2CDETECT_MODE_QUICK 1
|
---|
72 | #define I2CDETECT_MODE_READ 2
|
---|
73 |
|
---|
74 | /*
|
---|
75 | * This is needed for ioctl_or_perror_and_die() since it only accepts pointers.
|
---|
76 | */
|
---|
77 | static ALWAYS_INLINE void *itoptr(int i)
|
---|
78 | {
|
---|
79 | return (void*)(intptr_t)i;
|
---|
80 | }
|
---|
81 |
|
---|
82 | static int32_t i2c_smbus_access(int fd, char read_write, uint8_t cmd,
|
---|
83 | int size, union i2c_smbus_data *data)
|
---|
84 | {
|
---|
85 | struct i2c_smbus_ioctl_data args;
|
---|
86 |
|
---|
87 | args.read_write = read_write;
|
---|
88 | args.command = cmd;
|
---|
89 | args.size = size;
|
---|
90 | args.data = data;
|
---|
91 |
|
---|
92 | return ioctl(fd, I2C_SMBUS, &args);
|
---|
93 | }
|
---|
94 |
|
---|
95 | static int32_t i2c_smbus_read_byte(int fd)
|
---|
96 | {
|
---|
97 | union i2c_smbus_data data;
|
---|
98 | int err;
|
---|
99 |
|
---|
100 | err = i2c_smbus_access(fd, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
|
---|
101 | if (err < 0)
|
---|
102 | return err;
|
---|
103 |
|
---|
104 | return data.byte;
|
---|
105 | }
|
---|
106 |
|
---|
107 | #if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
|
---|
108 | static int32_t i2c_smbus_write_byte(int fd, uint8_t val)
|
---|
109 | {
|
---|
110 | return i2c_smbus_access(fd, I2C_SMBUS_WRITE,
|
---|
111 | val, I2C_SMBUS_BYTE, NULL);
|
---|
112 | }
|
---|
113 |
|
---|
114 | static int32_t i2c_smbus_read_byte_data(int fd, uint8_t cmd)
|
---|
115 | {
|
---|
116 | union i2c_smbus_data data;
|
---|
117 | int err;
|
---|
118 |
|
---|
119 | err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
|
---|
120 | I2C_SMBUS_BYTE_DATA, &data);
|
---|
121 | if (err < 0)
|
---|
122 | return err;
|
---|
123 |
|
---|
124 | return data.byte;
|
---|
125 | }
|
---|
126 |
|
---|
127 | static int32_t i2c_smbus_read_word_data(int fd, uint8_t cmd)
|
---|
128 | {
|
---|
129 | union i2c_smbus_data data;
|
---|
130 | int err;
|
---|
131 |
|
---|
132 | err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
|
---|
133 | I2C_SMBUS_WORD_DATA, &data);
|
---|
134 | if (err < 0)
|
---|
135 | return err;
|
---|
136 |
|
---|
137 | return data.word;
|
---|
138 | }
|
---|
139 | #endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
|
---|
140 |
|
---|
141 | #if ENABLE_I2CSET
|
---|
142 | static int32_t i2c_smbus_write_byte_data(int file,
|
---|
143 | uint8_t cmd, uint8_t value)
|
---|
144 | {
|
---|
145 | union i2c_smbus_data data;
|
---|
146 |
|
---|
147 | data.byte = value;
|
---|
148 |
|
---|
149 | return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
|
---|
150 | I2C_SMBUS_BYTE_DATA, &data);
|
---|
151 | }
|
---|
152 |
|
---|
153 | static int32_t i2c_smbus_write_word_data(int file, uint8_t cmd, uint16_t value)
|
---|
154 | {
|
---|
155 | union i2c_smbus_data data;
|
---|
156 |
|
---|
157 | data.word = value;
|
---|
158 |
|
---|
159 | return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
|
---|
160 | I2C_SMBUS_WORD_DATA, &data);
|
---|
161 | }
|
---|
162 |
|
---|
163 | static int32_t i2c_smbus_write_block_data(int file, uint8_t cmd,
|
---|
164 | uint8_t length, const uint8_t *values)
|
---|
165 | {
|
---|
166 | union i2c_smbus_data data;
|
---|
167 |
|
---|
168 | if (length > I2C_SMBUS_BLOCK_MAX)
|
---|
169 | length = I2C_SMBUS_BLOCK_MAX;
|
---|
170 |
|
---|
171 | memcpy(data.block+1, values, length);
|
---|
172 | data.block[0] = length;
|
---|
173 |
|
---|
174 | return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
|
---|
175 | I2C_SMBUS_BLOCK_DATA, &data);
|
---|
176 | }
|
---|
177 |
|
---|
178 | static int32_t i2c_smbus_write_i2c_block_data(int file, uint8_t cmd,
|
---|
179 | uint8_t length, const uint8_t *values)
|
---|
180 | {
|
---|
181 | union i2c_smbus_data data;
|
---|
182 |
|
---|
183 | if (length > I2C_SMBUS_BLOCK_MAX)
|
---|
184 | length = I2C_SMBUS_BLOCK_MAX;
|
---|
185 |
|
---|
186 | memcpy(data.block+1, values, length);
|
---|
187 | data.block[0] = length;
|
---|
188 |
|
---|
189 | return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
|
---|
190 | I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
|
---|
191 | }
|
---|
192 | #endif /* ENABLE_I2CSET */
|
---|
193 |
|
---|
194 | #if ENABLE_I2CDUMP
|
---|
195 | /*
|
---|
196 | * Returns the number of bytes read, vals must hold at
|
---|
197 | * least I2C_SMBUS_BLOCK_MAX bytes.
|
---|
198 | */
|
---|
199 | static int32_t i2c_smbus_read_block_data(int fd, uint8_t cmd, uint8_t *vals)
|
---|
200 | {
|
---|
201 | union i2c_smbus_data data;
|
---|
202 | int i, err;
|
---|
203 |
|
---|
204 | err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
|
---|
205 | I2C_SMBUS_BLOCK_DATA, &data);
|
---|
206 | if (err < 0)
|
---|
207 | return err;
|
---|
208 |
|
---|
209 | for (i = 1; i <= data.block[0]; i++)
|
---|
210 | *vals++ = data.block[i];
|
---|
211 | return data.block[0];
|
---|
212 | }
|
---|
213 |
|
---|
214 | static int32_t i2c_smbus_read_i2c_block_data(int fd, uint8_t cmd,
|
---|
215 | uint8_t len, uint8_t *vals)
|
---|
216 | {
|
---|
217 | union i2c_smbus_data data;
|
---|
218 | int i, err;
|
---|
219 |
|
---|
220 | if (len > I2C_SMBUS_BLOCK_MAX)
|
---|
221 | len = I2C_SMBUS_BLOCK_MAX;
|
---|
222 | data.block[0] = len;
|
---|
223 |
|
---|
224 | err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
|
---|
225 | len == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
|
---|
226 | I2C_SMBUS_I2C_BLOCK_DATA, &data);
|
---|
227 | if (err < 0)
|
---|
228 | return err;
|
---|
229 |
|
---|
230 | for (i = 1; i <= data.block[0]; i++)
|
---|
231 | *vals++ = data.block[i];
|
---|
232 | return data.block[0];
|
---|
233 | }
|
---|
234 | #endif /* ENABLE_I2CDUMP */
|
---|
235 |
|
---|
236 | #if ENABLE_I2CDETECT
|
---|
237 | static int32_t i2c_smbus_write_quick(int fd, uint8_t val)
|
---|
238 | {
|
---|
239 | return i2c_smbus_access(fd, val, 0, I2C_SMBUS_QUICK, NULL);
|
---|
240 | }
|
---|
241 | #endif /* ENABLE_I2CDETECT */
|
---|
242 |
|
---|
243 | static int i2c_bus_lookup(const char *bus_str)
|
---|
244 | {
|
---|
245 | return xstrtou_range(bus_str, 10, 0, 0xfffff);
|
---|
246 | }
|
---|
247 |
|
---|
248 | #if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
|
---|
249 | static int i2c_parse_bus_addr(const char *addr_str)
|
---|
250 | {
|
---|
251 | /* Slave address must be in range 0x03 - 0x77. */
|
---|
252 | return xstrtou_range(addr_str, 16, 0x03, 0x77);
|
---|
253 | }
|
---|
254 |
|
---|
255 | static void i2c_set_pec(int fd, int pec)
|
---|
256 | {
|
---|
257 | ioctl_or_perror_and_die(fd, I2C_PEC,
|
---|
258 | itoptr(pec ? 1 : 0),
|
---|
259 | "can't set PEC");
|
---|
260 | }
|
---|
261 |
|
---|
262 | static void i2c_set_slave_addr(int fd, int addr, int force)
|
---|
263 | {
|
---|
264 | ioctl_or_perror_and_die(fd, force ? I2C_SLAVE_FORCE : I2C_SLAVE,
|
---|
265 | itoptr(addr),
|
---|
266 | "can't set address to 0x%02x", addr);
|
---|
267 | }
|
---|
268 | #endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
|
---|
269 |
|
---|
270 | #if ENABLE_I2CGET || ENABLE_I2CSET
|
---|
271 | static int i2c_parse_data_addr(const char *data_addr)
|
---|
272 | {
|
---|
273 | /* Data address must be an 8 bit integer. */
|
---|
274 | return xstrtou_range(data_addr, 16, 0, 0xff);
|
---|
275 | }
|
---|
276 | #endif /* ENABLE_I2CGET || ENABLE_I2CSET */
|
---|
277 |
|
---|
278 | /*
|
---|
279 | * Opens the device file associated with given i2c bus.
|
---|
280 | *
|
---|
281 | * Upstream i2c-tools also support opening devices by i2c bus name
|
---|
282 | * but we drop it here for size reduction.
|
---|
283 | */
|
---|
284 | static int i2c_dev_open(int i2cbus)
|
---|
285 | {
|
---|
286 | char filename[sizeof("/dev/i2c-%d") + sizeof(int)*3];
|
---|
287 | int fd;
|
---|
288 |
|
---|
289 | sprintf(filename, "/dev/i2c-%d", i2cbus);
|
---|
290 | fd = open(filename, O_RDWR);
|
---|
291 | if (fd < 0) {
|
---|
292 | if (errno == ENOENT) {
|
---|
293 | filename[8] = '/'; /* change to "/dev/i2c/%d" */
|
---|
294 | fd = xopen(filename, O_RDWR);
|
---|
295 | } else {
|
---|
296 | bb_perror_msg_and_die("can't open '%s'", filename);
|
---|
297 | }
|
---|
298 | }
|
---|
299 |
|
---|
300 | return fd;
|
---|
301 | }
|
---|
302 |
|
---|
303 | /* Size reducing helpers for xxx_check_funcs(). */
|
---|
304 | static void get_funcs_matrix(int fd, unsigned long *funcs)
|
---|
305 | {
|
---|
306 | ioctl_or_perror_and_die(fd, I2C_FUNCS, funcs,
|
---|
307 | "can't get adapter functionality matrix");
|
---|
308 | }
|
---|
309 |
|
---|
310 | #if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
|
---|
311 | static void check_funcs_test_end(int funcs, int pec, const char *err)
|
---|
312 | {
|
---|
313 | if (pec && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C)))
|
---|
314 | bb_error_msg("warning: adapter does not support PEC");
|
---|
315 |
|
---|
316 | if (err)
|
---|
317 | bb_error_msg_and_die(
|
---|
318 | "adapter has no %s capability", err);
|
---|
319 | }
|
---|
320 | #endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
|
---|
321 |
|
---|
322 | /*
|
---|
323 | * The below functions emit an error message and exit if the adapter doesn't
|
---|
324 | * support desired functionalities.
|
---|
325 | */
|
---|
326 | #if ENABLE_I2CGET || ENABLE_I2CDUMP
|
---|
327 | static void check_read_funcs(int fd, int mode, int data_addr, int pec)
|
---|
328 | {
|
---|
329 | unsigned long funcs;
|
---|
330 | const char *err = NULL;
|
---|
331 |
|
---|
332 | get_funcs_matrix(fd, &funcs);
|
---|
333 | switch (mode) {
|
---|
334 | case I2C_SMBUS_BYTE:
|
---|
335 | if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
|
---|
336 | err = "SMBus receive byte";
|
---|
337 | break;
|
---|
338 | }
|
---|
339 | if (data_addr >= 0 && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
|
---|
340 | err = "SMBus send byte";
|
---|
341 | break;
|
---|
342 | case I2C_SMBUS_BYTE_DATA:
|
---|
343 | if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
|
---|
344 | err = "SMBus read byte";
|
---|
345 | break;
|
---|
346 | case I2C_SMBUS_WORD_DATA:
|
---|
347 | if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
|
---|
348 | err = "SMBus read word";
|
---|
349 | break;
|
---|
350 | #if ENABLE_I2CDUMP
|
---|
351 | case I2C_SMBUS_BLOCK_DATA:
|
---|
352 | if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
---|
353 | err = "SMBus block read";
|
---|
354 | break;
|
---|
355 |
|
---|
356 | case I2C_SMBUS_I2C_BLOCK_DATA:
|
---|
357 | if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
---|
358 | err = "I2C block read";
|
---|
359 | break;
|
---|
360 | #endif /* ENABLE_I2CDUMP */
|
---|
361 | default:
|
---|
362 | bb_error_msg_and_die("internal error");
|
---|
363 | }
|
---|
364 | check_funcs_test_end(funcs, pec, err);
|
---|
365 | }
|
---|
366 | #endif /* ENABLE_I2CGET || ENABLE_I2CDUMP */
|
---|
367 |
|
---|
368 | #if ENABLE_I2CSET
|
---|
369 | static void check_write_funcs(int fd, int mode, int pec)
|
---|
370 | {
|
---|
371 | unsigned long funcs;
|
---|
372 | const char *err = NULL;
|
---|
373 |
|
---|
374 | get_funcs_matrix(fd, &funcs);
|
---|
375 | switch (mode) {
|
---|
376 | case I2C_SMBUS_BYTE:
|
---|
377 | if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
|
---|
378 | err = "SMBus send byte";
|
---|
379 | break;
|
---|
380 |
|
---|
381 | case I2C_SMBUS_BYTE_DATA:
|
---|
382 | if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
|
---|
383 | err = "SMBus write byte";
|
---|
384 | break;
|
---|
385 |
|
---|
386 | case I2C_SMBUS_WORD_DATA:
|
---|
387 | if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
|
---|
388 | err = "SMBus write word";
|
---|
389 | break;
|
---|
390 |
|
---|
391 | case I2C_SMBUS_BLOCK_DATA:
|
---|
392 | if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
|
---|
393 | err = "SMBus block write";
|
---|
394 | break;
|
---|
395 | case I2C_SMBUS_I2C_BLOCK_DATA:
|
---|
396 | if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
|
---|
397 | err = "I2C block write";
|
---|
398 | break;
|
---|
399 | }
|
---|
400 | check_funcs_test_end(funcs, pec, err);
|
---|
401 | }
|
---|
402 | #endif /* ENABLE_I2CSET */
|
---|
403 |
|
---|
404 | static void confirm_or_abort(void)
|
---|
405 | {
|
---|
406 | fprintf(stderr, "Continue? [y/N] ");
|
---|
407 | fflush_all();
|
---|
408 | if (!bb_ask_confirmation())
|
---|
409 | bb_error_msg_and_die("aborting");
|
---|
410 | }
|
---|
411 |
|
---|
412 | /*
|
---|
413 | * Return only if user confirms the action, abort otherwise.
|
---|
414 | *
|
---|
415 | * The messages displayed here are much less elaborate than their i2c-tools
|
---|
416 | * counterparts - this is done for size reduction.
|
---|
417 | */
|
---|
418 | static void confirm_action(int bus_addr, int mode, int data_addr, int pec)
|
---|
419 | {
|
---|
420 | bb_error_msg("WARNING! This program can confuse your I2C bus");
|
---|
421 |
|
---|
422 | /* Don't let the user break his/her EEPROMs */
|
---|
423 | if (bus_addr >= 0x50 && bus_addr <= 0x57 && pec) {
|
---|
424 | bb_error_msg_and_die("this is I2C not smbus - using PEC on I2C "
|
---|
425 | "devices may result in data loss, aborting");
|
---|
426 | }
|
---|
427 |
|
---|
428 | if (mode == I2C_SMBUS_BYTE && data_addr >= 0 && pec)
|
---|
429 | bb_error_msg("WARNING! May interpret a write byte command "
|
---|
430 | "with PEC as a write byte data command");
|
---|
431 |
|
---|
432 | if (pec)
|
---|
433 | bb_error_msg("PEC checking enabled");
|
---|
434 |
|
---|
435 | confirm_or_abort();
|
---|
436 | }
|
---|
437 |
|
---|
438 | #if ENABLE_I2CGET
|
---|
439 | //usage:#define i2cget_trivial_usage
|
---|
440 | //usage: "[-f] [-y] BUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]"
|
---|
441 | //usage:#define i2cget_full_usage "\n\n"
|
---|
442 | //usage: "Read from I2C/SMBus chip registers\n"
|
---|
443 | //usage: "\n I2CBUS i2c bus number"
|
---|
444 | //usage: "\n ADDRESS 0x03 - 0x77"
|
---|
445 | //usage: "\nMODE is:"
|
---|
446 | //usage: "\n b read byte data (default)"
|
---|
447 | //usage: "\n w read word data"
|
---|
448 | //usage: "\n c write byte/read byte"
|
---|
449 | //usage: "\n Append p for SMBus PEC"
|
---|
450 | //usage: "\n"
|
---|
451 | //usage: "\n -f force access"
|
---|
452 | //usage: "\n -y disable interactive mode"
|
---|
453 | int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
454 | int i2cget_main(int argc UNUSED_PARAM, char **argv)
|
---|
455 | {
|
---|
456 | const unsigned opt_f = (1 << 0), opt_y = (1 << 1);
|
---|
457 | const char *const optstr = "fy";
|
---|
458 |
|
---|
459 | int bus_num, bus_addr, data_addr = -1, status;
|
---|
460 | int mode = I2C_SMBUS_BYTE, pec = 0, fd;
|
---|
461 | unsigned opts;
|
---|
462 |
|
---|
463 | opt_complementary = "-2:?4"; /* from 2 to 4 args */
|
---|
464 | opts = getopt32(argv, optstr);
|
---|
465 | argv += optind;
|
---|
466 |
|
---|
467 | bus_num = i2c_bus_lookup(argv[0]);
|
---|
468 | bus_addr = i2c_parse_bus_addr(argv[1]);
|
---|
469 |
|
---|
470 | if (argv[2]) {
|
---|
471 | data_addr = i2c_parse_data_addr(argv[2]);
|
---|
472 | mode = I2C_SMBUS_BYTE_DATA;
|
---|
473 | if (argv[3]) {
|
---|
474 | switch (argv[3][0]) {
|
---|
475 | case 'b': /* Already set */ break;
|
---|
476 | case 'w': mode = I2C_SMBUS_WORD_DATA; break;
|
---|
477 | case 'c': mode = I2C_SMBUS_BYTE; break;
|
---|
478 | default:
|
---|
479 | bb_error_msg("invalid mode");
|
---|
480 | bb_show_usage();
|
---|
481 | }
|
---|
482 | pec = argv[3][1] == 'p';
|
---|
483 | }
|
---|
484 | }
|
---|
485 |
|
---|
486 | fd = i2c_dev_open(bus_num);
|
---|
487 | check_read_funcs(fd, mode, data_addr, pec);
|
---|
488 | i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
|
---|
489 |
|
---|
490 | if (!(opts & opt_y))
|
---|
491 | confirm_action(bus_addr, mode, data_addr, pec);
|
---|
492 |
|
---|
493 | if (pec)
|
---|
494 | i2c_set_pec(fd, 1);
|
---|
495 |
|
---|
496 | switch (mode) {
|
---|
497 | case I2C_SMBUS_BYTE:
|
---|
498 | if (data_addr >= 0) {
|
---|
499 | status = i2c_smbus_write_byte(fd, data_addr);
|
---|
500 | if (status < 0)
|
---|
501 | bb_error_msg("warning - write failed");
|
---|
502 | }
|
---|
503 | status = i2c_smbus_read_byte(fd);
|
---|
504 | break;
|
---|
505 | case I2C_SMBUS_WORD_DATA:
|
---|
506 | status = i2c_smbus_read_word_data(fd, data_addr);
|
---|
507 | break;
|
---|
508 | default: /* I2C_SMBUS_BYTE_DATA */
|
---|
509 | status = i2c_smbus_read_byte_data(fd, data_addr);
|
---|
510 | }
|
---|
511 | close(fd);
|
---|
512 |
|
---|
513 | if (status < 0)
|
---|
514 | bb_perror_msg_and_die("read failed");
|
---|
515 |
|
---|
516 | printf("0x%0*x\n", mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
|
---|
517 |
|
---|
518 | return 0;
|
---|
519 | }
|
---|
520 | #endif /* ENABLE_I2CGET */
|
---|
521 |
|
---|
522 | #if ENABLE_I2CSET
|
---|
523 | //usage:#define i2cset_trivial_usage
|
---|
524 | //usage: "[-f] [-y] [-m MASK] BUS CHIP-ADDR DATA-ADDR [VALUE] ... [MODE]"
|
---|
525 | //usage:#define i2cset_full_usage "\n\n"
|
---|
526 | //usage: "Set I2C registers\n"
|
---|
527 | //usage: "\n I2CBUS i2c bus number"
|
---|
528 | //usage: "\n ADDRESS 0x03 - 0x77"
|
---|
529 | //usage: "\nMODE is:"
|
---|
530 | //usage: "\n c byte, no value"
|
---|
531 | //usage: "\n b byte data (default)"
|
---|
532 | //usage: "\n w word data"
|
---|
533 | //usage: "\n i I2C block data"
|
---|
534 | //usage: "\n s SMBus block data"
|
---|
535 | //usage: "\n Append p for SMBus PEC"
|
---|
536 | //usage: "\n"
|
---|
537 | //usage: "\n -f force access"
|
---|
538 | //usage: "\n -y disable interactive mode"
|
---|
539 | //usage: "\n -r read back and compare the result"
|
---|
540 | //usage: "\n -m MASK mask specifying which bits to write"
|
---|
541 | int i2cset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
542 | int i2cset_main(int argc, char **argv)
|
---|
543 | {
|
---|
544 | const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
|
---|
545 | opt_m = (1 << 2), opt_r = (1 << 3);
|
---|
546 | const char *const optstr = "fym:r";
|
---|
547 |
|
---|
548 | int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0;
|
---|
549 | int val, blen = 0, mask = 0, fd, status;
|
---|
550 | unsigned char block[I2C_SMBUS_BLOCK_MAX];
|
---|
551 | char *opt_m_arg = NULL;
|
---|
552 | unsigned opts;
|
---|
553 |
|
---|
554 | opt_complementary = "-3"; /* from 3 to ? args */
|
---|
555 | opts = getopt32(argv, optstr, &opt_m_arg);
|
---|
556 | argv += optind;
|
---|
557 | argc -= optind;
|
---|
558 |
|
---|
559 | bus_num = i2c_bus_lookup(argv[0]);
|
---|
560 | bus_addr = i2c_parse_bus_addr(argv[1]);
|
---|
561 | data_addr = i2c_parse_data_addr(argv[2]);
|
---|
562 |
|
---|
563 | if (argv[3]) {
|
---|
564 | if (!argv[4] && argv[3][0] != 'c') {
|
---|
565 | mode = I2C_SMBUS_BYTE_DATA; /* Implicit b */
|
---|
566 | } else {
|
---|
567 | switch (argv[argc-1][0]) {
|
---|
568 | case 'c': /* Already set */ break;
|
---|
569 | case 'b': mode = I2C_SMBUS_BYTE_DATA; break;
|
---|
570 | case 'w': mode = I2C_SMBUS_WORD_DATA; break;
|
---|
571 | case 's': mode = I2C_SMBUS_BLOCK_DATA; break;
|
---|
572 | case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break;
|
---|
573 | default:
|
---|
574 | bb_error_msg("invalid mode");
|
---|
575 | bb_show_usage();
|
---|
576 | }
|
---|
577 |
|
---|
578 | pec = argv[argc-1][1] == 'p';
|
---|
579 | if (mode == I2C_SMBUS_BLOCK_DATA ||
|
---|
580 | mode == I2C_SMBUS_I2C_BLOCK_DATA) {
|
---|
581 | if (pec && mode == I2C_SMBUS_I2C_BLOCK_DATA)
|
---|
582 | bb_error_msg_and_die(
|
---|
583 | "PEC not supported for I2C "
|
---|
584 | "block writes");
|
---|
585 | if (opts & opt_m)
|
---|
586 | bb_error_msg_and_die(
|
---|
587 | "mask not supported for block "
|
---|
588 | "writes");
|
---|
589 | }
|
---|
590 | }
|
---|
591 | }
|
---|
592 |
|
---|
593 | /* Prepare the value(s) to be written according to current mode. */
|
---|
594 | switch (mode) {
|
---|
595 | case I2C_SMBUS_BYTE_DATA:
|
---|
596 | val = xstrtou_range(argv[3], 0, 0, 0xff);
|
---|
597 | break;
|
---|
598 | case I2C_SMBUS_WORD_DATA:
|
---|
599 | val = xstrtou_range(argv[3], 0, 0, 0xffff);
|
---|
600 | break;
|
---|
601 | case I2C_SMBUS_BLOCK_DATA:
|
---|
602 | case I2C_SMBUS_I2C_BLOCK_DATA:
|
---|
603 | for (blen = 3; blen < (argc - 1); blen++)
|
---|
604 | block[blen] = xstrtou_range(argv[blen], 0, 0, 0xff);
|
---|
605 | val = -1;
|
---|
606 | break;
|
---|
607 | default:
|
---|
608 | val = -1;
|
---|
609 | break;
|
---|
610 | }
|
---|
611 |
|
---|
612 | if (opts & opt_m) {
|
---|
613 | mask = xstrtou_range(opt_m_arg, 0, 0,
|
---|
614 | (mode == I2C_SMBUS_BYTE ||
|
---|
615 | mode == I2C_SMBUS_BYTE_DATA) ? 0xff : 0xffff);
|
---|
616 | }
|
---|
617 |
|
---|
618 | fd = i2c_dev_open(bus_num);
|
---|
619 | check_write_funcs(fd, mode, pec);
|
---|
620 | i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
|
---|
621 |
|
---|
622 | if (!(opts & opt_y))
|
---|
623 | confirm_action(bus_addr, mode, data_addr, pec);
|
---|
624 |
|
---|
625 | /*
|
---|
626 | * If we're using mask - read the current value here and adjust the
|
---|
627 | * value to be written.
|
---|
628 | */
|
---|
629 | if (opts & opt_m) {
|
---|
630 | int tmpval;
|
---|
631 |
|
---|
632 | switch (mode) {
|
---|
633 | case I2C_SMBUS_BYTE:
|
---|
634 | tmpval = i2c_smbus_read_byte(fd);
|
---|
635 | break;
|
---|
636 | case I2C_SMBUS_WORD_DATA:
|
---|
637 | tmpval = i2c_smbus_read_word_data(fd, data_addr);
|
---|
638 | break;
|
---|
639 | default:
|
---|
640 | tmpval = i2c_smbus_read_byte_data(fd, data_addr);
|
---|
641 | }
|
---|
642 |
|
---|
643 | if (tmpval < 0)
|
---|
644 | bb_perror_msg_and_die("can't read old value");
|
---|
645 |
|
---|
646 | val = (val & mask) | (tmpval & ~mask);
|
---|
647 |
|
---|
648 | if (!(opts & opt_y)) {
|
---|
649 | bb_error_msg("old value 0x%0*x, write mask "
|
---|
650 | "0x%0*x, will write 0x%0*x to register "
|
---|
651 | "0x%02x",
|
---|
652 | mode == I2C_SMBUS_WORD_DATA ? 4 : 2, tmpval,
|
---|
653 | mode == I2C_SMBUS_WORD_DATA ? 4 : 2, mask,
|
---|
654 | mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
|
---|
655 | data_addr);
|
---|
656 | confirm_or_abort();
|
---|
657 | }
|
---|
658 | }
|
---|
659 |
|
---|
660 | if (pec)
|
---|
661 | i2c_set_pec(fd, 1);
|
---|
662 |
|
---|
663 | switch (mode) {
|
---|
664 | case I2C_SMBUS_BYTE:
|
---|
665 | status = i2c_smbus_write_byte(fd, data_addr);
|
---|
666 | break;
|
---|
667 | case I2C_SMBUS_WORD_DATA:
|
---|
668 | status = i2c_smbus_write_word_data(fd, data_addr, val);
|
---|
669 | break;
|
---|
670 | case I2C_SMBUS_BLOCK_DATA:
|
---|
671 | status = i2c_smbus_write_block_data(fd, data_addr,
|
---|
672 | blen, block);
|
---|
673 | break;
|
---|
674 | case I2C_SMBUS_I2C_BLOCK_DATA:
|
---|
675 | status = i2c_smbus_write_i2c_block_data(fd, data_addr,
|
---|
676 | blen, block);
|
---|
677 | break;
|
---|
678 | default: /* I2C_SMBUS_BYTE_DATA */
|
---|
679 | status = i2c_smbus_write_byte_data(fd, data_addr, val);
|
---|
680 | break;
|
---|
681 | }
|
---|
682 | if (status < 0)
|
---|
683 | bb_perror_msg_and_die("write failed");
|
---|
684 |
|
---|
685 | if (pec)
|
---|
686 | i2c_set_pec(fd, 0); /* Clear PEC. */
|
---|
687 |
|
---|
688 | /* No readback required - we're done. */
|
---|
689 | if (!(opts & opt_r))
|
---|
690 | return 0;
|
---|
691 |
|
---|
692 | switch (mode) {
|
---|
693 | case I2C_SMBUS_BYTE:
|
---|
694 | status = i2c_smbus_read_byte(fd);
|
---|
695 | val = data_addr;
|
---|
696 | break;
|
---|
697 | case I2C_SMBUS_WORD_DATA:
|
---|
698 | status = i2c_smbus_read_word_data(fd, data_addr);
|
---|
699 | break;
|
---|
700 | default: /* I2C_SMBUS_BYTE_DATA */
|
---|
701 | status = i2c_smbus_read_byte_data(fd, data_addr);
|
---|
702 | }
|
---|
703 |
|
---|
704 | if (status < 0) {
|
---|
705 | puts("Warning - readback failed");
|
---|
706 | } else
|
---|
707 | if (status != val) {
|
---|
708 | printf("Warning - data mismatch - wrote "
|
---|
709 | "0x%0*x, read back 0x%0*x\n",
|
---|
710 | mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
|
---|
711 | mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
|
---|
712 | } else {
|
---|
713 | printf("Value 0x%0*x written, readback matched\n",
|
---|
714 | mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val);
|
---|
715 | }
|
---|
716 |
|
---|
717 | return 0;
|
---|
718 | }
|
---|
719 | #endif /* ENABLE_I2CSET */
|
---|
720 |
|
---|
721 | #if ENABLE_I2CDUMP
|
---|
722 | static int read_block_data(int buf_fd, int mode, int *block)
|
---|
723 | {
|
---|
724 | uint8_t cblock[I2C_SMBUS_BLOCK_MAX + I2CDUMP_NUM_REGS];
|
---|
725 | int res, blen = 0, tmp, i;
|
---|
726 |
|
---|
727 | if (mode == I2C_SMBUS_BLOCK_DATA) {
|
---|
728 | blen = i2c_smbus_read_block_data(buf_fd, 0, cblock);
|
---|
729 | if (blen <= 0)
|
---|
730 | goto fail;
|
---|
731 | } else {
|
---|
732 | for (res = 0; res < I2CDUMP_NUM_REGS; res += tmp) {
|
---|
733 | tmp = i2c_smbus_read_i2c_block_data(
|
---|
734 | buf_fd, res, I2C_SMBUS_BLOCK_MAX,
|
---|
735 | cblock + res);
|
---|
736 | if (tmp <= 0) {
|
---|
737 | blen = tmp;
|
---|
738 | goto fail;
|
---|
739 | }
|
---|
740 | }
|
---|
741 |
|
---|
742 | if (res >= I2CDUMP_NUM_REGS)
|
---|
743 | res = I2CDUMP_NUM_REGS;
|
---|
744 |
|
---|
745 | for (i = 0; i < res; i++)
|
---|
746 | block[i] = cblock[i];
|
---|
747 |
|
---|
748 | if (mode != I2C_SMBUS_BLOCK_DATA)
|
---|
749 | for (i = res; i < I2CDUMP_NUM_REGS; i++)
|
---|
750 | block[i] = -1;
|
---|
751 | }
|
---|
752 |
|
---|
753 | return blen;
|
---|
754 |
|
---|
755 | fail:
|
---|
756 | bb_error_msg_and_die("block read failed: %d", blen);
|
---|
757 | }
|
---|
758 |
|
---|
759 | /* Dump all but word data. */
|
---|
760 | static void dump_data(int bus_fd, int mode, unsigned first,
|
---|
761 | unsigned last, int *block, int blen)
|
---|
762 | {
|
---|
763 | int i, j, res;
|
---|
764 |
|
---|
765 | puts(" 0 1 2 3 4 5 6 7 8 9 a b c d e f"
|
---|
766 | " 0123456789abcdef");
|
---|
767 |
|
---|
768 | for (i = 0; i < I2CDUMP_NUM_REGS; i += 0x10) {
|
---|
769 | if (mode == I2C_SMBUS_BLOCK_DATA && i >= blen)
|
---|
770 | break;
|
---|
771 | if (i/16 < first/16)
|
---|
772 | continue;
|
---|
773 | if (i/16 > last/16)
|
---|
774 | break;
|
---|
775 |
|
---|
776 | printf("%02x: ", i);
|
---|
777 | for (j = 0; j < 16; j++) {
|
---|
778 | fflush_all();
|
---|
779 | /* Skip unwanted registers */
|
---|
780 | if (i+j < first || i+j > last) {
|
---|
781 | printf(" ");
|
---|
782 | if (mode == I2C_SMBUS_WORD_DATA) {
|
---|
783 | printf(" ");
|
---|
784 | j++;
|
---|
785 | }
|
---|
786 | continue;
|
---|
787 | }
|
---|
788 |
|
---|
789 | switch (mode) {
|
---|
790 | case I2C_SMBUS_BYTE_DATA:
|
---|
791 | res = i2c_smbus_read_byte_data(bus_fd, i+j);
|
---|
792 | block[i+j] = res;
|
---|
793 | break;
|
---|
794 | case I2C_SMBUS_WORD_DATA:
|
---|
795 | res = i2c_smbus_read_word_data(bus_fd, i+j);
|
---|
796 | if (res < 0) {
|
---|
797 | block[i+j] = res;
|
---|
798 | block[i+j+1] = res;
|
---|
799 | } else {
|
---|
800 | block[i+j] = res & 0xff;
|
---|
801 | block[i+j+1] = res >> 8;
|
---|
802 | }
|
---|
803 | break;
|
---|
804 | case I2C_SMBUS_BYTE:
|
---|
805 | res = i2c_smbus_read_byte(bus_fd);
|
---|
806 | block[i+j] = res;
|
---|
807 | break;
|
---|
808 | default:
|
---|
809 | res = block[i+j];
|
---|
810 | }
|
---|
811 |
|
---|
812 | if (mode == I2C_SMBUS_BLOCK_DATA &&
|
---|
813 | i+j >= blen) {
|
---|
814 | printf(" ");
|
---|
815 | } else if (res < 0) {
|
---|
816 | printf("XX ");
|
---|
817 | if (mode == I2C_SMBUS_WORD_DATA)
|
---|
818 | printf("XX ");
|
---|
819 | } else {
|
---|
820 | printf("%02x ", block[i+j]);
|
---|
821 | if (mode == I2C_SMBUS_WORD_DATA)
|
---|
822 | printf("%02x ", block[i+j+1]);
|
---|
823 | }
|
---|
824 |
|
---|
825 | if (mode == I2C_SMBUS_WORD_DATA)
|
---|
826 | j++;
|
---|
827 | }
|
---|
828 | printf(" ");
|
---|
829 |
|
---|
830 | for (j = 0; j < 16; j++) {
|
---|
831 | if (mode == I2C_SMBUS_BLOCK_DATA && i+j >= blen)
|
---|
832 | break;
|
---|
833 | /* Skip unwanted registers */
|
---|
834 | if (i+j < first || i+j > last) {
|
---|
835 | bb_putchar(' ');
|
---|
836 | continue;
|
---|
837 | }
|
---|
838 |
|
---|
839 | res = block[i+j];
|
---|
840 | if (res < 0) {
|
---|
841 | bb_putchar('X');
|
---|
842 | } else if (res == 0x00 || res == 0xff) {
|
---|
843 | bb_putchar('.');
|
---|
844 | } else if (res < 32 || res >= 127) {
|
---|
845 | bb_putchar('?');
|
---|
846 | } else {
|
---|
847 | bb_putchar(res);
|
---|
848 | }
|
---|
849 | }
|
---|
850 | bb_putchar('\n');
|
---|
851 | }
|
---|
852 | }
|
---|
853 |
|
---|
854 | static void dump_word_data(int bus_fd, unsigned first, unsigned last)
|
---|
855 | {
|
---|
856 | int i, j, rv;
|
---|
857 |
|
---|
858 | /* Word data. */
|
---|
859 | puts(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f");
|
---|
860 | for (i = 0; i < 256; i += 8) {
|
---|
861 | if (i/8 < first/8)
|
---|
862 | continue;
|
---|
863 | if (i/8 > last/8)
|
---|
864 | break;
|
---|
865 |
|
---|
866 | printf("%02x: ", i);
|
---|
867 | for (j = 0; j < 8; j++) {
|
---|
868 | /* Skip unwanted registers. */
|
---|
869 | if (i+j < first || i+j > last) {
|
---|
870 | printf(" ");
|
---|
871 | continue;
|
---|
872 | }
|
---|
873 |
|
---|
874 | rv = i2c_smbus_read_word_data(bus_fd, i+j);
|
---|
875 | if (rv < 0)
|
---|
876 | printf("XXXX ");
|
---|
877 | else
|
---|
878 | printf("%04x ", rv & 0xffff);
|
---|
879 | }
|
---|
880 | bb_putchar('\n');
|
---|
881 | }
|
---|
882 | }
|
---|
883 |
|
---|
884 | //usage:#define i2cdump_trivial_usage
|
---|
885 | //usage: "[-f] [-r FIRST-LAST] [-y] BUS ADDR [MODE]"
|
---|
886 | //usage:#define i2cdump_full_usage "\n\n"
|
---|
887 | //usage: "Examine I2C registers\n"
|
---|
888 | //usage: "\n I2CBUS i2c bus number"
|
---|
889 | //usage: "\n ADDRESS 0x03 - 0x77"
|
---|
890 | //usage: "\nMODE is:"
|
---|
891 | //usage: "\n b byte (default)"
|
---|
892 | //usage: "\n w word"
|
---|
893 | //usage: "\n W word on even register addresses"
|
---|
894 | //usage: "\n i I2C block"
|
---|
895 | //usage: "\n s SMBus block"
|
---|
896 | //usage: "\n c consecutive byte"
|
---|
897 | //usage: "\n Append p for SMBus PEC"
|
---|
898 | //usage: "\n"
|
---|
899 | //usage: "\n -f force access"
|
---|
900 | //usage: "\n -y disable interactive mode"
|
---|
901 | //usage: "\n -r limit the number of registers being accessed"
|
---|
902 | int i2cdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
903 | int i2cdump_main(int argc UNUSED_PARAM, char **argv)
|
---|
904 | {
|
---|
905 | const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
|
---|
906 | opt_r = (1 << 2);
|
---|
907 | const char *const optstr = "fyr:";
|
---|
908 |
|
---|
909 | int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0;
|
---|
910 | unsigned first = 0x00, last = 0xff, opts;
|
---|
911 | int *block = (int *)bb_common_bufsiz1;
|
---|
912 | char *opt_r_str, *dash;
|
---|
913 | int fd, res;
|
---|
914 |
|
---|
915 | opt_complementary = "-2:?3"; /* from 2 to 3 args */
|
---|
916 | opts = getopt32(argv, optstr, &opt_r_str);
|
---|
917 | argv += optind;
|
---|
918 |
|
---|
919 | bus_num = i2c_bus_lookup(argv[0]);
|
---|
920 | bus_addr = i2c_parse_bus_addr(argv[1]);
|
---|
921 |
|
---|
922 | if (argv[2]) {
|
---|
923 | switch (argv[2][0]) {
|
---|
924 | case 'b': /* Already set. */ break;
|
---|
925 | case 'c': mode = I2C_SMBUS_BYTE; break;
|
---|
926 | case 'w': mode = I2C_SMBUS_WORD_DATA; break;
|
---|
927 | case 'W':
|
---|
928 | mode = I2C_SMBUS_WORD_DATA;
|
---|
929 | even = 1;
|
---|
930 | break;
|
---|
931 | case 's': mode = I2C_SMBUS_BLOCK_DATA; break;
|
---|
932 | case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break;
|
---|
933 | default:
|
---|
934 | bb_error_msg_and_die("invalid mode");
|
---|
935 | }
|
---|
936 |
|
---|
937 | if (argv[2][1] == 'p') {
|
---|
938 | if (argv[2][0] == 'W' || argv[2][0] == 'i') {
|
---|
939 | bb_error_msg_and_die(
|
---|
940 | "pec not supported for -W and -i");
|
---|
941 | } else {
|
---|
942 | pec = 1;
|
---|
943 | }
|
---|
944 | }
|
---|
945 | }
|
---|
946 |
|
---|
947 | if (opts & opt_r) {
|
---|
948 | first = strtol(opt_r_str, &dash, 0);
|
---|
949 | if (dash == opt_r_str || *dash != '-' || first > 0xff)
|
---|
950 | bb_error_msg_and_die("invalid range");
|
---|
951 | last = xstrtou_range(++dash, 0, first, 0xff);
|
---|
952 |
|
---|
953 | /* Range is not available for every mode. */
|
---|
954 | switch (mode) {
|
---|
955 | case I2C_SMBUS_BYTE:
|
---|
956 | case I2C_SMBUS_BYTE_DATA:
|
---|
957 | break;
|
---|
958 | case I2C_SMBUS_WORD_DATA:
|
---|
959 | if (!even || (!(first % 2) && last % 2))
|
---|
960 | break;
|
---|
961 | /* Fall through */
|
---|
962 | default:
|
---|
963 | bb_error_msg_and_die(
|
---|
964 | "range not compatible with selected mode");
|
---|
965 | }
|
---|
966 | }
|
---|
967 |
|
---|
968 | fd = i2c_dev_open(bus_num);
|
---|
969 | check_read_funcs(fd, mode, -1 /* data_addr */, pec);
|
---|
970 | i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
|
---|
971 |
|
---|
972 | if (pec)
|
---|
973 | i2c_set_pec(fd, 1);
|
---|
974 |
|
---|
975 | if (!(opts & opt_y))
|
---|
976 | confirm_action(bus_addr, mode, -1 /* data_addr */, pec);
|
---|
977 |
|
---|
978 | /* All but word data. */
|
---|
979 | if (mode != I2C_SMBUS_WORD_DATA || even) {
|
---|
980 | int blen = 0;
|
---|
981 |
|
---|
982 | if (mode == I2C_SMBUS_BLOCK_DATA || mode == I2C_SMBUS_I2C_BLOCK_DATA)
|
---|
983 | blen = read_block_data(fd, mode, block);
|
---|
984 |
|
---|
985 | if (mode == I2C_SMBUS_BYTE) {
|
---|
986 | res = i2c_smbus_write_byte(fd, first);
|
---|
987 | if (res < 0)
|
---|
988 | bb_perror_msg_and_die("write start address");
|
---|
989 | }
|
---|
990 |
|
---|
991 | dump_data(fd, mode, first, last, block, blen);
|
---|
992 | } else {
|
---|
993 | dump_word_data(fd, first, last);
|
---|
994 | }
|
---|
995 |
|
---|
996 | return 0;
|
---|
997 | }
|
---|
998 | #endif /* ENABLE_I2CDUMP */
|
---|
999 |
|
---|
1000 | #if ENABLE_I2CDETECT
|
---|
1001 | enum adapter_type {
|
---|
1002 | ADT_DUMMY = 0,
|
---|
1003 | ADT_ISA,
|
---|
1004 | ADT_I2C,
|
---|
1005 | ADT_SMBUS,
|
---|
1006 | };
|
---|
1007 |
|
---|
1008 | struct adap_desc {
|
---|
1009 | const char *funcs;
|
---|
1010 | const char *algo;
|
---|
1011 | };
|
---|
1012 |
|
---|
1013 | static const struct adap_desc adap_descs[] = {
|
---|
1014 | { .funcs = "dummy",
|
---|
1015 | .algo = "Dummy bus", },
|
---|
1016 | { .funcs = "isa",
|
---|
1017 | .algo = "ISA bus", },
|
---|
1018 | { .funcs = "i2c",
|
---|
1019 | .algo = "I2C adapter", },
|
---|
1020 | { .funcs = "smbus",
|
---|
1021 | .algo = "SMBus adapter", },
|
---|
1022 | };
|
---|
1023 |
|
---|
1024 | struct i2c_func
|
---|
1025 | {
|
---|
1026 | long value;
|
---|
1027 | const char* name;
|
---|
1028 | };
|
---|
1029 |
|
---|
1030 | static const struct i2c_func i2c_funcs_tab[] = {
|
---|
1031 | { .value = I2C_FUNC_I2C,
|
---|
1032 | .name = "I2C" },
|
---|
1033 | { .value = I2C_FUNC_SMBUS_QUICK,
|
---|
1034 | .name = "SMBus quick command" },
|
---|
1035 | { .value = I2C_FUNC_SMBUS_WRITE_BYTE,
|
---|
1036 | .name = "SMBus send byte" },
|
---|
1037 | { .value = I2C_FUNC_SMBUS_READ_BYTE,
|
---|
1038 | .name = "SMBus receive byte" },
|
---|
1039 | { .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
|
---|
1040 | .name = "SMBus write byte" },
|
---|
1041 | { .value = I2C_FUNC_SMBUS_READ_BYTE_DATA,
|
---|
1042 | .name = "SMBus read byte" },
|
---|
1043 | { .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA,
|
---|
1044 | .name = "SMBus write word" },
|
---|
1045 | { .value = I2C_FUNC_SMBUS_READ_WORD_DATA,
|
---|
1046 | .name = "SMBus read word" },
|
---|
1047 | { .value = I2C_FUNC_SMBUS_PROC_CALL,
|
---|
1048 | .name = "SMBus process call" },
|
---|
1049 | { .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA,
|
---|
1050 | .name = "SMBus block write" },
|
---|
1051 | { .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA,
|
---|
1052 | .name = "SMBus block read" },
|
---|
1053 | { .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL,
|
---|
1054 | .name = "SMBus block process call" },
|
---|
1055 | { .value = I2C_FUNC_SMBUS_PEC,
|
---|
1056 | .name = "SMBus PEC" },
|
---|
1057 | { .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
|
---|
1058 | .name = "I2C block write" },
|
---|
1059 | { .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK,
|
---|
1060 | .name = "I2C block read" },
|
---|
1061 | { .value = 0, .name = NULL }
|
---|
1062 | };
|
---|
1063 |
|
---|
1064 | static enum adapter_type i2cdetect_get_funcs(int bus)
|
---|
1065 | {
|
---|
1066 | enum adapter_type ret;
|
---|
1067 | unsigned long funcs;
|
---|
1068 | int fd;
|
---|
1069 |
|
---|
1070 | fd = i2c_dev_open(bus);
|
---|
1071 |
|
---|
1072 | get_funcs_matrix(fd, &funcs);
|
---|
1073 | if (funcs & I2C_FUNC_I2C)
|
---|
1074 | ret = ADT_I2C;
|
---|
1075 | else if (funcs & (I2C_FUNC_SMBUS_BYTE |
|
---|
1076 | I2C_FUNC_SMBUS_BYTE_DATA |
|
---|
1077 | I2C_FUNC_SMBUS_WORD_DATA))
|
---|
1078 | ret = ADT_SMBUS;
|
---|
1079 | else
|
---|
1080 | ret = ADT_DUMMY;
|
---|
1081 |
|
---|
1082 | close(fd);
|
---|
1083 |
|
---|
1084 | return ret;
|
---|
1085 | }
|
---|
1086 |
|
---|
1087 | static void NORETURN list_i2c_busses_and_exit(void)
|
---|
1088 | {
|
---|
1089 | const char *const i2cdev_path = "/sys/class/i2c-dev";
|
---|
1090 |
|
---|
1091 | char path[NAME_MAX], name[128];
|
---|
1092 | struct dirent *de, *subde;
|
---|
1093 | enum adapter_type adt;
|
---|
1094 | DIR *dir, *subdir;
|
---|
1095 | int rv, bus;
|
---|
1096 | char *pos;
|
---|
1097 | FILE *fp;
|
---|
1098 |
|
---|
1099 | /*
|
---|
1100 | * XXX Upstream i2cdetect also looks for i2c bus info in /proc/bus/i2c,
|
---|
1101 | * but we won't bother since it's only useful on older kernels (before
|
---|
1102 | * 2.6.5). We expect sysfs to be present and mounted at /sys/.
|
---|
1103 | */
|
---|
1104 |
|
---|
1105 | dir = xopendir(i2cdev_path);
|
---|
1106 | while ((de = readdir(dir))) {
|
---|
1107 | if (de->d_name[0] == '.')
|
---|
1108 | continue;
|
---|
1109 |
|
---|
1110 | /* Simple version for ISA chips. */
|
---|
1111 | snprintf(path, NAME_MAX, "%s/%s/name",
|
---|
1112 | i2cdev_path, de->d_name);
|
---|
1113 | fp = fopen(path, "r");
|
---|
1114 | if (fp == NULL) {
|
---|
1115 | snprintf(path, NAME_MAX,
|
---|
1116 | "%s/%s/device/name",
|
---|
1117 | i2cdev_path, de->d_name);
|
---|
1118 | fp = fopen(path, "r");
|
---|
1119 | }
|
---|
1120 |
|
---|
1121 | /* Non-ISA chips require the hard-way. */
|
---|
1122 | if (fp == NULL) {
|
---|
1123 | snprintf(path, NAME_MAX,
|
---|
1124 | "%s/%s/device/name",
|
---|
1125 | i2cdev_path, de->d_name);
|
---|
1126 | subdir = opendir(path);
|
---|
1127 | if (subdir == NULL)
|
---|
1128 | continue;
|
---|
1129 |
|
---|
1130 | while ((subde = readdir(subdir))) {
|
---|
1131 | if (subde->d_name[0] == '.')
|
---|
1132 | continue;
|
---|
1133 |
|
---|
1134 | if (is_prefixed_with(subde->d_name, "i2c-")) {
|
---|
1135 | snprintf(path, NAME_MAX,
|
---|
1136 | "%s/%s/device/%s/name",
|
---|
1137 | i2cdev_path, de->d_name,
|
---|
1138 | subde->d_name);
|
---|
1139 | fp = fopen(path, "r");
|
---|
1140 | break;
|
---|
1141 | }
|
---|
1142 | }
|
---|
1143 | }
|
---|
1144 |
|
---|
1145 | if (fp != NULL) {
|
---|
1146 | /*
|
---|
1147 | * Get the rest of the info and display a line
|
---|
1148 | * for a single bus.
|
---|
1149 | */
|
---|
1150 | memset(name, 0, sizeof(name));
|
---|
1151 | pos = fgets(name, sizeof(name), fp);
|
---|
1152 | fclose(fp);
|
---|
1153 | if (pos == NULL)
|
---|
1154 | continue;
|
---|
1155 |
|
---|
1156 | pos = strchr(name, '\n');
|
---|
1157 | if (pos != NULL)
|
---|
1158 | *pos = '\0';
|
---|
1159 |
|
---|
1160 | rv = sscanf(de->d_name, "i2c-%d", &bus);
|
---|
1161 | if (rv != 1)
|
---|
1162 | continue;
|
---|
1163 |
|
---|
1164 | if (is_prefixed_with(name, "ISA"))
|
---|
1165 | adt = ADT_ISA;
|
---|
1166 | else
|
---|
1167 | adt = i2cdetect_get_funcs(bus);
|
---|
1168 |
|
---|
1169 | printf(
|
---|
1170 | "i2c-%d\t%-10s\t%-32s\t%s\n",
|
---|
1171 | bus, adap_descs[adt].funcs,
|
---|
1172 | name, adap_descs[adt].algo);
|
---|
1173 | }
|
---|
1174 | }
|
---|
1175 |
|
---|
1176 | exit(EXIT_SUCCESS);
|
---|
1177 | }
|
---|
1178 |
|
---|
1179 | static void NORETURN no_support(const char *cmd)
|
---|
1180 | {
|
---|
1181 | bb_error_msg_and_die("bus doesn't support %s", cmd);
|
---|
1182 | }
|
---|
1183 |
|
---|
1184 | static void will_skip(const char *cmd)
|
---|
1185 | {
|
---|
1186 | bb_error_msg(
|
---|
1187 | "warning: can't use %s command, "
|
---|
1188 | "will skip some addresses", cmd);
|
---|
1189 | }
|
---|
1190 |
|
---|
1191 | //usage:#define i2cdetect_trivial_usage
|
---|
1192 | //usage: "[-F I2CBUS] [-l] [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]"
|
---|
1193 | //usage:#define i2cdetect_full_usage "\n\n"
|
---|
1194 | //usage: "Detect I2C chips.\n"
|
---|
1195 | //usage: "\n I2CBUS i2c bus number"
|
---|
1196 | //usage: "\n FIRST and LAST limit the probing range"
|
---|
1197 | //usage: "\n"
|
---|
1198 | //usage: "\n -l output list of installed busses"
|
---|
1199 | //usage: "\n -y disable interactive mode"
|
---|
1200 | //usage: "\n -a force scanning of non-regular addresses"
|
---|
1201 | //usage: "\n -q use smbus quick write commands for probing (default)"
|
---|
1202 | //usage: "\n -r use smbus read byte commands for probing"
|
---|
1203 | //usage: "\n -F display list of functionalities"
|
---|
1204 | int i2cdetect_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
1205 | int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
|
---|
1206 | {
|
---|
1207 | const unsigned opt_y = (1 << 0), opt_a = (1 << 1),
|
---|
1208 | opt_q = (1 << 2), opt_r = (1 << 3),
|
---|
1209 | opt_F = (1 << 4), opt_l = (1 << 5);
|
---|
1210 | const char *const optstr = "yaqrFl";
|
---|
1211 |
|
---|
1212 | int fd, bus_num, i, j, mode = I2CDETECT_MODE_AUTO, status, cmd;
|
---|
1213 | unsigned first = 0x03, last = 0x77, opts;
|
---|
1214 | unsigned long funcs;
|
---|
1215 |
|
---|
1216 | opt_complementary = "q--r:r--q:" /* mutually exclusive */
|
---|
1217 | "?3"; /* up to 3 args */
|
---|
1218 | opts = getopt32(argv, optstr);
|
---|
1219 | argv += optind;
|
---|
1220 |
|
---|
1221 | if (opts & opt_l)
|
---|
1222 | list_i2c_busses_and_exit();
|
---|
1223 |
|
---|
1224 | if (!argv[0])
|
---|
1225 | bb_show_usage();
|
---|
1226 |
|
---|
1227 | bus_num = i2c_bus_lookup(argv[0]);
|
---|
1228 | fd = i2c_dev_open(bus_num);
|
---|
1229 | get_funcs_matrix(fd, &funcs);
|
---|
1230 |
|
---|
1231 | if (opts & opt_F) {
|
---|
1232 | /* Only list the functionalities. */
|
---|
1233 | printf("Functionalities implemented by bus #%d\n", bus_num);
|
---|
1234 | for (i = 0; i2c_funcs_tab[i].value; i++) {
|
---|
1235 | printf("%-32s %s\n", i2c_funcs_tab[i].name,
|
---|
1236 | funcs & i2c_funcs_tab[i].value ? "yes" : "no");
|
---|
1237 | }
|
---|
1238 |
|
---|
1239 | return EXIT_SUCCESS;
|
---|
1240 | }
|
---|
1241 |
|
---|
1242 | if (opts & opt_r)
|
---|
1243 | mode = I2CDETECT_MODE_READ;
|
---|
1244 | else if (opts & opt_q)
|
---|
1245 | mode = I2CDETECT_MODE_QUICK;
|
---|
1246 |
|
---|
1247 | if (opts & opt_a) {
|
---|
1248 | first = 0x00;
|
---|
1249 | last = 0x7f;
|
---|
1250 | }
|
---|
1251 |
|
---|
1252 | /* Read address range. */
|
---|
1253 | if (argv[1]) {
|
---|
1254 | first = xstrtou_range(argv[1], 16, first, last);
|
---|
1255 | if (argv[2])
|
---|
1256 | last = xstrtou_range(argv[2], 16, first, last);
|
---|
1257 | }
|
---|
1258 |
|
---|
1259 | if (!(funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE))) {
|
---|
1260 | no_support("detection commands");
|
---|
1261 | } else
|
---|
1262 | if (mode == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)) {
|
---|
1263 | no_support("SMBus quick write");
|
---|
1264 | } else
|
---|
1265 | if (mode == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
|
---|
1266 | no_support("SMBus receive byte");
|
---|
1267 | }
|
---|
1268 |
|
---|
1269 | if (mode == I2CDETECT_MODE_AUTO) {
|
---|
1270 | if (!(funcs & I2C_FUNC_SMBUS_QUICK))
|
---|
1271 | will_skip("SMBus quick write");
|
---|
1272 | if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
|
---|
1273 | will_skip("SMBus receive byte");
|
---|
1274 | }
|
---|
1275 |
|
---|
1276 | if (!(opts & opt_y))
|
---|
1277 | confirm_action(-1, -1, -1, 0);
|
---|
1278 |
|
---|
1279 | puts(" 0 1 2 3 4 5 6 7 8 9 a b c d e f");
|
---|
1280 | for (i = 0; i < 128; i += 16) {
|
---|
1281 | printf("%02x: ", i);
|
---|
1282 | for (j = 0; j < 16; j++) {
|
---|
1283 | fflush_all();
|
---|
1284 |
|
---|
1285 | cmd = mode;
|
---|
1286 | if (mode == I2CDETECT_MODE_AUTO) {
|
---|
1287 | if ((i+j >= 0x30 && i+j <= 0x37) ||
|
---|
1288 | (i+j >= 0x50 && i+j <= 0x5F))
|
---|
1289 | cmd = I2CDETECT_MODE_READ;
|
---|
1290 | else
|
---|
1291 | cmd = I2CDETECT_MODE_QUICK;
|
---|
1292 | }
|
---|
1293 |
|
---|
1294 | /* Skip unwanted addresses. */
|
---|
1295 | if (i+j < first
|
---|
1296 | || i+j > last
|
---|
1297 | || (cmd == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE))
|
---|
1298 | || (cmd == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)))
|
---|
1299 | {
|
---|
1300 | printf(" ");
|
---|
1301 | continue;
|
---|
1302 | }
|
---|
1303 |
|
---|
1304 | status = ioctl(fd, I2C_SLAVE, itoptr(i + j));
|
---|
1305 | if (status < 0) {
|
---|
1306 | if (errno == EBUSY) {
|
---|
1307 | printf("UU ");
|
---|
1308 | continue;
|
---|
1309 | }
|
---|
1310 |
|
---|
1311 | bb_perror_msg_and_die(
|
---|
1312 | "can't set address to 0x%02x", i + j);
|
---|
1313 | }
|
---|
1314 |
|
---|
1315 | switch (cmd) {
|
---|
1316 | case I2CDETECT_MODE_READ:
|
---|
1317 | /*
|
---|
1318 | * This is known to lock SMBus on various
|
---|
1319 | * write-only chips (mainly clock chips).
|
---|
1320 | */
|
---|
1321 | status = i2c_smbus_read_byte(fd);
|
---|
1322 | break;
|
---|
1323 | default: /* I2CDETECT_MODE_QUICK: */
|
---|
1324 | /*
|
---|
1325 | * This is known to corrupt the Atmel
|
---|
1326 | * AT24RF08 EEPROM.
|
---|
1327 | */
|
---|
1328 | status = i2c_smbus_write_quick(fd,
|
---|
1329 | I2C_SMBUS_WRITE);
|
---|
1330 | break;
|
---|
1331 | }
|
---|
1332 |
|
---|
1333 | if (status < 0)
|
---|
1334 | printf("-- ");
|
---|
1335 | else
|
---|
1336 | printf("%02x ", i+j);
|
---|
1337 | }
|
---|
1338 | bb_putchar('\n');
|
---|
1339 | }
|
---|
1340 |
|
---|
1341 | return 0;
|
---|
1342 | }
|
---|
1343 | #endif /* ENABLE_I2CDETECT */
|
---|