Centralize offset width handling, and make it possible to specify explicit endianess.

This commit is contained in:
Poul-Henning Kamp 2021-05-12 20:01:14 +00:00
parent 38a4732f39
commit e06874f3f6
2 changed files with 100 additions and 117 deletions

View File

@ -36,7 +36,7 @@
.Cm -a Ar address
.Op Fl f Ar device
.Op Fl d Ar r|w
.Op Fl w Ar 0|8|16
.Op Fl w Ar 0|8|16|16LE|16BE
.Op Fl o Ar offset
.Op Fl c Ar count
.Op Fl m Ar tr|ss|rs|no
@ -112,7 +112,7 @@ reset the controller.
scan the bus for devices.
.It Fl v
be verbose.
.It Fl w Ar 0|8|16
.It Fl w Ar 0|8|16|16LE|16BE
device addressing width (in bits).
This is used to determine how to pass
.Ar offset
@ -120,6 +120,7 @@ specified with
.Fl o
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

View File

@ -51,18 +51,20 @@ __FBSDID("$FreeBSD$");
#define I2C_MODE_TRANSFER 4
struct options {
int width;
int count;
int verbose;
int addr_set;
int binary;
int scan;
int skip;
int reset;
int mode;
char dir;
const char *width;
int count;
int verbose;
int addr_set;
int binary;
int scan;
int skip;
int reset;
int mode;
char dir;
uint32_t addr;
uint32_t off;
uint8_t off_buf[2];
size_t off_len;
};
struct skip_range {
@ -267,30 +269,56 @@ reset_bus(const char *dev, int fd)
}
}
static char *
prepare_buf(int size, uint32_t off)
static const char *
encode_offset(const char *width, unsigned address, uint8_t *dst, size_t *len)
{
char *buf;
buf = malloc(size);
if (buf == NULL)
return (buf);
if (size == 1)
buf[0] = off & 0xff;
else if (size == 2) {
buf[0] = (off >> 8) & 0xff;
buf[1] = off & 0xff;
if (!strcmp(width, "0")) {
*len = 0;
return (NULL);
}
if (!strcmp(width, "8")) {
if (address > 0xff)
return ("Invalid 8-bit address\n");
*dst = address;
*len = 1;
return (NULL);
}
if (address > 0xffff)
return ("Invalid 16-bit address\n");
if (!strcmp(width, "16LE") || !strcmp(width, "16")) {
le16enc(dst, address);
*len = 2;
return (NULL);
}
if (!strcmp(width, "16BE")) {
be16enc(dst, address);
*len = 2;
return (NULL);
}
return ("Invalid offset width, must be: 0|8|16|16LE|16BE\n");
}
return (buf);
static const char *
write_offset(int fd, struct options i2c_opt, struct iiccmd *cmd)
{
int error;
if (i2c_opt.off_len > 0) {
cmd->count = i2c_opt.off_len;
cmd->buf = i2c_opt.off_buf;
error = ioctl(fd, I2CWRITE, cmd);
if (error == -1)
return ("ioctl: error writing offset\n");
}
return (NULL);
}
static int
i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
{
struct iiccmd cmd;
int error, bufsize;
int error;
char *buf;
const char *err_msg;
@ -301,33 +329,11 @@ i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
goto err1;
}
if (i2c_opt.width) {
bufsize = i2c_opt.width / 8;
buf = prepare_buf(bufsize, i2c_opt.off);
if (buf == NULL) {
err_msg = "error: offset malloc";
goto err1;
}
} else {
bufsize = 0;
buf = NULL;
}
switch(i2c_opt.mode) {
case I2C_MODE_STOP_START:
/*
* Write offset where the data will go
*/
if (i2c_opt.width) {
cmd.count = bufsize;
cmd.buf = buf;
error = ioctl(fd, I2CWRITE, &cmd);
free(buf);
if (error == -1) {
err_msg = "ioctl: error writing offset";
goto err1;
}
}
err_msg = write_offset(fd, i2c_opt, &cmd);
if (err_msg != NULL)
goto err1;
error = ioctl(fd, I2CSTOP);
if (error == -1) {
@ -355,19 +361,9 @@ i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
break;
case I2C_MODE_REPEATED_START:
/*
* Write offset where the data will go
*/
if (i2c_opt.width) {
cmd.count = bufsize;
cmd.buf = buf;
error = ioctl(fd, I2CWRITE, &cmd);
free(buf);
if (error == -1) {
err_msg = "ioctl: error writing offset";
goto err1;
}
}
err_msg = write_offset(fd, i2c_opt, &cmd);
if (err_msg != NULL)
goto err1;
cmd.slave = i2c_opt.addr;
error = ioctl(fd, I2CRPTSTART, &cmd);
@ -392,17 +388,18 @@ i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
case I2C_MODE_NONE: /* fall through */
default:
buf = realloc(buf, bufsize + i2c_opt.count);
buf = malloc(i2c_opt.off_len + i2c_opt.count);
if (buf == NULL) {
err_msg = "error: data malloc";
goto err1;
}
memcpy(buf, i2c_opt.off_buf, i2c_opt.off_len);
memcpy(buf + bufsize, i2c_buf, i2c_opt.count);
memcpy(buf + i2c_opt.off_len, i2c_buf, i2c_opt.count);
/*
* Write offset and data
*/
cmd.count = bufsize + i2c_opt.count;
cmd.count = i2c_opt.off_len + i2c_opt.count;
cmd.buf = buf;
cmd.last = 0;
error = ioctl(fd, I2CWRITE, &cmd);
@ -436,13 +433,13 @@ static int
i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
{
struct iiccmd cmd;
int error, bufsize;
char data = 0, *buf;
int error;
char data = 0;
const char *err_msg;
bzero(&cmd, sizeof(cmd));
if (i2c_opt.width) {
if (i2c_opt.off_len) {
cmd.slave = i2c_opt.addr;
cmd.count = 1;
cmd.last = 0;
@ -452,22 +449,10 @@ i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
err_msg = "ioctl: error sending start condition";
goto err1;
}
bufsize = i2c_opt.width / 8;
buf = prepare_buf(bufsize, i2c_opt.off);
if (buf == NULL) {
err_msg = "error: offset malloc";
goto err1;
}
cmd.count = bufsize;
cmd.buf = buf;
cmd.last = 0;
error = ioctl(fd, I2CWRITE, &cmd);
free(buf);
if (error == -1) {
err_msg = "ioctl: error writing offset";
err_msg = write_offset(fd, i2c_opt, &cmd);
if (err_msg != NULL)
goto err1;
}
if (i2c_opt.mode == I2C_MODE_STOP_START) {
error = ioctl(fd, I2CSTOP);
@ -481,7 +466,7 @@ i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
cmd.count = 1;
cmd.last = 0;
cmd.buf = &data;
if (i2c_opt.mode == I2C_MODE_STOP_START || i2c_opt.width == 0) {
if (i2c_opt.mode == I2C_MODE_STOP_START || i2c_opt.off_len == 0) {
error = ioctl(fd, I2CSTART, &cmd);
if (error == -1) {
err_msg = "ioctl: error sending start condition";
@ -538,24 +523,17 @@ i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
static int
i2c_rdwr_transfer(int fd, struct options i2c_opt, char *i2c_buf)
{
struct iic_msg msgs[2];
struct iic_msg msgs[2], *msgp = msgs;
struct iic_rdwr_data xfer;
int i;
uint8_t off_buf[2];
int flag = 0;
i = 0;
if (i2c_opt.width > 0) {
msgs[i].flags = IIC_M_WR | IIC_M_NOSTOP;
msgs[i].slave = i2c_opt.addr;
msgs[i].buf = off_buf;
if (i2c_opt.width == 8) {
off_buf[0] = i2c_opt.off;
msgs[i].len = 1;
} else {
le16enc(off_buf, i2c_opt.off);
msgs[i].len = 2;
}
++i;
if (i2c_opt.off_len) {
msgp->flags = IIC_M_WR | IIC_M_NOSTOP;
msgp->slave = i2c_opt.addr;
msgp->buf = i2c_opt.off_buf;
msgp->len = i2c_opt.off_len;
msgp++;
flag = IIC_M_NOSTART;
}
/*
@ -566,16 +544,16 @@ i2c_rdwr_transfer(int fd, struct options i2c_opt, char *i2c_buf)
* because of the NOSTOP flag used above.
*/
if (i2c_opt.dir == 'w')
msgs[i].flags = IIC_M_WR | ((i > 0) ? IIC_M_NOSTART : 0);
msgp->flags = IIC_M_WR | flag;
else
msgs[i].flags = IIC_M_RD;
msgs[i].slave = i2c_opt.addr;
msgs[i].len = i2c_opt.count;
msgs[i].buf = i2c_buf;
++i;
msgp->flags = IIC_M_RD;
msgp->slave = i2c_opt.addr;
msgp->len = i2c_opt.count;
msgp->buf = i2c_buf;
msgp++;
xfer.msgs = msgs;
xfer.nmsgs = i;
xfer.nmsgs = msgp - msgs;
if (ioctl(fd, I2CRDWR, &xfer) == -1 )
err(1, "ioctl(I2CRDWR) failed");
@ -588,7 +566,7 @@ main(int argc, char** argv)
{
struct options i2c_opt;
char *skip_addr = NULL, *i2c_buf;
const char *dev;
const char *dev, *err_msg;
int fd, error, chunk_size, i, j, ch;
errno = 0;
@ -604,7 +582,7 @@ main(int argc, char** argv)
i2c_opt.off = 0;
i2c_opt.verbose = 0;
i2c_opt.dir = 'r'; /* direction = read */
i2c_opt.width = 8;
i2c_opt.width = "8";
i2c_opt.count = 1;
i2c_opt.binary = 0; /* ASCII text output */
i2c_opt.scan = 0; /* no bus scan */
@ -633,7 +611,7 @@ main(int argc, char** argv)
error = 1;
break;
case 'w':
i2c_opt.width = atoi(optarg);
i2c_opt.width = optarg;
break;
case 'c':
i2c_opt.count = atoi(optarg);
@ -686,6 +664,15 @@ main(int argc, char** argv)
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)
@ -695,16 +682,11 @@ main(int argc, char** argv)
usage();
} else if (error) {
usage();
} else if ((i2c_opt.dir == 'r' || i2c_opt.dir == 'w')) {
if ((i2c_opt.addr_set == 0) ||
!(i2c_opt.width == 0 || i2c_opt.width == 8 ||
i2c_opt.width == 16))
usage();
}
if (i2c_opt.verbose)
fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
"offset: 0x%02x, width: %d, count: %d\n", dev,
"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);