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 b
|
||||||
.Op Fl v
|
.Op Fl v
|
||||||
.Nm
|
.Nm
|
||||||
.Cm -s
|
.Cm -h
|
||||||
.Op Fl f Ar device
|
.Nm
|
||||||
.Op Fl n Ar skip_addr
|
.Cm -i
|
||||||
.Op Fl v
|
.Op Fl v
|
||||||
|
.Op Ar cmd ...
|
||||||
|
.Op Ar -
|
||||||
.Nm
|
.Nm
|
||||||
.Cm -r
|
.Cm -r
|
||||||
.Op Fl f Ar device
|
.Op Fl f Ar device
|
||||||
.Op Fl v
|
.Op Fl v
|
||||||
|
.Nm
|
||||||
|
.Cm -s
|
||||||
|
.Op Fl f Ar device
|
||||||
|
.Op Fl n Ar skip_addr
|
||||||
|
.Op Fl v
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
utility can be used to perform raw data transfers (read or write) with devices
|
utility can be used to perform raw data transfers (read or write) to devices
|
||||||
on the I2C bus.
|
on an I2C bus.
|
||||||
It can also scan the bus for available devices and reset the I2C controller.
|
It can also scan the bus for available devices and reset the I2C controller.
|
||||||
.Pp
|
.Pp
|
||||||
The options are as follows:
|
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.
|
Data to be written is read from stdin as binary bytes.
|
||||||
.It Fl f Ar device
|
.It Fl f Ar device
|
||||||
I2C bus to use (default is /dev/iic0).
|
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
|
.It Fl m Ar tr|ss|rs|no
|
||||||
addressing mode, i.e., I2C bus operations performed after the offset for the
|
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
|
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.
|
Zero means that the offset is ignored and not passed to the slave at all.
|
||||||
The endianess defaults to little-endian.
|
The endianess defaults to little-endian.
|
||||||
.El
|
.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
|
.Sh EXAMPLES
|
||||||
.Bl -bullet
|
.Bl -bullet
|
||||||
.It
|
.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:
|
Reset the controller:
|
||||||
.Pp
|
.Pp
|
||||||
i2c -f /dev/iic1 -r
|
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
|
.El
|
||||||
.Sh WARNING
|
.Sh WARNING
|
||||||
Many systems store critical low-level information in I2C memories, and
|
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
|
.An Bartlomiej Sieka Aq Mt tur@semihalf.com
|
||||||
and
|
and
|
||||||
.An Michal Hajduk Aq Mt mih@semihalf.com .
|
.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;
|
size_t off_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define N_FDCACHE 128
|
||||||
|
static int fd_cache[N_FDCACHE];
|
||||||
|
|
||||||
__dead2 static void
|
__dead2 static void
|
||||||
usage(const char *msg)
|
usage(const char *msg)
|
||||||
{
|
{
|
||||||
@ -531,6 +534,210 @@ access_bus(int fd, struct options i2c_opt)
|
|||||||
return (error);
|
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
|
int
|
||||||
main(int argc, char** argv)
|
main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
@ -541,6 +748,8 @@ main(int argc, char** argv)
|
|||||||
char do_what = 0;
|
char do_what = 0;
|
||||||
|
|
||||||
dev = I2C_DEV;
|
dev = I2C_DEV;
|
||||||
|
for (ch = 0; ch < N_FDCACHE; ch++)
|
||||||
|
fd_cache[ch] = -1;
|
||||||
|
|
||||||
/* Default values */
|
/* Default values */
|
||||||
i2c_opt.off = 0;
|
i2c_opt.off = 0;
|
||||||
@ -554,9 +763,10 @@ main(int argc, char** argv)
|
|||||||
|
|
||||||
/* Find out what we are going to do */
|
/* 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) {
|
switch(ch) {
|
||||||
case 'a':
|
case 'a':
|
||||||
|
case 'i':
|
||||||
case 'r':
|
case 'r':
|
||||||
case 's':
|
case 's':
|
||||||
if (do_what)
|
if (do_what)
|
||||||
@ -574,8 +784,9 @@ main(int argc, char** argv)
|
|||||||
/* Then handle the legal subset of arguments */
|
/* Then handle the legal subset of arguments */
|
||||||
|
|
||||||
switch (do_what) {
|
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 'a': optflags = "a:f:d:w:o:c:m:bv"; break;
|
||||||
|
case 'i': optflags = "iv"; break;
|
||||||
case 'r': optflags = "rf:v"; break;
|
case 'r': optflags = "rf:v"; break;
|
||||||
case 's': optflags = "sf:n:v"; break;
|
case 's': optflags = "sf:n:v"; break;
|
||||||
default: assert("Bad do_what");
|
default: assert("Bad do_what");
|
||||||
@ -610,6 +821,7 @@ main(int argc, char** argv)
|
|||||||
case 'f':
|
case 'f':
|
||||||
dev = optarg;
|
dev = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'i': break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (!strcmp(optarg, "no"))
|
if (!strcmp(optarg, "no"))
|
||||||
i2c_opt.mode = I2C_MODE_NONE;
|
i2c_opt.mode = I2C_MODE_NONE;
|
||||||
@ -645,6 +857,8 @@ main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
if (do_what == 'i')
|
||||||
|
return(instruct_bus(i2c_opt, argc, argv));
|
||||||
if (argc > 0)
|
if (argc > 0)
|
||||||
usage("Too many arguments");
|
usage("Too many arguments");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user