The branch main has been updated by phk:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9c10d00bf8cdf7735cdba7c09bc16495ce22095b

commit 9c10d00bf8cdf7735cdba7c09bc16495ce22095b
Author:     Poul-Henning Kamp <p...@freebsd.org>
AuthorDate: 2021-05-19 18:56:59 +0000
Commit:     Poul-Henning Kamp <p...@freebsd.org>
CommitDate: 2021-05-19 18:56:59 +0000

    i2c(8): Add interpreted mode for batch/scripted i2c operations
---
 usr.sbin/i2c/i2c.8 |  63 ++++++++++++++--
 usr.sbin/i2c/i2c.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 274 insertions(+), 7 deletions(-)

diff --git a/usr.sbin/i2c/i2c.8 b/usr.sbin/i2c/i2c.8
index 92cc62e983aa..7c995f5743a4 100644
--- a/usr.sbin/i2c/i2c.8
+++ b/usr.sbin/i2c/i2c.8
@@ -43,19 +43,26 @@
 .Op Fl b
 .Op Fl v
 .Nm
-.Cm -s
-.Op Fl f Ar device
-.Op Fl n Ar skip_addr
+.Cm -h
+.Nm
+.Cm -i
 .Op Fl v
+.Op Ar cmd ...
+.Op Ar -
 .Nm
 .Cm -r
 .Op Fl f Ar device
 .Op Fl v
+.Nm
+.Cm -s
+.Op Fl f Ar device
+.Op Fl n Ar skip_addr
+.Op Fl v
 .Sh DESCRIPTION
 The
 .Nm
-utility can be used to perform raw data transfers (read or write) with devices
-on the I2C bus.
+utility can be used to perform raw data transfers (read or write) to devices
+on an I2C bus.
 It can also scan the bus for available devices and reset the I2C controller.
 .Pp
 The options are as follows:
@@ -72,6 +79,10 @@ 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 i
+Interpreted mode
+.It Fl h
+Help
 .It Fl m Ar tr|ss|rs|no
 addressing mode, i.e., I2C bus operations performed after the offset for the
 transfer has been written to the device and before the actual read/write
@@ -121,6 +132,37 @@ 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 INTERPRETED MODE
+When started with
+.Fl i
+any remaining arguments are interpreted as commands, and
+if the last argument is '-', or there are no arguments,
+commands will (also) be read from stdin.
+.Pp
+Available commands:
+.Bl -tag -compact
+.It 'r' bus address [0|8|16|16LE|16BE] offset count
+Read command, count bytes are read and hexdumped to stdout.
+.It 'w' bus address [0|8|16|16LE|16BE] offset hexstring
+Write command, hexstring (white-space is allowed) is written to device.
+.It 'p' anything
+Print command, the entire line is printed to stdout.  (This can be used
+for synchronization.)
+.El
+.Pp
+All numeric fields accept canonical decimal/octal/hex notation.
+.Pp
+Without the
+.Fl v
+option, all errors are fatal with non-zero exit status.
+.Pp
+With the
+.Fl v
+option, no errors are fatal, and all commands will return
+either "OK\en" or "ERROR\en" on stdout.
+In case of error, detailed diagnostics will precede that on stderr.
+.Pp
+Blank lines and lines starting with '#' are ignored.
 .Sh EXAMPLES
 .Bl -bullet
 .It
@@ -148,6 +190,14 @@ i2c -a 0x56 -f /dev/iic1 -d r -c 0x4 -b | i2c -a 0x57 -f 
/dev/iic0 -d w -c 4 -b
 Reset the controller:
 .Pp
 i2c -f /dev/iic1 -r
+.It
+Read 8 bytes at address 24 in an EEPROM:
+.Pp
+i2c -i 'r 0 0x50 16BE 24 8'
+.It
+Read 2x8 bytes at address 24 and 48 in an EEPROM:
+.Pp
+echo 'r 0 0x50 16BE 48 8' | i2c -i 'r 0 0x50 16BE 24 8' -
 .El
 .Sh WARNING
 Many systems store critical low-level information in I2C memories, and
@@ -171,3 +221,6 @@ utility and this manual page were written by
 .An Bartlomiej Sieka Aq Mt t...@semihalf.com
 and
 .An Michal Hajduk Aq Mt m...@semihalf.com .
+.Pp
+.An Poul-Henning Kamp Aq Mt p...@freebsd.org
+added interpreted mode.
diff --git a/usr.sbin/i2c/i2c.c b/usr.sbin/i2c/i2c.c
index a471b6533170..87bb1f0fe983 100644
--- a/usr.sbin/i2c/i2c.c
+++ b/usr.sbin/i2c/i2c.c
@@ -65,6 +65,9 @@ struct options {
        size_t          off_len;
 };
 
+#define N_FDCACHE 128
+static int fd_cache[N_FDCACHE];
+
 __dead2 static void
 usage(const char *msg)
 {
@@ -531,6 +534,210 @@ access_bus(int fd, struct options i2c_opt)
        return (error);
 }
 
+static const char *widths[] = {
+       "0",
+       "8",
+       "16LE",
+       "16BE",
+       "16",
+       NULL,
+};
+
+static int
+command_bus(struct options i2c_opt, char *cmd)
+{
+       int error, fd;
+       char devbuf[64];
+       uint8_t dbuf[BUFSIZ];
+       unsigned bus;
+       const char *width = NULL;
+       const char *err_msg;
+       unsigned offset;
+       unsigned u;
+       size_t length;
+
+       while (isspace(*cmd))
+               cmd++;
+
+       switch(*cmd) {
+       case 0:
+       case '#':
+               return (0);
+       case 'p':
+       case 'P':
+               printf("%s", cmd);
+               return (0);
+       case 'r':
+       case 'R':
+               i2c_opt.dir = 'r';
+               break;
+       case 'w':
+       case 'W':
+               i2c_opt.dir = 'w';
+               break;
+       default:
+               fprintf(stderr,
+                   "Did not understand command: 0x%02x ", *cmd);
+               if (isgraph(*cmd))
+                       fprintf(stderr, "'%c'", *cmd);
+               fprintf(stderr, "\n");
+               return(-1);
+       }
+       cmd++;
+
+       bus = strtoul(cmd, &cmd, 0);
+       if (bus == 0 && errno == EINVAL) {
+               fprintf(stderr, "Could not translate bus number\n");
+               return(-1);
+       }
+
+       i2c_opt.addr = strtoul(cmd, &cmd, 0);
+       if (i2c_opt.addr == 0 && errno == EINVAL) {
+               fprintf(stderr, "Could not translate device\n");
+               return(-1);
+       }
+       if (i2c_opt.addr < 1 || i2c_opt.addr > 0x7f) {
+               fprintf(stderr, "Invalid device (0x%x)\n", i2c_opt.addr);
+               return(-1);
+       }
+       i2c_opt.addr <<= 1;
+
+       while(isspace(*cmd))
+               cmd++;
+
+       for(u = 0; widths[u]; u++) {
+               length = strlen(widths[u]);
+               if (memcmp(cmd, widths[u], length))
+                       continue;
+               if (!isspace(cmd[length]))
+                       continue;
+               width = widths[u];
+               cmd += length;
+               break;
+       }
+       if (width == NULL) {
+               fprintf(stderr, "Invalid width\n");
+               return(-1);
+       }
+
+       offset = strtoul(cmd, &cmd, 0);
+       if (offset == 0 && errno == EINVAL) {
+               fprintf(stderr, "Could not translate offset\n");
+               return(-1);
+       }
+
+       err_msg = encode_offset(width, offset,
+           i2c_opt.off_buf, &i2c_opt.off_len);
+       if (err_msg) {
+               fprintf(stderr, "%s", err_msg);
+               return(-1);
+       }
+
+       if (i2c_opt.dir == 'r') {
+               i2c_opt.count = strtoul(cmd, &cmd, 0);
+               if (i2c_opt.count == 0 && errno == EINVAL) {
+                       fprintf(stderr, "Could not translate length\n");
+                       return(-1);
+               }
+       } else {
+               i2c_opt.count = 0;
+               while (1) {
+                       while(isspace(*cmd))
+                               cmd++;
+                       if (!*cmd)
+                               break;
+                       if (!isxdigit(*cmd)) {
+                               fprintf(stderr, "Not a hex digit.\n");
+                               return(-1);
+                       }
+                       dbuf[i2c_opt.count] = digittoint(*cmd++) << 4;
+                       while(isspace(*cmd))
+                               cmd++;
+                       if (!*cmd) {
+                               fprintf(stderr,
+                                   "Uneven number of hex digits.\n");
+                               return(-1);
+                       }
+                       if (!isxdigit(*cmd)) {
+                               fprintf(stderr, "Not a hex digit.\n");
+                               return(-1);
+                       }
+                       dbuf[i2c_opt.count++] |= digittoint(*cmd++);
+               }
+       }
+       assert(bus < N_FDCACHE);
+       fd = fd_cache[bus];
+       if (fd < 0) {
+               (void)sprintf(devbuf, "/dev/iic%u", bus);
+               fd = open(devbuf, O_RDWR);
+               if (fd == -1) {
+                       fprintf(stderr, "Error opening I2C controller (%s): 
%s\n",
+                           devbuf, strerror(errno));
+                       return (EX_NOINPUT);
+               }
+               fd_cache[bus] = fd;
+       }
+
+       error = i2c_rdwr_transfer(fd, i2c_opt, dbuf);
+       if (error)
+               return(-1);
+
+       if (i2c_opt.dir == 'r') {
+               for (u = 0; u < i2c_opt.count; u++)
+                       printf("%02x", dbuf[u]);
+               printf("\n");
+       }
+       return (0);
+}
+
+static int
+exec_bus(struct options i2c_opt, char *cmd)
+{
+       int error;
+
+       while (isspace(*cmd))
+               cmd++;
+       if (*cmd == '#' || *cmd == '\0')
+               return (0);
+       error = command_bus(i2c_opt, cmd);
+       if (i2c_opt.verbose) {
+               (void)fflush(stderr);
+               printf(error ? "ERROR\n" : "OK\n");
+               error = 0;
+       } else if (error) {
+               fprintf(stderr, "  in: %s", cmd);
+       }
+       (void)fflush(stdout);
+       return (error);
+}
+
+static int
+instruct_bus(struct options i2c_opt, int argc, char **argv)
+{
+       char buf[BUFSIZ];
+       int rd_cmds = (argc == 0);
+       int error;
+
+       while (argc-- > 0) {
+               if (argc == 0 && !strcmp(*argv, "-")) {
+                       rd_cmds = 1;
+               } else {
+                       error = exec_bus(i2c_opt, *argv);
+                       if (error)
+                               return (error);
+               }
+               argv++;
+       }
+       if (!rd_cmds)
+               return (0);
+       while (fgets(buf, sizeof buf, stdin) != NULL) {
+               error = exec_bus(i2c_opt, buf);
+               if (error)
+                       return (error);
+       }
+       return (0);
+}
+
 int
 main(int argc, char** argv)
 {
@@ -541,6 +748,8 @@ main(int argc, char** argv)
        char do_what = 0;
 
        dev = I2C_DEV;
+       for (ch = 0; ch < N_FDCACHE; ch++)
+               fd_cache[ch] = -1;
 
        /* Default values */
        i2c_opt.off = 0;
@@ -554,9 +763,10 @@ main(int argc, char** argv)
 
        /* Find out what we are going to do */
 
-       while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
+       while ((ch = getopt(argc, argv, "a:f:d:iw:c:m:n:sbvrh")) != -1) {
                switch(ch) {
                case 'a':
+               case 'i':
                case 'r':
                case 's':
                        if (do_what)
@@ -574,8 +784,9 @@ main(int argc, char** argv)
        /* Then handle the legal subset of arguments */
 
        switch (do_what) {
-       case 0: usage("Pick one of [-a|-h|-r|-s]"); break;
+       case 0: usage("Pick one of [-a|-h|-i|-r|-s]"); break;
        case 'a': optflags = "a:f:d:w:o:c:m:bv"; break;
+       case 'i': optflags = "iv"; break;
        case 'r': optflags = "rf:v"; break;
        case 's': optflags = "sf:n:v"; break;
        default: assert("Bad do_what");
@@ -610,6 +821,7 @@ main(int argc, char** argv)
                case 'f':
                        dev = optarg;
                        break;
+               case 'i': break;
                case 'm':
                        if (!strcmp(optarg, "no"))
                                i2c_opt.mode = I2C_MODE_NONE;
@@ -645,6 +857,8 @@ main(int argc, char** argv)
        }
        argc -= optind;
        argv += optind;
+       if (do_what == 'i')
+               return(instruct_bus(i2c_opt, argc, argv));
        if (argc > 0)
                usage("Too many arguments");
 
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to