diff options
author | Ron Yorston <rmy@pobox.com> | 2015-03-14 20:33:00 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2015-03-14 20:33:00 +0000 |
commit | a4f58436b78fe59e57620c6e0301f213ee25f273 (patch) | |
tree | 8355f724926e605280af2d6f2b1ccc6b1bd02dee /miscutils | |
parent | ba0c36cfcf84efbac6f89e27238e04bb57e9cd45 (diff) | |
parent | 49acc1a7618a28d34381cbb7661d7c981fcb238f (diff) | |
download | busybox-w32-a4f58436b78fe59e57620c6e0301f213ee25f273.tar.gz busybox-w32-a4f58436b78fe59e57620c6e0301f213ee25f273.tar.bz2 busybox-w32-a4f58436b78fe59e57620c6e0301f213ee25f273.zip |
Merge branch 'busybox' into merge
Conflicts:
coreutils/od_bloaty.c
libbb/lineedit.c
Diffstat (limited to 'miscutils')
-rw-r--r-- | miscutils/crond.c | 4 | ||||
-rw-r--r-- | miscutils/dc.c | 57 | ||||
-rw-r--r-- | miscutils/devfsd.c | 10 | ||||
-rw-r--r-- | miscutils/fbsplash.c | 2 | ||||
-rw-r--r-- | miscutils/i2c_tools.c | 1396 | ||||
-rw-r--r-- | miscutils/last.c | 6 | ||||
-rw-r--r-- | miscutils/last_fancy.c | 5 | ||||
-rw-r--r-- | miscutils/man.c | 4 | ||||
-rw-r--r-- | miscutils/ubi_tools.c | 12 |
9 files changed, 1446 insertions, 50 deletions
diff --git a/miscutils/crond.c b/miscutils/crond.c index 3659b9a6f..eb327f855 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c | |||
@@ -438,14 +438,14 @@ static void load_crontab(const char *fileName) | |||
438 | log5("user:%s entry:%s", fileName, parser->data); | 438 | log5("user:%s entry:%s", fileName, parser->data); |
439 | 439 | ||
440 | /* check if line is setting MAILTO= */ | 440 | /* check if line is setting MAILTO= */ |
441 | if (0 == strncmp(tokens[0], "MAILTO=", 7)) { | 441 | if (is_prefixed_with(tokens[0], "MAILTO=")) { |
442 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 442 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
443 | free(mailTo); | 443 | free(mailTo); |
444 | mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; | 444 | mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; |
445 | #endif /* otherwise just ignore such lines */ | 445 | #endif /* otherwise just ignore such lines */ |
446 | continue; | 446 | continue; |
447 | } | 447 | } |
448 | if (0 == strncmp(tokens[0], "SHELL=", 6)) { | 448 | if (is_prefixed_with(tokens[0], "SHELL=")) { |
449 | free(shell); | 449 | free(shell); |
450 | shell = xstrdup(&tokens[0][6]); | 450 | shell = xstrdup(&tokens[0][6]); |
451 | continue; | 451 | continue; |
diff --git a/miscutils/dc.c b/miscutils/dc.c index 6bcfbe249..f94d6fa6b 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c | |||
@@ -196,14 +196,6 @@ struct op { | |||
196 | }; | 196 | }; |
197 | 197 | ||
198 | static const struct op operators[] = { | 198 | static const struct op operators[] = { |
199 | {"+", add}, | ||
200 | {"add", add}, | ||
201 | {"-", sub}, | ||
202 | {"sub", sub}, | ||
203 | {"*", mul}, | ||
204 | {"mul", mul}, | ||
205 | {"/", divide}, | ||
206 | {"div", divide}, | ||
207 | #if ENABLE_FEATURE_DC_LIBM | 199 | #if ENABLE_FEATURE_DC_LIBM |
208 | {"**", power}, | 200 | {"**", power}, |
209 | {"exp", power}, | 201 | {"exp", power}, |
@@ -216,28 +208,47 @@ static const struct op operators[] = { | |||
216 | {"not", not}, | 208 | {"not", not}, |
217 | {"eor", eor}, | 209 | {"eor", eor}, |
218 | {"xor", eor}, | 210 | {"xor", eor}, |
211 | {"+", add}, | ||
212 | {"add", add}, | ||
213 | {"-", sub}, | ||
214 | {"sub", sub}, | ||
215 | {"*", mul}, | ||
216 | {"mul", mul}, | ||
217 | {"/", divide}, | ||
218 | {"div", divide}, | ||
219 | {"p", print_no_pop}, | 219 | {"p", print_no_pop}, |
220 | {"f", print_stack_no_pop}, | 220 | {"f", print_stack_no_pop}, |
221 | {"o", set_output_base}, | 221 | {"o", set_output_base}, |
222 | }; | 222 | }; |
223 | 223 | ||
224 | /* Feed the stack machine */ | ||
224 | static void stack_machine(const char *argument) | 225 | static void stack_machine(const char *argument) |
225 | { | 226 | { |
226 | char *end; | 227 | char *end; |
227 | double d; | 228 | double number; |
228 | const struct op *o; | 229 | const struct op *o; |
229 | 230 | ||
230 | d = strtod(argument, &end); | 231 | next: |
231 | if (end != argument && *end == '\0') { | 232 | number = strtod(argument, &end); |
232 | push(d); | 233 | if (end != argument) { |
233 | return; | 234 | argument = end; |
235 | push(number); | ||
236 | goto next; | ||
234 | } | 237 | } |
235 | 238 | ||
239 | /* We might have matched a digit, eventually advance the argument */ | ||
240 | argument = skip_whitespace(argument); | ||
241 | |||
242 | if (*argument == '\0') | ||
243 | return; | ||
244 | |||
236 | o = operators; | 245 | o = operators; |
237 | do { | 246 | do { |
238 | if (strcmp(o->name, argument) == 0) { | 247 | char *after_name = is_prefixed_with(argument, o->name); |
248 | if (after_name) { | ||
249 | argument = after_name; | ||
239 | o->function(); | 250 | o->function(); |
240 | return; | 251 | goto next; |
241 | } | 252 | } |
242 | o++; | 253 | o++; |
243 | } while (o != operators + ARRAY_SIZE(operators)); | 254 | } while (o != operators + ARRAY_SIZE(operators)); |
@@ -254,25 +265,11 @@ int dc_main(int argc UNUSED_PARAM, char **argv) | |||
254 | if (!argv[0]) { | 265 | if (!argv[0]) { |
255 | /* take stuff from stdin if no args are given */ | 266 | /* take stuff from stdin if no args are given */ |
256 | char *line; | 267 | char *line; |
257 | char *cursor; | ||
258 | char *token; | ||
259 | while ((line = xmalloc_fgetline(stdin)) != NULL) { | 268 | while ((line = xmalloc_fgetline(stdin)) != NULL) { |
260 | cursor = line; | 269 | stack_machine(line); |
261 | while (1) { | ||
262 | token = skip_whitespace(cursor); | ||
263 | if (*token == '\0') | ||
264 | break; | ||
265 | cursor = skip_non_whitespace(token); | ||
266 | if (*cursor != '\0') | ||
267 | *cursor++ = '\0'; | ||
268 | stack_machine(token); | ||
269 | } | ||
270 | free(line); | 270 | free(line); |
271 | } | 271 | } |
272 | } else { | 272 | } else { |
273 | // why? it breaks "dc -2 2 + p" | ||
274 | //if (argv[0][0] == '-') | ||
275 | // bb_show_usage(); | ||
276 | do { | 273 | do { |
277 | stack_machine(*argv); | 274 | stack_machine(*argv); |
278 | } while (*++argv); | 275 | } while (*++argv); |
diff --git a/miscutils/devfsd.c b/miscutils/devfsd.c index 96ffe0738..5a6aec6bd 100644 --- a/miscutils/devfsd.c +++ b/miscutils/devfsd.c | |||
@@ -1405,7 +1405,6 @@ const char *get_old_name(const char *devname, unsigned int namelen, | |||
1405 | int indexx; | 1405 | int indexx; |
1406 | const char *pty1; | 1406 | const char *pty1; |
1407 | const char *pty2; | 1407 | const char *pty2; |
1408 | size_t len; | ||
1409 | /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */ | 1408 | /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */ |
1410 | static const char *const fmt[] = { | 1409 | static const char *const fmt[] = { |
1411 | NULL , | 1410 | NULL , |
@@ -1425,12 +1424,11 @@ const char *get_old_name(const char *devname, unsigned int namelen, | |||
1425 | }; | 1424 | }; |
1426 | 1425 | ||
1427 | for (trans = translate_table; trans->match != NULL; ++trans) { | 1426 | for (trans = translate_table; trans->match != NULL; ++trans) { |
1428 | len = strlen(trans->match); | 1427 | char *after_match = is_prefixed_with(devname, trans->match); |
1429 | 1428 | if (after_match) { | |
1430 | if (strncmp(devname, trans->match, len) == 0) { | ||
1431 | if (trans->format == NULL) | 1429 | if (trans->format == NULL) |
1432 | return devname + len; | 1430 | return after_match; |
1433 | sprintf(buffer, trans->format, devname + len); | 1431 | sprintf(buffer, trans->format, after_match); |
1434 | return buffer; | 1432 | return buffer; |
1435 | } | 1433 | } |
1436 | } | 1434 | } |
diff --git a/miscutils/fbsplash.c b/miscutils/fbsplash.c index 7b695b26f..77033c258 100644 --- a/miscutils/fbsplash.c +++ b/miscutils/fbsplash.c | |||
@@ -516,7 +516,7 @@ int fbsplash_main(int argc UNUSED_PARAM, char **argv) | |||
516 | // handle a case when we have many buffered lines | 516 | // handle a case when we have many buffered lines |
517 | // already in the pipe | 517 | // already in the pipe |
518 | while ((num_buf = xmalloc_fgetline(fp)) != NULL) { | 518 | while ((num_buf = xmalloc_fgetline(fp)) != NULL) { |
519 | if (strncmp(num_buf, "exit", 4) == 0) { | 519 | if (is_prefixed_with(num_buf, "exit")) { |
520 | DEBUG_MESSAGE("exit"); | 520 | DEBUG_MESSAGE("exit"); |
521 | break; | 521 | break; |
522 | } | 522 | } |
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c new file mode 100644 index 000000000..03bb03974 --- /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 (is_prefixed_with(subde->d_name, "i2c-")) { | ||
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 (is_prefixed_with(name, "ISA")) | ||
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 */ | ||
diff --git a/miscutils/last.c b/miscutils/last.c index 24f6e1c78..a144c7e47 100644 --- a/miscutils/last.c +++ b/miscutils/last.c | |||
@@ -87,11 +87,11 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
87 | if (++n > 0) | 87 | if (++n > 0) |
88 | ut.ut_type = n != 3 ? n : SHUTDOWN_TIME; | 88 | ut.ut_type = n != 3 ? n : SHUTDOWN_TIME; |
89 | #else | 89 | #else |
90 | if (strncmp(ut.ut_user, "shutdown", 8) == 0) | 90 | if (is_prefixed_with(ut.ut_user, "shutdown")) |
91 | ut.ut_type = SHUTDOWN_TIME; | 91 | ut.ut_type = SHUTDOWN_TIME; |
92 | else if (strncmp(ut.ut_user, "reboot", 6) == 0) | 92 | else if (is_prefixed_with(ut.ut_user, "reboot")) |
93 | ut.ut_type = BOOT_TIME; | 93 | ut.ut_type = BOOT_TIME; |
94 | else if (strncmp(ut.ut_user, "runlevel", 8) == 0) | 94 | else if (is_prefixed_with(ut.ut_user, "runlevel")) |
95 | ut.ut_type = RUN_LVL; | 95 | ut.ut_type = RUN_LVL; |
96 | #endif | 96 | #endif |
97 | } else { | 97 | } else { |
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c index f687d7e16..16ed9e920 100644 --- a/miscutils/last_fancy.c +++ b/miscutils/last_fancy.c | |||
@@ -42,7 +42,7 @@ enum { | |||
42 | static void show_entry(struct utmp *ut, int state, time_t dur_secs) | 42 | static void show_entry(struct utmp *ut, int state, time_t dur_secs) |
43 | { | 43 | { |
44 | unsigned days, hours, mins; | 44 | unsigned days, hours, mins; |
45 | char duration[32]; | 45 | char duration[sizeof("(%u+02:02)") + sizeof(int)*3]; |
46 | char login_time[17]; | 46 | char login_time[17]; |
47 | char logout_time[8]; | 47 | char logout_time[8]; |
48 | const char *logout_str; | 48 | const char *logout_str; |
@@ -53,7 +53,8 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs) | |||
53 | * but some systems have it wrong */ | 53 | * but some systems have it wrong */ |
54 | tmp = ut->ut_tv.tv_sec; | 54 | tmp = ut->ut_tv.tv_sec; |
55 | safe_strncpy(login_time, ctime(&tmp), 17); | 55 | safe_strncpy(login_time, ctime(&tmp), 17); |
56 | snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11); | 56 | tmp = dur_secs; |
57 | snprintf(logout_time, 8, "- %s", ctime(&tmp) + 11); | ||
57 | 58 | ||
58 | dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); | 59 | dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0); |
59 | /* unsigned int is easier to divide than time_t (which may be signed long) */ | 60 | /* unsigned int is easier to divide than time_t (which may be signed long) */ |
diff --git a/miscutils/man.c b/miscutils/man.c index 3f389b435..4ce40b247 100644 --- a/miscutils/man.c +++ b/miscutils/man.c | |||
@@ -66,7 +66,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level) | |||
66 | goto ordinary_manpage; | 66 | goto ordinary_manpage; |
67 | 67 | ||
68 | line = xmalloc_open_zipped_read_close(man_filename, NULL); | 68 | line = xmalloc_open_zipped_read_close(man_filename, NULL); |
69 | if (!line || strncmp(line, ".so ", 4) != 0) { | 69 | if (!line || !is_prefixed_with(line, ".so ")) { |
70 | free(line); | 70 | free(line); |
71 | goto ordinary_manpage; | 71 | goto ordinary_manpage; |
72 | } | 72 | } |
@@ -232,7 +232,7 @@ int man_main(int argc UNUSED_PARAM, char **argv) | |||
232 | if (!token[1]) | 232 | if (!token[1]) |
233 | continue; | 233 | continue; |
234 | if (strcmp("DEFINE", token[0]) == 0) { | 234 | if (strcmp("DEFINE", token[0]) == 0) { |
235 | if (strncmp("pager", token[1], 5) == 0) { | 235 | if (is_prefixed_with("pager", token[1])) { |
236 | pager = xstrdup(skip_whitespace(token[1]) + 5); | 236 | pager = xstrdup(skip_whitespace(token[1]) + 5); |
237 | } | 237 | } |
238 | } else | 238 | } else |
diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c index 6c09fe534..dd1bda300 100644 --- a/miscutils/ubi_tools.c +++ b/miscutils/ubi_tools.c | |||
@@ -105,6 +105,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | |||
105 | int mtd_num; | 105 | int mtd_num; |
106 | int dev_num = UBI_DEV_NUM_AUTO; | 106 | int dev_num = UBI_DEV_NUM_AUTO; |
107 | int vol_id = UBI_VOL_NUM_AUTO; | 107 | int vol_id = UBI_VOL_NUM_AUTO; |
108 | int vid_hdr_offset = 0; | ||
108 | char *vol_name; | 109 | char *vol_name; |
109 | unsigned long long size_bytes = size_bytes; /* for compiler */ | 110 | unsigned long long size_bytes = size_bytes; /* for compiler */ |
110 | char *size_bytes_str; | 111 | char *size_bytes_str; |
@@ -133,10 +134,11 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | |||
133 | #define OPTION_a (1 << 5) | 134 | #define OPTION_a (1 << 5) |
134 | #define OPTION_t (1 << 6) | 135 | #define OPTION_t (1 << 6) |
135 | if (do_mkvol) { | 136 | if (do_mkvol) { |
136 | opt_complementary = "-1:d+:n+:a+"; | 137 | opt_complementary = "-1:d+:n+:a+:O+"; |
137 | opts = getopt32(argv, "md:n:N:s:a:t:", | 138 | opts = getopt32(argv, "md:n:N:s:a:t:O:", |
138 | &dev_num, &vol_id, | 139 | &dev_num, &vol_id, |
139 | &vol_name, &size_bytes_str, &alignment, &type | 140 | &vol_name, &size_bytes_str, &alignment, &type, |
141 | &vid_hdr_offset | ||
140 | ); | 142 | ); |
141 | } else | 143 | } else |
142 | if (do_update) { | 144 | if (do_update) { |
@@ -162,17 +164,19 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) | |||
162 | // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); | 164 | // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); |
163 | 165 | ||
164 | //usage:#define ubiattach_trivial_usage | 166 | //usage:#define ubiattach_trivial_usage |
165 | //usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" | 167 | //usage: "-m MTD_NUM [-d UBI_NUM] [-O VID_HDR_OFF] UBI_CTRL_DEV" |
166 | //usage:#define ubiattach_full_usage "\n\n" | 168 | //usage:#define ubiattach_full_usage "\n\n" |
167 | //usage: "Attach MTD device to UBI\n" | 169 | //usage: "Attach MTD device to UBI\n" |
168 | //usage: "\n -m MTD_NUM MTD device number to attach" | 170 | //usage: "\n -m MTD_NUM MTD device number to attach" |
169 | //usage: "\n -d UBI_NUM UBI device number to assign" | 171 | //usage: "\n -d UBI_NUM UBI device number to assign" |
172 | //usage: "\n -O VID_HDR_OFF VID header offset" | ||
170 | if (do_attach) { | 173 | if (do_attach) { |
171 | if (!(opts & OPTION_m)) | 174 | if (!(opts & OPTION_m)) |
172 | bb_error_msg_and_die("%s device not specified", "MTD"); | 175 | bb_error_msg_and_die("%s device not specified", "MTD"); |
173 | 176 | ||
174 | attach_req.mtd_num = mtd_num; | 177 | attach_req.mtd_num = mtd_num; |
175 | attach_req.ubi_num = dev_num; | 178 | attach_req.ubi_num = dev_num; |
179 | attach_req.vid_hdr_offset = vid_hdr_offset; | ||
176 | 180 | ||
177 | xioctl(fd, UBI_IOCATT, &attach_req); | 181 | xioctl(fd, UBI_IOCATT, &attach_req); |
178 | } else | 182 | } else |