Index: usr.sbin/i2c/i2c.8 =================================================================== --- usr.sbin/i2c/i2c.8 +++ usr.sbin/i2c/i2c.8 @@ -1,5 +1,6 @@ .\" .\" Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka +.\" Copyright (C) 2015-2016 Emmanuel Vadot .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -25,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 23, 2009 +.Dd March 18, 2016 .Dt I2C 8 .Os .Sh NAME @@ -33,7 +34,7 @@ .Nd test I2C bus and slave devices .Sh SYNOPSIS .Nm -.Cm -a Ar address +.Cm -a|-A Ar address .Op Fl f Ar device .Op Fl d Ar r|w .Op Fl w Ar 0|8|16 @@ -41,6 +42,7 @@ .Op Fl c Ar count .Op Fl m Ar ss|rs|no .Op Fl b +.Op Fl x .Op Fl v .Nm .Cm -s @@ -61,7 +63,9 @@ The options are as follows: .Bl -tag -width ".Fl d Ar direction" .It Fl a Ar address -7-bit address on the I2C device to operate on (hex). +7-bit hex address of the I2C device. +.It Fl A Ar address +8-bit hex address of the I2C device. .It Fl b binary mode - when performing a read operation, the data read from the device is output in binary format on stdout; when doing a write, the binary data to @@ -86,7 +90,21 @@ .It Fl r reset the controller. .It Fl s -scan the bus for devices. +scan the bus for devices and print a table of all addresses. +The content of each cell can have these meanings: +.Bl -bullet +.It +"--" No chip answered +.It +"UU" Chip is in use by a driver +.It +"XX" The address was skipped (see -n) +.It +A 7-bit address of a chip. +.El +.It Fl x +Use the I2CRDRW ioctl instead of I2CREAD/I2CWRITE. +Some newer drivers only support this method. .It Fl v be verbose. .It Fl w Ar 0|8|16 Index: usr.sbin/i2c/i2c.c =================================================================== --- usr.sbin/i2c/i2c.c +++ usr.sbin/i2c/i2c.c @@ -1,5 +1,6 @@ /*- * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka + * Copyright (C) 2015 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,6 +58,7 @@ int reset; int mode; char dir; + char rdrw; uint32_t addr; uint32_t off; }; @@ -71,7 +73,7 @@ { 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 [ss|rs|no]] [-b] [-x] [-v]\n", getprogname()); fprintf(stderr, " %s -s [-f device] [-n skip_addr] -v\n", getprogname()); @@ -119,11 +121,13 @@ } static int -scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr) +scan_bus(struct iiccmd cmd, char rdrw, char *dev, int skip, char *skip_addr) { + struct iic_rdwr_data msg; struct skip_range addr_range = { 0, 0 }; - int *tokens, fd, error, i, index, j; - int len = 0, do_skip = 0, no_range = 1; + int *tokens, fd, error, index, i; + int len = 0, no_range = 1, row, col, addr, result; + char buf = 0; fd = open(dev, O_RDWR); if (fd == -1) { @@ -156,51 +160,86 @@ } } - printf("Scanning I2C devices on %s: ", dev); - for (i = 1; i < 127; i++) { - - if (skip && ( addr_range.start < addr_range.end)) { - if (i >= addr_range.start && i <= addr_range.end) - continue; + msg.nmsgs = 1; + if ((msg.msgs = calloc(1, sizeof(struct iic_msg))) == NULL) + errx(1, "%s", strerror(errno)); + msg.msgs->buf = &buf; + msg.msgs->len = 1; + msg.msgs->flags = IIC_M_RD; + + printf("Scanning I2C devices on %s:\n", dev); + printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + for (row = 1; row < 8; row++) { + printf("%02X:", row * 16); + for (col = 0; col < 16; col++) { + addr = (row * 16 + col); + result = 0; + + if (skip && ( addr_range.start < addr_range.end)) { + if (addr >= addr_range.start && addr <= addr_range.end) { + printf(" XX"); + continue; + } - } else if (skip && no_range) - for (j = 0; j < index; j++) { - if (tokens[j] == i) { - do_skip = 1; - break; + } else if (skip && no_range) + for (i = 0; i < index; i++) { + if (tokens[i] == (row * 16) + col) { + printf(" XX"); + continue; + } } - } - if (do_skip) { - do_skip = 0; - continue; + if (rdrw) { + msg.msgs->slave = addr << 1; + if (ioctl(fd, I2CRDWR, &msg) < 0) + result = errno; + } else { + cmd.slave = addr << 1; + cmd.last = 1; + cmd.count = 0; + if (ioctl(fd, I2CRSTCARD, &cmd) < 0) + goto out; + + cmd.slave = addr << 1; + cmd.last = 1; + if (ioctl(fd, I2CSTART, &cmd) < 0) + result = errno; + cmd.slave = addr << 1; + cmd.last = 1; + ioctl(fd, I2CSTOP, &cmd); + } + switch (result) { + case 0: + printf(" %02X", addr); + break; + case EIO: + case ENXIO: + case ENOENT: + printf(" --"); + break; + case EBUSY: + printf(" UU"); + break; + default: + printf(" "); + break; + } } - - cmd.slave = i << 1; - cmd.last = 1; - cmd.count = 0; - error = ioctl(fd, I2CRSTCARD, &cmd); - if (error) - 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); + printf("\n"); } printf("\n"); - error = ioctl(fd, I2CRSTCARD, &cmd); + if (!rdrw) + error = ioctl(fd, I2CRSTCARD, &cmd); out: close(fd); if (skip && no_range) free(tokens); - if (error) { + if (rdrw) + free(msg.msgs); + + if (!rdrw && error) { fprintf(stderr, "Error scanning I2C controller (%s): %s\n", dev, strerror(errno)); return (EX_NOINPUT); @@ -516,6 +555,87 @@ return (1); } +static int +i2c_rdrw(char *dev, struct options i2c_opt, char *i2c_buf) +{ + struct iic_rdwr_data msgs; + int fd, error, i; + char *err_msg, ch; + + if (i2c_opt.dir == 'w') { + 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) + err(1, "open failed"); + + msgs.nmsgs = 1; + if (i2c_opt.width) { + msgs.nmsgs = 2; + msgs.msgs = calloc(2, sizeof(struct iic_msg)); + msgs.msgs[0].slave = i2c_opt.addr; + msgs.msgs[0].len = i2c_opt.width / 8; + + if ((msgs.msgs[0].buf = calloc(msgs.msgs[0].len, sizeof(uint8_t))) == NULL) { + err_msg = "can't malloc\n"; + goto err; + } + if (msgs.msgs[0].len == 1) + msgs.msgs[0].buf[0] = i2c_opt.off & 0xff; + else if (msgs.msgs[0].len == 2) { + msgs.msgs[0].buf[0] = (i2c_opt.off >> 8) & 0xff; + msgs.msgs[0].buf[1] = i2c_opt.off & 0xff; + } + if (msgs.msgs[0].buf == NULL) { + err_msg = "error: offset malloc"; + goto err; + } + + msgs.msgs[0].flags = IIC_M_WR; + if (i2c_opt.mode == I2C_MODE_REPEATED_START || i2c_opt.mode == I2C_MODE_NONE) + msgs.msgs[0].flags |= IIC_M_NOSTOP; + } + + msgs.msgs[msgs.nmsgs - 1].slave = i2c_opt.addr; + msgs.msgs[msgs.nmsgs - 1].len = i2c_opt.count; + msgs.msgs[msgs.nmsgs - 1].buf = i2c_buf; + + if (i2c_opt.dir == 'r') + msgs.msgs[msgs.nmsgs - 1].flags = IIC_M_RD; + else + msgs.msgs[msgs.nmsgs - 1].flags = IIC_M_WR; + + if (i2c_opt.mode == I2C_MODE_NONE) + msgs.msgs[msgs.nmsgs - 1].flags |= IIC_M_NOSTART; + + error = ioctl(fd, I2CRDWR, &msgs); + if (error == -1) { + err_msg = "error sending i2c frame\n"; + goto err; + } + + close(fd); + return (0); + +err: + if (err_msg) + fprintf(stderr, "%s", err_msg); + + close(fd); + return (1); +} + int main(int argc, char** argv) { @@ -544,11 +664,15 @@ i2c_opt.skip = 0; /* scan all addresses */ i2c_opt.reset = 0; /* no bus reset */ i2c_opt.mode = I2C_MODE_NOTSET; + i2c_opt.rdrw = 0; /* Use read/write by default */ - while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) { + while ((ch = getopt(argc, argv, "a:A:f:d:o:w:c:m:n:sbvrhx")) != -1) { switch(ch) { case 'a': - i2c_opt.addr = (strtoul(optarg, 0, 16) << 1); + case 'A': + if (i2c_opt.addr_set) + usage(); + i2c_opt.addr = ch == 'a' ? (strtoul(optarg, 0, 16) << 1) : strtoul(optarg, 0, 16); if (i2c_opt.addr == 0 && errno == EINVAL) i2c_opt.addr_set = 0; else @@ -597,6 +721,9 @@ case 'r': i2c_opt.reset = 1; break; + case 'x': + i2c_opt.rdrw = 1; + break; case 'h': default: usage(); @@ -632,11 +759,11 @@ if (i2c_opt.verbose) fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, " "offset: 0x%02x, width: %u, count: %u\n", dev, - i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off, + i2c_opt.addr, i2c_opt.dir, i2c_opt.off, i2c_opt.width, i2c_opt.count); if (i2c_opt.scan) - exit(scan_bus(cmd, dev, i2c_opt.skip, skip_addr)); + exit(scan_bus(cmd, i2c_opt.rdrw, dev, i2c_opt.skip, skip_addr)); if (i2c_opt.reset) exit(reset_bus(cmd, dev)); @@ -645,19 +772,18 @@ if (i2c_buf == NULL) err(1, "data malloc"); - if (i2c_opt.dir == 'w') { - error = i2c_write(dev, i2c_opt, i2c_buf); - if (error) { - free(i2c_buf); - return (1); + if (i2c_opt.rdrw == 1) { + error = i2c_rdrw(dev, i2c_opt, i2c_buf); + } else { + if (i2c_opt.dir == 'w') { + error = i2c_write(dev, i2c_opt, i2c_buf); + } else if (i2c_opt.dir == 'r') { + error = i2c_read(dev, i2c_opt, i2c_buf); } } - if (i2c_opt.dir == 'r') { - error = i2c_read(dev, i2c_opt, i2c_buf); - if (error) { - free(i2c_buf); - return (1); - } + if (error) { + free(i2c_buf); + return (1); } if (i2c_opt.verbose)