Tested on a Raspberry Pi 400 with a BME680 connected to the relevant pins, and commands like `i2ctransfer 1 w3@0x77 0x60 0xb6 0xd0 r1`, which writes 0xb6 to register 0x60 of chip 0x77 on bus 1, and then reads one byte. --- toys/other/i2ctools.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+)
From 815076b3b4147c77c1d5b04defd50d298d21456c Mon Sep 17 00:00:00 2001 From: Elliott Hughes <e...@google.com> Date: Thu, 6 Jul 2023 16:19:26 -0700 Subject: [PATCH] Add i2ctransfer.
Tested on a Raspberry Pi 400 with a BME680 connected to the relevant pins, and commands like `i2ctransfer 1 w3@0x77 0x60 0xb6 0xd0 r1`, which writes 0xb6 to register 0x60 of chip 0x77 on bus 1, and then reads one byte. --- toys/other/i2ctools.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/toys/other/i2ctools.c b/toys/other/i2ctools.c index ad296adf..95280988 100644 --- a/toys/other/i2ctools.c +++ b/toys/other/i2ctools.c @@ -17,6 +17,7 @@ USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_SBIN)) USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_SBIN)) USE_I2CGET(NEWTOY(i2cget, "<2>3fy", TOYFLAG_USR|TOYFLAG_SBIN)) USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_SBIN)) +USE_I2CTRANSFER(NEWTOY(i2ctransfer, "<2vfy", TOYFLAG_USR|TOYFLAG_SBIN)) config I2CDETECT bool "i2cdetect" @@ -67,6 +68,22 @@ config I2CSET -f Force access to busy devices -y Skip confirmation prompts (yes to all) + +config I2CTRANSFER + bool "i2ctransfer" + default y + help + usage: i2ctransfer [-fy] BUS DESC [DATA...]... + + Make i2c transfers. DESC is 'r' for read or 'w' for write, followed by + the number of bytes to read or write, followed by '@' and a 7-bit address. + For any message after the first, the '@' and address can be omitted to + reuse the previous address. A 'w' DESC must be followed by the number of + DATA bytes that was specified in the DESC. + + -f Force access to busy devices + -v Verbose (show messages sent, not just received) + -y Skip confirmation prompts (yes to all) */ #define FOR_i2cdetect @@ -313,3 +330,87 @@ void i2cset_main(void) xioctl(fd, I2C_SMBUS, &ioctl_data); close(fd); } + +#define FOR_i2ctransfer +#include "generated/flags.h" + +static void show_msgs(FILE *fp, struct i2c_rdwr_ioctl_data *data, int before) +{ + int i; + + for (i = 0; i < data->nmsgs; i++) { + struct i2c_msg *msg = &data->msgs[i]; + int j, write = !msg->flags, hexdump; + + // Even with -v we can't show read data before it's read! + hexdump = (before && write) || (!before && !write) || (!before && FLAG(v)); + if (!before && !FLAG(v) && !hexdump) continue; + + if (before || FLAG(v)) { + fprintf(fp, "msg %d: addr 0x%02x, %s, length %u%s", i, msg->addr, + write ? "write" : "read", msg->len, hexdump ? ", data " : ""); + } + if (hexdump) { + for (j = 0; j < msg->len; j++) fprintf(fp, "0x%02x ", msg->buf[j]); + } + fprintf(fp, "\n"); + } +} + +void i2ctransfer_main(void) +{ + int fd, bus = atolx_range(toys.optargs[0], 0, 0x3f), i = 1, j; + char *arg, *addr_str; + struct i2c_rdwr_ioctl_data ioctl_data; + struct i2c_msg msgs[I2C_RDWR_IOCTL_MAX_MSGS], *msg; + + ioctl_data.msgs = msgs; + ioctl_data.nmsgs = 0; + + while ((arg = toys.optargs[i++])) { + if (ioctl_data.nmsgs >= I2C_RDWR_IOCTL_MAX_MSGS) error_exit("too much!"); + + msg = &msgs[ioctl_data.nmsgs]; + if (*arg == 'r') { + msg->flags = I2C_M_RD; + } else if (*arg == 'w') { + msg->flags = 0; + } else error_exit("expected read or write: %s", arg); + + addr_str = strchr(arg, '@'); + if (addr_str) { + msg->addr = atolx_range(addr_str + 1, 0, 0x7f); + *addr_str = '\0'; + } else { + if (ioctl_data.nmsgs == 0) error_exit("missing address: %s", arg); + msg->addr = msgs[ioctl_data.nmsgs - 1].addr; + } + + // The struct field is 16 bits, but the kernel (as of 6.4) limits each + // message to 8KiB. Either is far larger than you're likely to see in + // practice. + msg->len = atolx_range(arg + 1, 0, 0xffff); + msg->buf = xzalloc(msg->len); + if (*arg == 'w') { + for (j = 0; j < msg->len; j++) { + arg = toys.optargs[i++]; + if (!arg) error_exit("expected %d data bytes", msg->len); + msg->buf[j] = atolx_range(arg, 0, 0xff); + } + } + + ioctl_data.nmsgs++; + } + + fprintf(stderr, "Will send following messages on bus %d...\n", bus); + show_msgs(stderr, &ioctl_data, 1); + confirm("Send transfers on bus %d?", bus); + + fd = i2c_open(bus, 0, 0); + xioctl(fd, I2C_RDWR, &ioctl_data); + close(fd); + + show_msgs(stdout, &ioctl_data, 0); + + for (i = 0; i < ioctl_data.nmsgs; i++) free(msgs[i].buf); +} -- 2.41.0.255.g8b1d071c50-goog
_______________________________________________ Toybox mailing list Toybox@lists.landley.net http://lists.landley.net/listinfo.cgi/toybox-landley.net