Centralize offset width handling, and make it possible to specify explicit endianess.
This commit is contained in:
parent
38a4732f39
commit
e06874f3f6
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user