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 .Cm -a Ar address
.Op Fl f Ar device .Op Fl f Ar device
.Op Fl d Ar r|w .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 o Ar offset
.Op Fl c Ar count .Op Fl c Ar count
.Op Fl m Ar tr|ss|rs|no .Op Fl m Ar tr|ss|rs|no
@ -112,7 +112,7 @@ reset the controller.
scan the bus for devices. scan the bus for devices.
.It Fl v .It Fl v
be verbose. be verbose.
.It Fl w Ar 0|8|16 .It Fl w Ar 0|8|16|16LE|16BE
device addressing width (in bits). device addressing width (in bits).
This is used to determine how to pass This is used to determine how to pass
.Ar offset .Ar offset
@ -120,6 +120,7 @@ specified with
.Fl o .Fl o
to the slave. 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.
.El .El
.Sh WARNINGS .Sh WARNINGS
Great care must be taken when manipulating slave I2C devices with the 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 #define I2C_MODE_TRANSFER 4
struct options { struct options {
int width; const char *width;
int count; int count;
int verbose; int verbose;
int addr_set; int addr_set;
int binary; int binary;
int scan; int scan;
int skip; int skip;
int reset; int reset;
int mode; int mode;
char dir; char dir;
uint32_t addr; uint32_t addr;
uint32_t off; uint32_t off;
uint8_t off_buf[2];
size_t off_len;
}; };
struct skip_range { struct skip_range {
@ -267,30 +269,56 @@ reset_bus(const char *dev, int fd)
} }
} }
static char * static const char *
prepare_buf(int size, uint32_t off) encode_offset(const char *width, unsigned address, uint8_t *dst, size_t *len)
{ {
char *buf;
buf = malloc(size); if (!strcmp(width, "0")) {
if (buf == NULL) *len = 0;
return (buf); return (NULL);
if (size == 1)
buf[0] = off & 0xff;
else if (size == 2) {
buf[0] = (off >> 8) & 0xff;
buf[1] = off & 0xff;
} }
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 static int
i2c_write(int fd, struct options i2c_opt, char *i2c_buf) i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
{ {
struct iiccmd cmd; struct iiccmd cmd;
int error, bufsize; int error;
char *buf; char *buf;
const char *err_msg; const char *err_msg;
@ -301,33 +329,11 @@ i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
goto err1; 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) { switch(i2c_opt.mode) {
case I2C_MODE_STOP_START: case I2C_MODE_STOP_START:
/* err_msg = write_offset(fd, i2c_opt, &cmd);
* Write offset where the data will go if (err_msg != NULL)
*/ goto err1;
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;
}
}
error = ioctl(fd, I2CSTOP); error = ioctl(fd, I2CSTOP);
if (error == -1) { if (error == -1) {
@ -355,19 +361,9 @@ i2c_write(int fd, struct options i2c_opt, char *i2c_buf)
break; break;
case I2C_MODE_REPEATED_START: case I2C_MODE_REPEATED_START:
/* err_msg = write_offset(fd, i2c_opt, &cmd);
* Write offset where the data will go if (err_msg != NULL)
*/ goto err1;
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;
}
}
cmd.slave = i2c_opt.addr; cmd.slave = i2c_opt.addr;
error = ioctl(fd, I2CRPTSTART, &cmd); 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 */ case I2C_MODE_NONE: /* fall through */
default: default:
buf = realloc(buf, bufsize + i2c_opt.count); buf = malloc(i2c_opt.off_len + i2c_opt.count);
if (buf == NULL) { if (buf == NULL) {
err_msg = "error: data malloc"; err_msg = "error: data malloc";
goto err1; 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 * Write offset and data
*/ */
cmd.count = bufsize + i2c_opt.count; cmd.count = i2c_opt.off_len + i2c_opt.count;
cmd.buf = buf; cmd.buf = buf;
cmd.last = 0; cmd.last = 0;
error = ioctl(fd, I2CWRITE, &cmd); error = ioctl(fd, I2CWRITE, &cmd);
@ -436,13 +433,13 @@ static int
i2c_read(int fd, struct options i2c_opt, char *i2c_buf) i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
{ {
struct iiccmd cmd; struct iiccmd cmd;
int error, bufsize; int error;
char data = 0, *buf; char data = 0;
const char *err_msg; const char *err_msg;
bzero(&cmd, sizeof(cmd)); bzero(&cmd, sizeof(cmd));
if (i2c_opt.width) { if (i2c_opt.off_len) {
cmd.slave = i2c_opt.addr; cmd.slave = i2c_opt.addr;
cmd.count = 1; cmd.count = 1;
cmd.last = 0; 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"; err_msg = "ioctl: error sending start condition";
goto err1; 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; err_msg = write_offset(fd, i2c_opt, &cmd);
cmd.buf = buf; if (err_msg != NULL)
cmd.last = 0;
error = ioctl(fd, I2CWRITE, &cmd);
free(buf);
if (error == -1) {
err_msg = "ioctl: error writing offset";
goto err1; goto err1;
}
if (i2c_opt.mode == I2C_MODE_STOP_START) { if (i2c_opt.mode == I2C_MODE_STOP_START) {
error = ioctl(fd, I2CSTOP); error = ioctl(fd, I2CSTOP);
@ -481,7 +466,7 @@ i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
cmd.count = 1; cmd.count = 1;
cmd.last = 0; cmd.last = 0;
cmd.buf = &data; 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); error = ioctl(fd, I2CSTART, &cmd);
if (error == -1) { if (error == -1) {
err_msg = "ioctl: error sending start condition"; err_msg = "ioctl: error sending start condition";
@ -538,24 +523,17 @@ i2c_read(int fd, struct options i2c_opt, char *i2c_buf)
static int static int
i2c_rdwr_transfer(int fd, struct options i2c_opt, char *i2c_buf) 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; struct iic_rdwr_data xfer;
int i; int flag = 0;
uint8_t off_buf[2];
i = 0; if (i2c_opt.off_len) {
if (i2c_opt.width > 0) { msgp->flags = IIC_M_WR | IIC_M_NOSTOP;
msgs[i].flags = IIC_M_WR | IIC_M_NOSTOP; msgp->slave = i2c_opt.addr;
msgs[i].slave = i2c_opt.addr; msgp->buf = i2c_opt.off_buf;
msgs[i].buf = off_buf; msgp->len = i2c_opt.off_len;
if (i2c_opt.width == 8) { msgp++;
off_buf[0] = i2c_opt.off; flag = IIC_M_NOSTART;
msgs[i].len = 1;
} else {
le16enc(off_buf, i2c_opt.off);
msgs[i].len = 2;
}
++i;
} }
/* /*
@ -566,16 +544,16 @@ i2c_rdwr_transfer(int fd, struct options i2c_opt, char *i2c_buf)
* because of the NOSTOP flag used above. * because of the NOSTOP flag used above.
*/ */
if (i2c_opt.dir == 'w') if (i2c_opt.dir == 'w')
msgs[i].flags = IIC_M_WR | ((i > 0) ? IIC_M_NOSTART : 0); msgp->flags = IIC_M_WR | flag;
else else
msgs[i].flags = IIC_M_RD; msgp->flags = IIC_M_RD;
msgs[i].slave = i2c_opt.addr; msgp->slave = i2c_opt.addr;
msgs[i].len = i2c_opt.count; msgp->len = i2c_opt.count;
msgs[i].buf = i2c_buf; msgp->buf = i2c_buf;
++i; msgp++;
xfer.msgs = msgs; xfer.msgs = msgs;
xfer.nmsgs = i; xfer.nmsgs = msgp - msgs;
if (ioctl(fd, I2CRDWR, &xfer) == -1 ) if (ioctl(fd, I2CRDWR, &xfer) == -1 )
err(1, "ioctl(I2CRDWR) failed"); err(1, "ioctl(I2CRDWR) failed");
@ -588,7 +566,7 @@ main(int argc, char** argv)
{ {
struct options i2c_opt; struct options i2c_opt;
char *skip_addr = NULL, *i2c_buf; char *skip_addr = NULL, *i2c_buf;
const char *dev; const char *dev, *err_msg;
int fd, error, chunk_size, i, j, ch; int fd, error, chunk_size, i, j, ch;
errno = 0; errno = 0;
@ -604,7 +582,7 @@ main(int argc, char** argv)
i2c_opt.off = 0; i2c_opt.off = 0;
i2c_opt.verbose = 0; i2c_opt.verbose = 0;
i2c_opt.dir = 'r'; /* direction = read */ i2c_opt.dir = 'r'; /* direction = read */
i2c_opt.width = 8; i2c_opt.width = "8";
i2c_opt.count = 1; i2c_opt.count = 1;
i2c_opt.binary = 0; /* ASCII text output */ i2c_opt.binary = 0; /* ASCII text output */
i2c_opt.scan = 0; /* no bus scan */ i2c_opt.scan = 0; /* no bus scan */
@ -633,7 +611,7 @@ main(int argc, char** argv)
error = 1; error = 1;
break; break;
case 'w': case 'w':
i2c_opt.width = atoi(optarg); i2c_opt.width = optarg;
break; break;
case 'c': case 'c':
i2c_opt.count = atoi(optarg); i2c_opt.count = atoi(optarg);
@ -686,6 +664,15 @@ main(int argc, char** argv)
i2c_opt.mode = I2C_MODE_NONE; 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 */ /* Basic sanity check of command line arguments */
if (i2c_opt.scan) { if (i2c_opt.scan) {
if (i2c_opt.addr_set) if (i2c_opt.addr_set)
@ -695,16 +682,11 @@ main(int argc, char** argv)
usage(); usage();
} else if (error) { } else if (error) {
usage(); 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) if (i2c_opt.verbose)
fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, " 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.addr >> 1, i2c_opt.dir, i2c_opt.off,
i2c_opt.width, i2c_opt.count); i2c_opt.width, i2c_opt.count);