i2c(8): Add interpreted mode for batch/scripted i2c operations
This commit is contained in:
parent
e32b2bcff0
commit
9c10d00bf8
@ -43,19 +43,26 @@
|
||||
.Op Fl b
|
||||
.Op Fl v
|
||||
.Nm
|
||||
.Cm -s
|
||||
.Op Fl f Ar device
|
||||
.Op Fl n Ar skip_addr
|
||||
.Cm -h
|
||||
.Nm
|
||||
.Cm -i
|
||||
.Op Fl v
|
||||
.Op Ar cmd ...
|
||||
.Op Ar -
|
||||
.Nm
|
||||
.Cm -r
|
||||
.Op Fl f Ar device
|
||||
.Op Fl v
|
||||
.Nm
|
||||
.Cm -s
|
||||
.Op Fl f Ar device
|
||||
.Op Fl n Ar skip_addr
|
||||
.Op Fl v
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility can be used to perform raw data transfers (read or write) with devices
|
||||
on the I2C bus.
|
||||
utility can be used to perform raw data transfers (read or write) to devices
|
||||
on an I2C bus.
|
||||
It can also scan the bus for available devices and reset the I2C controller.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
@ -72,6 +79,10 @@ transfer direction: r - read, w - write.
|
||||
Data to be written is read from stdin as binary bytes.
|
||||
.It Fl f Ar device
|
||||
I2C bus to use (default is /dev/iic0).
|
||||
.It Fl i
|
||||
Interpreted mode
|
||||
.It Fl h
|
||||
Help
|
||||
.It Fl m Ar tr|ss|rs|no
|
||||
addressing mode, i.e., I2C bus operations performed after the offset for the
|
||||
transfer has been written to the device and before the actual read/write
|
||||
@ -121,6 +132,37 @@ to the slave.
|
||||
Zero means that the offset is ignored and not passed to the slave at all.
|
||||
The endianess defaults to little-endian.
|
||||
.El
|
||||
.Sh INTERPRETED MODE
|
||||
When started with
|
||||
.Fl i
|
||||
any remaining arguments are interpreted as commands, and
|
||||
if the last argument is '-', or there are no arguments,
|
||||
commands will (also) be read from stdin.
|
||||
.Pp
|
||||
Available commands:
|
||||
.Bl -tag -compact
|
||||
.It 'r' bus address [0|8|16|16LE|16BE] offset count
|
||||
Read command, count bytes are read and hexdumped to stdout.
|
||||
.It 'w' bus address [0|8|16|16LE|16BE] offset hexstring
|
||||
Write command, hexstring (white-space is allowed) is written to device.
|
||||
.It 'p' anything
|
||||
Print command, the entire line is printed to stdout. (This can be used
|
||||
for synchronization.)
|
||||
.El
|
||||
.Pp
|
||||
All numeric fields accept canonical decimal/octal/hex notation.
|
||||
.Pp
|
||||
Without the
|
||||
.Fl v
|
||||
option, all errors are fatal with non-zero exit status.
|
||||
.Pp
|
||||
With the
|
||||
.Fl v
|
||||
option, no errors are fatal, and all commands will return
|
||||
either "OK\en" or "ERROR\en" on stdout.
|
||||
In case of error, detailed diagnostics will precede that on stderr.
|
||||
.Pp
|
||||
Blank lines and lines starting with '#' are ignored.
|
||||
.Sh EXAMPLES
|
||||
.Bl -bullet
|
||||
.It
|
||||
@ -148,6 +190,14 @@ i2c -a 0x56 -f /dev/iic1 -d r -c 0x4 -b | i2c -a 0x57 -f /dev/iic0 -d w -c 4 -b
|
||||
Reset the controller:
|
||||
.Pp
|
||||
i2c -f /dev/iic1 -r
|
||||
.It
|
||||
Read 8 bytes at address 24 in an EEPROM:
|
||||
.Pp
|
||||
i2c -i 'r 0 0x50 16BE 24 8'
|
||||
.It
|
||||
Read 2x8 bytes at address 24 and 48 in an EEPROM:
|
||||
.Pp
|
||||
echo 'r 0 0x50 16BE 48 8' | i2c -i 'r 0 0x50 16BE 24 8' -
|
||||
.El
|
||||
.Sh WARNING
|
||||
Many systems store critical low-level information in I2C memories, and
|
||||
@ -171,3 +221,6 @@ utility and this manual page were written by
|
||||
.An Bartlomiej Sieka Aq Mt tur@semihalf.com
|
||||
and
|
||||
.An Michal Hajduk Aq Mt mih@semihalf.com .
|
||||
.Pp
|
||||
.An Poul-Henning Kamp Aq Mt phk@FreeBSD.org
|
||||
added interpreted mode.
|
||||
|
@ -65,6 +65,9 @@ struct options {
|
||||
size_t off_len;
|
||||
};
|
||||
|
||||
#define N_FDCACHE 128
|
||||
static int fd_cache[N_FDCACHE];
|
||||
|
||||
__dead2 static void
|
||||
usage(const char *msg)
|
||||
{
|
||||
@ -531,6 +534,210 @@ access_bus(int fd, struct options i2c_opt)
|
||||
return (error);
|
||||
}
|
||||
|
||||
static const char *widths[] = {
|
||||
"0",
|
||||
"8",
|
||||
"16LE",
|
||||
"16BE",
|
||||
"16",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int
|
||||
command_bus(struct options i2c_opt, char *cmd)
|
||||
{
|
||||
int error, fd;
|
||||
char devbuf[64];
|
||||
uint8_t dbuf[BUFSIZ];
|
||||
unsigned bus;
|
||||
const char *width = NULL;
|
||||
const char *err_msg;
|
||||
unsigned offset;
|
||||
unsigned u;
|
||||
size_t length;
|
||||
|
||||
while (isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
switch(*cmd) {
|
||||
case 0:
|
||||
case '#':
|
||||
return (0);
|
||||
case 'p':
|
||||
case 'P':
|
||||
printf("%s", cmd);
|
||||
return (0);
|
||||
case 'r':
|
||||
case 'R':
|
||||
i2c_opt.dir = 'r';
|
||||
break;
|
||||
case 'w':
|
||||
case 'W':
|
||||
i2c_opt.dir = 'w';
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"Did not understand command: 0x%02x ", *cmd);
|
||||
if (isgraph(*cmd))
|
||||
fprintf(stderr, "'%c'", *cmd);
|
||||
fprintf(stderr, "\n");
|
||||
return(-1);
|
||||
}
|
||||
cmd++;
|
||||
|
||||
bus = strtoul(cmd, &cmd, 0);
|
||||
if (bus == 0 && errno == EINVAL) {
|
||||
fprintf(stderr, "Could not translate bus number\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
i2c_opt.addr = strtoul(cmd, &cmd, 0);
|
||||
if (i2c_opt.addr == 0 && errno == EINVAL) {
|
||||
fprintf(stderr, "Could not translate device\n");
|
||||
return(-1);
|
||||
}
|
||||
if (i2c_opt.addr < 1 || i2c_opt.addr > 0x7f) {
|
||||
fprintf(stderr, "Invalid device (0x%x)\n", i2c_opt.addr);
|
||||
return(-1);
|
||||
}
|
||||
i2c_opt.addr <<= 1;
|
||||
|
||||
while(isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
for(u = 0; widths[u]; u++) {
|
||||
length = strlen(widths[u]);
|
||||
if (memcmp(cmd, widths[u], length))
|
||||
continue;
|
||||
if (!isspace(cmd[length]))
|
||||
continue;
|
||||
width = widths[u];
|
||||
cmd += length;
|
||||
break;
|
||||
}
|
||||
if (width == NULL) {
|
||||
fprintf(stderr, "Invalid width\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
offset = strtoul(cmd, &cmd, 0);
|
||||
if (offset == 0 && errno == EINVAL) {
|
||||
fprintf(stderr, "Could not translate offset\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
err_msg = encode_offset(width, offset,
|
||||
i2c_opt.off_buf, &i2c_opt.off_len);
|
||||
if (err_msg) {
|
||||
fprintf(stderr, "%s", err_msg);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (i2c_opt.dir == 'r') {
|
||||
i2c_opt.count = strtoul(cmd, &cmd, 0);
|
||||
if (i2c_opt.count == 0 && errno == EINVAL) {
|
||||
fprintf(stderr, "Could not translate length\n");
|
||||
return(-1);
|
||||
}
|
||||
} else {
|
||||
i2c_opt.count = 0;
|
||||
while (1) {
|
||||
while(isspace(*cmd))
|
||||
cmd++;
|
||||
if (!*cmd)
|
||||
break;
|
||||
if (!isxdigit(*cmd)) {
|
||||
fprintf(stderr, "Not a hex digit.\n");
|
||||
return(-1);
|
||||
}
|
||||
dbuf[i2c_opt.count] = digittoint(*cmd++) << 4;
|
||||
while(isspace(*cmd))
|
||||
cmd++;
|
||||
if (!*cmd) {
|
||||
fprintf(stderr,
|
||||
"Uneven number of hex digits.\n");
|
||||
return(-1);
|
||||
}
|
||||
if (!isxdigit(*cmd)) {
|
||||
fprintf(stderr, "Not a hex digit.\n");
|
||||
return(-1);
|
||||
}
|
||||
dbuf[i2c_opt.count++] |= digittoint(*cmd++);
|
||||
}
|
||||
}
|
||||
assert(bus < N_FDCACHE);
|
||||
fd = fd_cache[bus];
|
||||
if (fd < 0) {
|
||||
(void)sprintf(devbuf, "/dev/iic%u", bus);
|
||||
fd = open(devbuf, O_RDWR);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Error opening I2C controller (%s): %s\n",
|
||||
devbuf, strerror(errno));
|
||||
return (EX_NOINPUT);
|
||||
}
|
||||
fd_cache[bus] = fd;
|
||||
}
|
||||
|
||||
error = i2c_rdwr_transfer(fd, i2c_opt, dbuf);
|
||||
if (error)
|
||||
return(-1);
|
||||
|
||||
if (i2c_opt.dir == 'r') {
|
||||
for (u = 0; u < i2c_opt.count; u++)
|
||||
printf("%02x", dbuf[u]);
|
||||
printf("\n");
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_bus(struct options i2c_opt, char *cmd)
|
||||
{
|
||||
int error;
|
||||
|
||||
while (isspace(*cmd))
|
||||
cmd++;
|
||||
if (*cmd == '#' || *cmd == '\0')
|
||||
return (0);
|
||||
error = command_bus(i2c_opt, cmd);
|
||||
if (i2c_opt.verbose) {
|
||||
(void)fflush(stderr);
|
||||
printf(error ? "ERROR\n" : "OK\n");
|
||||
error = 0;
|
||||
} else if (error) {
|
||||
fprintf(stderr, " in: %s", cmd);
|
||||
}
|
||||
(void)fflush(stdout);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
instruct_bus(struct options i2c_opt, int argc, char **argv)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
int rd_cmds = (argc == 0);
|
||||
int error;
|
||||
|
||||
while (argc-- > 0) {
|
||||
if (argc == 0 && !strcmp(*argv, "-")) {
|
||||
rd_cmds = 1;
|
||||
} else {
|
||||
error = exec_bus(i2c_opt, *argv);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
argv++;
|
||||
}
|
||||
if (!rd_cmds)
|
||||
return (0);
|
||||
while (fgets(buf, sizeof buf, stdin) != NULL) {
|
||||
error = exec_bus(i2c_opt, buf);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
@ -541,6 +748,8 @@ main(int argc, char** argv)
|
||||
char do_what = 0;
|
||||
|
||||
dev = I2C_DEV;
|
||||
for (ch = 0; ch < N_FDCACHE; ch++)
|
||||
fd_cache[ch] = -1;
|
||||
|
||||
/* Default values */
|
||||
i2c_opt.off = 0;
|
||||
@ -554,9 +763,10 @@ main(int argc, char** argv)
|
||||
|
||||
/* Find out what we are going to do */
|
||||
|
||||
while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "a:f:d:iw:c:m:n:sbvrh")) != -1) {
|
||||
switch(ch) {
|
||||
case 'a':
|
||||
case 'i':
|
||||
case 'r':
|
||||
case 's':
|
||||
if (do_what)
|
||||
@ -574,8 +784,9 @@ main(int argc, char** argv)
|
||||
/* Then handle the legal subset of arguments */
|
||||
|
||||
switch (do_what) {
|
||||
case 0: usage("Pick one of [-a|-h|-r|-s]"); break;
|
||||
case 0: usage("Pick one of [-a|-h|-i|-r|-s]"); break;
|
||||
case 'a': optflags = "a:f:d:w:o:c:m:bv"; break;
|
||||
case 'i': optflags = "iv"; break;
|
||||
case 'r': optflags = "rf:v"; break;
|
||||
case 's': optflags = "sf:n:v"; break;
|
||||
default: assert("Bad do_what");
|
||||
@ -610,6 +821,7 @@ main(int argc, char** argv)
|
||||
case 'f':
|
||||
dev = optarg;
|
||||
break;
|
||||
case 'i': break;
|
||||
case 'm':
|
||||
if (!strcmp(optarg, "no"))
|
||||
i2c_opt.mode = I2C_MODE_NONE;
|
||||
@ -645,6 +857,8 @@ main(int argc, char** argv)
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (do_what == 'i')
|
||||
return(instruct_bus(i2c_opt, argc, argv));
|
||||
if (argc > 0)
|
||||
usage("Too many arguments");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user