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