svn commit: r323465 - head/usr.sbin/i2c
Andriy Gapon
avg at FreeBSD.org
Tue Oct 10 14:46:31 UTC 2017
On 12/09/2017 00:49, Ian Lepore wrote:
> Author: ian
> Date: Mon Sep 11 21:49:38 2017
> New Revision: 323465
> URL: https://svnweb.freebsd.org/changeset/base/323465
>
> Log:
> Make i2c -s (device scan) work on hardware that supports only full xfers.
>
> The existing scan code is based on sending an i2c START condition and if
> there is no error it assumes there is a device at that i2c address. Some
> i2c controllers don't support sending individual start/stop signals on the
> bus, they can only perform complete data transfers with start/stop handled
> in the silicon.
>
> This adds a fallback mechanism that attempts to read a single byte from each
> i2c address. It's less reliable than looking for an an ACK repsonse to a
> start, because some devices will NAK an attempt to read that isn't preceeded
> by a write of a register address. Writing to devices to probe them is too
> dangerous to even consider. The user is told that a less-reliable scan is
> being done, so even if the read-scan comes up empty too, it's still a vast
> improvement over the old situation where it would just claim there were no
> devices on the bus even though the devices were there and working fine.
>
> If the i2c controller responds with a proper ENODEV (device doesn't support
> operation) or an almost-proper EOPNOTSUPP, the START/STOP scan is switched
> to a read-scan right away. Most controllers respond with ENXIO or EIO if
> they don't support START/STOP, so no quick-out is available. For those,
> if a scan of all 127 addresses and come up empty, the scan is re-done using
> the read method.
Perhaps the new scan method should have been added as a separate option that has
to be explicitly activated... My concern is that there are some extremely
simple I2C devices out there that do no sanity checking and may get confused
> Reported by: Maxim Filimonov <che at bein.link>
>
> Modified:
> head/usr.sbin/i2c/i2c.c
>
> Modified: head/usr.sbin/i2c/i2c.c
> ==============================================================================
> --- head/usr.sbin/i2c/i2c.c Mon Sep 11 21:32:35 2017 (r323464)
> +++ head/usr.sbin/i2c/i2c.c Mon Sep 11 21:49:38 2017 (r323465)
> @@ -121,9 +121,12 @@ skip_get_tokens(char *skip_addr, int *sk_addr, int max
> static int
> scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr)
> {
> + struct iic_msg rdmsg;
> + struct iic_rdwr_data rdwrdata;
> struct skip_range addr_range = { 0, 0 };
> int *tokens, fd, error, i, index, j;
> - int len = 0, do_skip = 0, no_range = 1;
> + int len = 0, do_skip = 0, no_range = 1, num_found = 0, use_read_xfer = 0;
> + uint8_t rdbyte;
>
> fd = open(dev, O_RDWR);
> if (fd == -1) {
> @@ -157,6 +160,14 @@ scan_bus(struct iiccmd cmd, char *dev, int skip, char
> }
>
> printf("Scanning I2C devices on %s: ", dev);
> +
> +start_over:
> + if (use_read_xfer) {
> + fprintf(stderr,
> + "Hardware may not support START/STOP scanning; "
> + "trying less-reliable read method.\n");
> + }
> +
> for (i = 1; i < 127; i++) {
>
> if (skip && ( addr_range.start < addr_range.end)) {
> @@ -180,17 +191,46 @@ scan_bus(struct iiccmd cmd, char *dev, int skip, char
> cmd.last = 1;
> cmd.count = 0;
> error = ioctl(fd, I2CRSTCARD, &cmd);
> - if (error)
> + if (error) {
> + fprintf(stderr, "Controller reset failed\n");
> goto out;
> -
> - cmd.slave = i << 1;
> - cmd.last = 1;
> - error = ioctl(fd, I2CSTART, &cmd);
> - if (!error)
> - printf("%x ", i);
> - cmd.slave = i << 1;
> - cmd.last = 1;
> - error = ioctl(fd, I2CSTOP, &cmd);
> + }
> + if (use_read_xfer) {
> + rdmsg.buf = &rdbyte;
> + rdmsg.len = 1;
> + rdmsg.flags = IIC_M_RD;
> + rdmsg.slave = i << 1;
> + rdwrdata.msgs = &rdmsg;
> + rdwrdata.nmsgs = 1;
> + error = ioctl(fd, I2CRDWR, &rdwrdata);
> + } else {
> + cmd.slave = i << 1;
> + cmd.last = 1;
> + error = ioctl(fd, I2CSTART, &cmd);
> + if (errno == ENODEV || errno == EOPNOTSUPP) {
> + /* If START not supported try reading. */
> + use_read_xfer = 1;
> + goto start_over;
> + }
> + cmd.slave = i << 1;
> + cmd.last = 1;
> + ioctl(fd, I2CSTOP, &cmd);
> + }
> + if (error == 0) {
> + ++num_found;
> + printf("%02x ", i);
> + }
> + }
> + /*
> + * If we found nothing, maybe START is not supported and returns a
> + * generic error code such as EIO or ENXIO, so try again using reads.
> + */
> + if (num_found == 0) {
> + if (!use_read_xfer) {
> + use_read_xfer = 1;
> + goto start_over;
> + }
> + printf("<none found>");
> }
> printf("\n");
>
>
--
Andriy Gapon
More information about the svn-src-all
mailing list