From owner-svn-src-all@freebsd.org Mon Jul 8 14:34:01 2019 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id AA92615E00EB; Mon, 8 Jul 2019 14:34:01 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 50B89713E5; Mon, 8 Jul 2019 14:34:01 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 2BF2219C0B; Mon, 8 Jul 2019 14:34:01 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x68EY1RC099775; Mon, 8 Jul 2019 14:34:01 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x68EY0sr099774; Mon, 8 Jul 2019 14:34:00 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201907081434.x68EY0sr099774@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Mon, 8 Jul 2019 14:34:00 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r349836 - stable/11/usr.sbin/i2c X-SVN-Group: stable-11 X-SVN-Commit-Author: ian X-SVN-Commit-Paths: stable/11/usr.sbin/i2c X-SVN-Commit-Revision: 349836 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 50B89713E5 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.95 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_SHORT(-0.95)[-0.948,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Jul 2019 14:34:02 -0000 Author: ian Date: Mon Jul 8 14:34:00 2019 New Revision: 349836 URL: https://svnweb.freebsd.org/changeset/base/349836 Log: MFC r348120: Add a new 'tr' (transfer) mode to i2c(8) to support more i2c controllers. Some i2c controller hardware does not provide a way to do individual START, REPEAT-START and STOP actions on the i2c bus. Instead, they can only do a complete transfer as a single operation. Typically they can do either START-data-STOP or START-data-REPEATSTART-data-STOP. In the i2c driver framework, this corresponds to the iicbus_transfer method. In the userland interface they are initiated with the I2CRDWR ioctl command. These changes add a new 'tr' mode which can be specified with the '-m' command line option. This mode should work on all hardware; when an i2c controller driver doesn't directly support the iicbus_transfer method, code in the i2c driver framework uses the lower-level START/REPEAT/STOP methods to implement the transfer. After this new mode has gotten some testing on various hardware, the 'tr' mode should probably become the new default mode. PR: 189914 Modified: stable/11/usr.sbin/i2c/i2c.8 stable/11/usr.sbin/i2c/i2c.c Directory Properties: stable/11/ (props changed) Modified: stable/11/usr.sbin/i2c/i2c.8 ============================================================================== --- stable/11/usr.sbin/i2c/i2c.8 Mon Jul 8 14:04:44 2019 (r349835) +++ stable/11/usr.sbin/i2c/i2c.8 Mon Jul 8 14:34:00 2019 (r349836) @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 23, 2009 +.Dd May 22, 2019 .Dt I2C 8 .Os .Sh NAME @@ -39,7 +39,7 @@ .Op Fl w Ar 0|8|16 .Op Fl o Ar offset .Op Fl c Ar count -.Op Fl m Ar ss|rs|no +.Op Fl m Ar tr|ss|rs|no .Op Fl b .Op Fl v .Nm @@ -72,10 +72,29 @@ number of bytes to transfer (dec). transfer direction: r - read, w - write. .It Fl f Ar device I2C bus to use (default is /dev/iic0). -.It Fl m Ar ss|rs|no +.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 -operation. rs - repeated start; ss - stop start; no - none. +operation. +.Bl -tag -compact -offset indent +.It Va tr +complete-transfer +.It Va ss +stop then start +.It Va rs +repeated start +.It Va no +none +.El +Some I2C bus hardware does not provide control over the individual start, +repeat-start, and stop operations. +Such hardware can only perform a complete transfer of the offset and the +data as a single operation. +The +.Va tr +mode creates control structures describing the transfer and submits them +to the driver as a single complete transaction. +This mode works on all types of I2C hardware. .It Fl n Ar skip_addr skip address - address(es) to be skipped during bus scan. There are two ways to specify addresses to ignore: by range 'a..b' or Modified: stable/11/usr.sbin/i2c/i2c.c ============================================================================== --- stable/11/usr.sbin/i2c/i2c.c Mon Jul 8 14:04:44 2019 (r349835) +++ stable/11/usr.sbin/i2c/i2c.c Mon Jul 8 14:34:00 2019 (r349836) @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #define I2C_MODE_NONE 1 #define I2C_MODE_STOP_START 2 #define I2C_MODE_REPEATED_START 3 +#define I2C_MODE_TRANSFER 4 struct options { int width; @@ -73,7 +74,7 @@ usage(void) { fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] " - "[-w [0|8|16]] [-c count] [-m [ss|rs|no]] [-b] [-v]\n", + "[-w [0|8|16]] [-c count] [-m [tr|ss|rs|no]] [-b] [-v]\n", getprogname()); fprintf(stderr, " %s -s [-f device] [-n skip_addr] -v\n", getprogname()); @@ -297,24 +298,9 @@ static int i2c_write(char *dev, struct options i2c_opt, char *i2c_buf) { struct iiccmd cmd; - int ch, i, error, fd, bufsize; + int error, fd, bufsize; char *err_msg, *buf; - /* - * Read data to be written to the chip from stdin - */ - if (i2c_opt.verbose && !i2c_opt.binary) - fprintf(stderr, "Enter %u bytes of data: ", i2c_opt.count); - - for (i = 0; i < i2c_opt.count; i++) { - ch = getchar(); - if (ch == EOF) { - free(i2c_buf); - err(1, "not enough data, exiting\n"); - } - i2c_buf[i] = ch; - } - fd = open(dev, O_RDWR); if (fd == -1) { free(i2c_buf); @@ -558,6 +544,72 @@ err2: return (1); } +/* + * i2c_rdwr_transfer() - use I2CRDWR to conduct a complete i2c transfer. + * + * Some i2c hardware is unable to provide direct control over START, REPEAT- + * START, and STOP operations. Such hardware can only perform a complete + * START--STOP or START--REPEAT-START--STOP sequence as a + * single operation. The driver framework refers to this sequence as a + * "transfer" so we call it "transfer mode". We assemble either one or two + * iic_msg structures to describe the IO operations, and hand them off to the + * driver to be handled as a single transfer. + */ +static int +i2c_rdwr_transfer(char *dev, struct options i2c_opt, char *i2c_buf) +{ + struct iic_msg msgs[2]; + struct iic_rdwr_data xfer; + int fd, i; + union { + uint8_t buf[2]; + uint8_t off8; + uint16_t off16; + } off; + + 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.off8 = (uint8_t)i2c_opt.off; + msgs[i].len = 1; + } else { + off.off16 = (uint16_t)i2c_opt.off; + msgs[i].len = 2; + } + ++i; + } + + /* + * If the transfer direction is write and we did a write of the offset + * above, then we need to elide the start; this transfer is just more + * writing that follows the one started above. For a read, we always do + * a start; if we did an offset write above it'll be a repeat-start + * because of the NOSTOP flag used above. + */ + if (i2c_opt.dir == 'w') + msgs[i].flags = IIC_M_WR | (i > 0) ? IIC_M_NOSTART : 0; + 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; + + xfer.msgs = msgs; + xfer.nmsgs = i; + + if ((fd = open(dev, O_RDWR)) == -1) + err(1, "open(%s) failed", dev); + if (ioctl(fd, I2CRDWR, &xfer) == -1 ) + err(1, "ioctl(I2CRDWR) failed"); + close(fd); + + return (0); +} + int main(int argc, char** argv) { @@ -620,6 +672,8 @@ main(int argc, char** argv) i2c_opt.mode = I2C_MODE_STOP_START; else if (!strcmp(optarg, "rs")) i2c_opt.mode = I2C_MODE_REPEATED_START; + else if (!strcmp(optarg, "tr")) + i2c_opt.mode = I2C_MODE_TRANSFER; else usage(); break; @@ -687,19 +741,33 @@ main(int argc, char** argv) if (i2c_buf == NULL) err(1, "data malloc"); + /* + * For a write, read the data to be written to the chip from stdin. + */ if (i2c_opt.dir == 'w') { - error = i2c_write(dev, i2c_opt, i2c_buf); - if (error) { - free(i2c_buf); - return (1); + if (i2c_opt.verbose && !i2c_opt.binary) + fprintf(stderr, "Enter %u bytes of data: ", + i2c_opt.count); + for (i = 0; i < i2c_opt.count; i++) { + ch = getchar(); + if (ch == EOF) { + free(i2c_buf); + err(1, "not enough data, exiting\n"); + } + i2c_buf[i] = ch; } } - if (i2c_opt.dir == 'r') { + + if (i2c_opt.mode == I2C_MODE_TRANSFER) + error = i2c_rdwr_transfer(dev, i2c_opt, i2c_buf); + else if (i2c_opt.dir == 'w') + error = i2c_write(dev, i2c_opt, i2c_buf); + else error = i2c_read(dev, i2c_opt, i2c_buf); - if (error) { - free(i2c_buf); - return (1); - } + + if (error != 0) { + free(i2c_buf); + return (1); } if (i2c_opt.verbose)