mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
More refactoring:
Extract the '-a' mode into a separate function, and simplify the hexdumping logic. Dont call usage() without telling why.
This commit is contained in:
parent
eec6aed5b8
commit
5ab41ff8e9
|
@ -64,12 +64,12 @@ The options are as follows:
|
|||
7-bit address on the I2C device to operate on (hex).
|
||||
.It Fl b
|
||||
binary mode - when performing a read operation, the data read from the device
|
||||
is output in binary format on stdout; when doing a write, the binary data to
|
||||
be written to the device is read from stdin.
|
||||
is output in binary format on stdout.
|
||||
.It Fl c Ar count
|
||||
number of bytes to transfer (dec).
|
||||
number of bytes to transfer (decimal).
|
||||
.It Fl d Ar r|w
|
||||
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 m Ar tr|ss|rs|no
|
||||
|
@ -113,7 +113,7 @@ scan the bus for devices.
|
|||
.It Fl v
|
||||
be verbose.
|
||||
.It Fl w Ar 0|8|16|16LE|16BE
|
||||
device addressing width (in bits).
|
||||
device offset width (in bits).
|
||||
This is used to determine how to pass
|
||||
.Ar offset
|
||||
specified with
|
||||
|
@ -122,28 +122,6 @@ 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 WARNINGS
|
||||
Great care must be taken when manipulating slave I2C devices with the
|
||||
.Nm
|
||||
utility.
|
||||
Often times important configuration data for the system is kept in non-volatile
|
||||
but write enabled memories located on the I2C bus, for example Ethernet hardware
|
||||
addresses, RAM module parameters (SPD), processor reset configuration word etc.
|
||||
.Pp
|
||||
It is very easy to render the whole system unusable when such configuration
|
||||
data is deleted or altered, so use the
|
||||
.Dq -d w
|
||||
(write) command only if you know exactly what you are doing.
|
||||
.Pp
|
||||
Also avoid ungraceful interrupting of an ongoing transaction on the I2C bus,
|
||||
as it can lead to potentially dangerous effects.
|
||||
Consider the following scenario: when the host CPU is reset (for whatever reason)
|
||||
in the middle of a started I2C transaction, the I2C slave device could be left
|
||||
in write mode waiting for data or offset to arrive.
|
||||
When the CPU reinitializes itself and talks to this I2C slave device again,
|
||||
the commands and other control info it sends are treated by the slave device
|
||||
as data or offset it was waiting for, and there's great potential for
|
||||
corruption if such a write is performed.
|
||||
.Sh EXAMPLES
|
||||
.Bl -bullet
|
||||
.It
|
||||
|
@ -177,9 +155,15 @@ Reset the controller:
|
|||
.Pp
|
||||
i2c -f /dev/iic1 -r
|
||||
.El
|
||||
.Sh WARNING
|
||||
Many systems store critical low-level information in I2C memories, and
|
||||
may contain other I2C devices, such as temperature or voltage sensors.
|
||||
Reading these can disturb the firmware's operation and writing to them
|
||||
can "brick" the hardware.
|
||||
.Sh SEE ALSO
|
||||
.Xr iic 4 ,
|
||||
.Xr iicbus 4
|
||||
.Xr smbus 4
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
|
|
|
@ -73,9 +73,11 @@ struct skip_range {
|
|||
};
|
||||
|
||||
__dead2 static void
|
||||
usage(void)
|
||||
usage(const char *msg)
|
||||
{
|
||||
|
||||
if (msg != NULL)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] "
|
||||
"[-w [0|8|16]] [-c count] [-m [tr|ss|rs|no]] [-b] [-v]\n",
|
||||
getprogname());
|
||||
|
@ -561,147 +563,11 @@ i2c_rdwr_transfer(int fd, struct options i2c_opt, char *i2c_buf)
|
|||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
static int
|
||||
access_bus(int fd, struct options i2c_opt)
|
||||
{
|
||||
struct options i2c_opt;
|
||||
char *skip_addr = NULL, *i2c_buf;
|
||||
const char *dev, *err_msg;
|
||||
int fd, error, chunk_size, i, j, ch;
|
||||
|
||||
errno = 0;
|
||||
error = 0;
|
||||
|
||||
/* Line-break the output every chunk_size bytes */
|
||||
chunk_size = 16;
|
||||
|
||||
dev = I2C_DEV;
|
||||
|
||||
/* Default values */
|
||||
i2c_opt.addr_set = 0;
|
||||
i2c_opt.off = 0;
|
||||
i2c_opt.verbose = 0;
|
||||
i2c_opt.dir = 'r'; /* direction = read */
|
||||
i2c_opt.width = "8";
|
||||
i2c_opt.count = 1;
|
||||
i2c_opt.binary = 0; /* ASCII text output */
|
||||
i2c_opt.scan = 0; /* no bus scan */
|
||||
i2c_opt.skip = 0; /* scan all addresses */
|
||||
i2c_opt.reset = 0; /* no bus reset */
|
||||
i2c_opt.mode = I2C_MODE_NOTSET;
|
||||
|
||||
while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
|
||||
switch(ch) {
|
||||
case 'a':
|
||||
i2c_opt.addr = (strtoul(optarg, 0, 16) << 1);
|
||||
if (i2c_opt.addr == 0 && errno == EINVAL)
|
||||
i2c_opt.addr_set = 0;
|
||||
else
|
||||
i2c_opt.addr_set = 1;
|
||||
break;
|
||||
case 'f':
|
||||
dev = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
i2c_opt.dir = optarg[0];
|
||||
break;
|
||||
case 'o':
|
||||
i2c_opt.off = strtoul(optarg, 0, 16);
|
||||
if (i2c_opt.off == 0 && errno == EINVAL)
|
||||
error = 1;
|
||||
break;
|
||||
case 'w':
|
||||
i2c_opt.width = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
i2c_opt.count = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
if (!strcmp(optarg, "no"))
|
||||
i2c_opt.mode = I2C_MODE_NONE;
|
||||
else if (!strcmp(optarg, "ss"))
|
||||
i2c_opt.mode = I2C_MODE_STOP_START;
|
||||
else if (!strcmp(optarg, "rs"))
|
||||
i2c_opt.mode = I2C_MODE_REPEATED_START;
|
||||
else if (!strcmp(optarg, "tr"))
|
||||
i2c_opt.mode = I2C_MODE_TRANSFER;
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
case 'n':
|
||||
i2c_opt.skip = 1;
|
||||
skip_addr = optarg;
|
||||
break;
|
||||
case 's':
|
||||
i2c_opt.scan = 1;
|
||||
break;
|
||||
case 'b':
|
||||
i2c_opt.binary = 1;
|
||||
break;
|
||||
case 'v':
|
||||
i2c_opt.verbose = 1;
|
||||
break;
|
||||
case 'r':
|
||||
i2c_opt.reset = 1;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc > 0) {
|
||||
fprintf(stderr, "Too many arguments\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
/* Set default mode if option -m is not specified */
|
||||
if (i2c_opt.mode == I2C_MODE_NOTSET) {
|
||||
if (i2c_opt.dir == 'r')
|
||||
i2c_opt.mode = I2C_MODE_STOP_START;
|
||||
else if (i2c_opt.dir == 'w')
|
||||
i2c_opt.mode = I2C_MODE_NONE;
|
||||
}
|
||||
|
||||
if (i2c_opt.addr_set) {
|
||||
err_msg = encode_offset(i2c_opt.width, i2c_opt.off,
|
||||
i2c_opt.off_buf, &i2c_opt.off_len);
|
||||
if (err_msg != NULL) {
|
||||
fprintf(stderr, "%s", err_msg);
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Basic sanity check of command line arguments */
|
||||
if (i2c_opt.scan) {
|
||||
if (i2c_opt.addr_set)
|
||||
usage();
|
||||
} else if (i2c_opt.reset) {
|
||||
if (i2c_opt.addr_set)
|
||||
usage();
|
||||
} else if (error) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if (i2c_opt.verbose)
|
||||
fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
|
||||
"offset: 0x%02x, width: %s, count: %d\n", dev,
|
||||
i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off,
|
||||
i2c_opt.width, i2c_opt.count);
|
||||
|
||||
fd = open(dev, O_RDWR);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Error opening I2C controller (%s): %s\n",
|
||||
dev, strerror(errno));
|
||||
return (EX_NOINPUT);
|
||||
}
|
||||
|
||||
if (i2c_opt.scan)
|
||||
exit(scan_bus(dev, fd, i2c_opt.skip, skip_addr));
|
||||
|
||||
if (i2c_opt.reset)
|
||||
exit(reset_bus(dev, fd));
|
||||
char *i2c_buf;
|
||||
int error, chunk_size = 16, i, ch;
|
||||
|
||||
i2c_buf = malloc(i2c_opt.count);
|
||||
if (i2c_buf == NULL)
|
||||
|
@ -731,38 +597,165 @@ main(int argc, char** argv)
|
|||
else
|
||||
error = i2c_read(fd, i2c_opt, i2c_buf);
|
||||
|
||||
if (error != 0) {
|
||||
free(i2c_buf);
|
||||
return (1);
|
||||
}
|
||||
|
||||
error = close(fd);
|
||||
assert(error == 0);
|
||||
|
||||
if (i2c_opt.verbose)
|
||||
fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ?
|
||||
"read" : "written");
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (i < i2c_opt.count) {
|
||||
if (i2c_opt.verbose || (i2c_opt.dir == 'r' &&
|
||||
!i2c_opt.binary))
|
||||
fprintf (stderr, "%02hhx ", i2c_buf[i++]);
|
||||
|
||||
if (error == 0) {
|
||||
if (i2c_opt.dir == 'r' && i2c_opt.binary) {
|
||||
fprintf(stdout, "%c", i2c_buf[j++]);
|
||||
if(!i2c_opt.verbose)
|
||||
i++;
|
||||
(void)fwrite(i2c_buf, 1, i2c_opt.count, stdout);
|
||||
} else if (i2c_opt.verbose || i2c_opt.dir == 'r') {
|
||||
if (i2c_opt.verbose)
|
||||
fprintf(stderr, "\nData %s (hex):\n",
|
||||
i2c_opt.dir == 'r' ? "read" : "written");
|
||||
|
||||
for (i = 0; i < i2c_opt.count; i++) {
|
||||
fprintf (stderr, "%02hhx ", i2c_buf[i]);
|
||||
if ((i % chunk_size) == chunk_size - 1)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
if ((i % chunk_size) != 0)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
if (!i2c_opt.verbose && (i2c_opt.dir == 'w'))
|
||||
break;
|
||||
if ((i % chunk_size) == 0)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
if ((i % chunk_size) != 0)
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
free(i2c_buf);
|
||||
return (0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
struct options i2c_opt;
|
||||
char *skip_addr = NULL;
|
||||
const char *dev, *err_msg;
|
||||
int fd, error, ch;
|
||||
|
||||
errno = 0;
|
||||
|
||||
dev = I2C_DEV;
|
||||
|
||||
/* Default values */
|
||||
i2c_opt.addr_set = 0;
|
||||
i2c_opt.off = 0;
|
||||
i2c_opt.verbose = 0;
|
||||
i2c_opt.dir = 'r'; /* direction = read */
|
||||
i2c_opt.width = "8";
|
||||
i2c_opt.count = 1;
|
||||
i2c_opt.binary = 0; /* ASCII text output */
|
||||
i2c_opt.scan = 0; /* no bus scan */
|
||||
i2c_opt.skip = 0; /* scan all addresses */
|
||||
i2c_opt.reset = 0; /* no bus reset */
|
||||
i2c_opt.mode = I2C_MODE_NOTSET;
|
||||
|
||||
while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
|
||||
switch(ch) {
|
||||
case 'a':
|
||||
i2c_opt.addr = (strtoul(optarg, 0, 16) << 1);
|
||||
if (i2c_opt.addr == 0 && errno == EINVAL)
|
||||
usage("Bad -a argument (hex)");
|
||||
i2c_opt.addr_set = 1;
|
||||
break;
|
||||
case 'f':
|
||||
dev = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
i2c_opt.dir = optarg[0];
|
||||
break;
|
||||
case 'o':
|
||||
i2c_opt.off = strtoul(optarg, 0, 16);
|
||||
if (i2c_opt.off == 0 && errno == EINVAL)
|
||||
usage("Bad -o argument (hex)");
|
||||
break;
|
||||
case 'w':
|
||||
i2c_opt.width = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
i2c_opt.count = (strtoul(optarg, 0, 10));
|
||||
if (i2c_opt.count == 0 && errno == EINVAL)
|
||||
usage("Bad -c argument (decimal)");
|
||||
break;
|
||||
case 'm':
|
||||
if (!strcmp(optarg, "no"))
|
||||
i2c_opt.mode = I2C_MODE_NONE;
|
||||
else if (!strcmp(optarg, "ss"))
|
||||
i2c_opt.mode = I2C_MODE_STOP_START;
|
||||
else if (!strcmp(optarg, "rs"))
|
||||
i2c_opt.mode = I2C_MODE_REPEATED_START;
|
||||
else if (!strcmp(optarg, "tr"))
|
||||
i2c_opt.mode = I2C_MODE_TRANSFER;
|
||||
else
|
||||
usage("Bad -m argument ([no|ss|rs|tr])");
|
||||
break;
|
||||
case 'n':
|
||||
i2c_opt.skip = 1;
|
||||
skip_addr = optarg;
|
||||
break;
|
||||
case 's':
|
||||
i2c_opt.scan = 1;
|
||||
break;
|
||||
case 'b':
|
||||
i2c_opt.binary = 1;
|
||||
break;
|
||||
case 'v':
|
||||
i2c_opt.verbose = 1;
|
||||
break;
|
||||
case 'r':
|
||||
i2c_opt.reset = 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage("Help:");
|
||||
break;
|
||||
default:
|
||||
usage("Bad argument");
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc > 0)
|
||||
usage("Too many arguments");
|
||||
|
||||
/* Set default mode if option -m is not specified */
|
||||
if (i2c_opt.mode == I2C_MODE_NOTSET) {
|
||||
if (i2c_opt.dir == 'r')
|
||||
i2c_opt.mode = I2C_MODE_STOP_START;
|
||||
else if (i2c_opt.dir == 'w')
|
||||
i2c_opt.mode = I2C_MODE_NONE;
|
||||
}
|
||||
|
||||
err_msg = encode_offset(i2c_opt.width, i2c_opt.off,
|
||||
i2c_opt.off_buf, &i2c_opt.off_len);
|
||||
if (err_msg != NULL) {
|
||||
fprintf(stderr, "%s", err_msg);
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
/* Basic sanity check of command line arguments */
|
||||
if (i2c_opt.scan) {
|
||||
if (i2c_opt.addr_set)
|
||||
usage("-s and -a are incompatible");
|
||||
} else if (i2c_opt.reset) {
|
||||
if (i2c_opt.addr_set)
|
||||
usage("-r and -a are incompatible");
|
||||
}
|
||||
|
||||
if (i2c_opt.verbose)
|
||||
fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
|
||||
"offset: 0x%02x, width: %s, count: %d\n", dev,
|
||||
i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off,
|
||||
i2c_opt.width, i2c_opt.count);
|
||||
|
||||
fd = open(dev, O_RDWR);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Error opening I2C controller (%s): %s\n",
|
||||
dev, strerror(errno));
|
||||
return (EX_NOINPUT);
|
||||
}
|
||||
|
||||
if (i2c_opt.scan)
|
||||
error = scan_bus(dev, fd, i2c_opt.skip, skip_addr);
|
||||
else if (i2c_opt.reset)
|
||||
error = reset_bus(dev, fd);
|
||||
else
|
||||
error = access_bus(fd, i2c_opt);
|
||||
|
||||
ch = close(fd);
|
||||
assert(ch == 0);
|
||||
return (error);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue