From owner-svn-src-all@FreeBSD.ORG Mon Jan 26 14:00:51 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 0C0CB10656CA; Mon, 26 Jan 2009 14:00:51 +0000 (UTC) (envelope-from raj@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id EB4168FC1E; Mon, 26 Jan 2009 14:00:50 +0000 (UTC) (envelope-from raj@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n0QE0oOj051768; Mon, 26 Jan 2009 14:00:50 GMT (envelope-from raj@svn.freebsd.org) Received: (from raj@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n0QE0o5n051764; Mon, 26 Jan 2009 14:00:50 GMT (envelope-from raj@svn.freebsd.org) Message-Id: <200901261400.n0QE0o5n051764@svn.freebsd.org> From: Rafal Jaworowski Date: Mon, 26 Jan 2009 14:00:50 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r187712 - in head/usr.sbin: . i2c X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 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, 26 Jan 2009 14:00:52 -0000 Author: raj Date: Mon Jan 26 14:00:50 2009 New Revision: 187712 URL: http://svn.freebsd.org/changeset/base/187712 Log: Introduce the I2C diagnostic utility. It let's discover and inspect slave devices on the bus. Reviewed by: bms, stas Obtained from: Semihalf Added: head/usr.sbin/i2c/ head/usr.sbin/i2c/Makefile (contents, props changed) head/usr.sbin/i2c/i2c.8 (contents, props changed) head/usr.sbin/i2c/i2c.c (contents, props changed) Modified: head/usr.sbin/Makefile Modified: head/usr.sbin/Makefile ============================================================================== --- head/usr.sbin/Makefile Mon Jan 26 13:57:05 2009 (r187711) +++ head/usr.sbin/Makefile Mon Jan 26 14:00:50 2009 (r187712) @@ -65,6 +65,7 @@ SUBDIR= ${_ac} \ getpmac \ gstat \ ${_gssd} \ + i2c \ ifmcstat \ inetd \ iostat \ Added: head/usr.sbin/i2c/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/i2c/Makefile Mon Jan 26 14:00:50 2009 (r187712) @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= i2c +MAN= i2c.8 + +WARNS?= 2 + +.include Added: head/usr.sbin/i2c/i2c.8 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/i2c/i2c.8 Mon Jan 26 14:00:50 2009 (r187712) @@ -0,0 +1,168 @@ +.\" +.\" Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" + +.Dd Jan 23, 2009 +.Dt I2C 8 +.Os +.Sh NAME +.Nm i2c +.Nd test I2C bus and slave devices +.Sh SYNOPSIS +.Nm +.Cm -a Ar address +.Op Fl f Ar device +.Op Fl d Ar r|w +.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 b +.Op Fl v +.Nm +.Cm -s +.Op Fl f Ar device +.Op Fl n Ar skip_addr +.Op Fl v +.Nm +.Cm -r +.Op Fl f Ar device +.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. It can also scan the bus for available devices and reset the +I2C controller. +.Pp +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). +.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 +be written to the device is read from stdin. +.It Fl c Ar count +number of bytes to transfer (dec). +.It Fl d Ar r|w +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 +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. +.It Fl n Ar skip_addr +skip address - address(es) to be skipped during bus scan. +The are two ways to specify addresses to ignore: by range 'a..b' or +using selected addresses 'a:b:c'. This option is available only when "-s" is +used. +.It Fl o Ar offset +offset within the device for data transfer (hex). +.It Fl r +reset the controller. +.It Fl s +scan the bus for devices. +.It Fl v +be verbose +.It Fl w Ar 0|8|16 +device addressing width (in bits). +.El +.Sh WARNINGS +Great care must be taken when manipulating slave I2C devices with the +.Nm +utility. Often times important configuration data for the system is kept in +non-volatile but write enabled memories located on the I2C bus, for example +Ethernet hardware addresses, RAM module parameters (SPD), processor reset +configuration word etc. +.Pp +It is very easy to render the whole system unusable when such configuration +data is deleted or altered, so use the +.Dq -d w +(write) command only if you know exactly what you are doing. +.Pp +Also avoid ungraceful interrupting of an ongoing transaction on the I2C bus, +as it can lead to potentially dangerous effects. Consider the following +scenario: when the host CPU is reset (for whatever reason) in the middle of a +started I2C transaction, the I2C slave device could be left in write mode +waiting for data or offset to arrive. When the CPU reinitializes itself and +talks to this I2C slave device again, the commands and other control info it +sends are treated by the slave device as data or offset it was waiting for, +and there's great potential for corruption if such a write is performed. +.Sh EXAMPLES +.Pp +.Bl -bullet +.It +Scan the default bus (/dev/iic0) for devices: +.Pp +i2c -s +.It +Scan the default bus (/dev/iic0) for devices and skip addresses 0x56 and +0x45. +.Pp +i2c -s -n 0x56:0x45 +.It +Scan the default bus (/dev/iic0) for devices and skip address range +0x34 to 0x56. +.Pp +i2c -s -n 0x34..0x56 +.It +Read 8 bytes of data from device at address 0x56 (e.g., an EEPROM): +.Pp +i2c -a 0x56 -d r -c 8 +.It +Write 16 bytes of data from file data.bin to device 0x56 at offset 0x10: +.Pp +i2c -a 0x56 -d w -c 16 -o 0x10 -b < data.bin +.It +Copy 4 bytes between two EEPROMs (0x56 on /dev/iic1 to 0x57 on /dev/iic0): +.Pp +i2c -a 0x56 -f /dev/iic1 -d r -c 0x4 -b | i2c -a 0x57 -f /dev/iic0 -d w -c 4 -b +.It +Reset the controller: +.Pp +i2c -f /dev/iic1 -r +.El +.Sh SEE ALSO +.Xr iic 4 , +.Xr iicbus 4 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 8.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility and this manual page were written by +.An Bartlomiej Sieka +.Aq tur@semihalf.com +and +.An Michal Hajduk +.Aq mih@semihalf.com . Added: head/usr.sbin/i2c/i2c.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/i2c/i2c.c Mon Jan 26 14:00:50 2009 (r187712) @@ -0,0 +1,633 @@ +/*- + * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define I2C_DEV "/dev/iic0" +#define I2C_MODE_NOTSET 0 +#define I2C_MODE_NONE 1 +#define I2C_MODE_STOP_START 2 +#define I2C_MODE_REPEATED_START 3 + +struct options { + int width; + int count; + int verbose; + int addr_set; + int binary; + int scan; + int skip; + int reset; + int mode; + char dir; + uint32_t addr; + uint32_t off; +}; + +struct skip_range { + int start; + int end; +}; + +__dead2 static void +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", + getprogname()); + fprintf(stderr, " %s -s [-f device] [-n skip_addr] -v\n", + getprogname()); + fprintf(stderr, " %s -r [-f device] -v\n", getprogname()); + exit(EX_USAGE); +} + +static struct skip_range +skip_get_range(char *skip_addr) +{ + struct skip_range addr_range; + char *token; + + addr_range.start = 0; + addr_range.end = 0; + + token = strsep(&skip_addr, ".."); + if (token) { + addr_range.start = strtoul(token, 0, 16); + token = strsep(&skip_addr, ".."); + if ((token != NULL) && !atoi(token)) { + token = strsep(&skip_addr, ".."); + if (token) + addr_range.end = strtoul(token, 0, 16); + } + } + + return (addr_range); +} + +/* Parse the string to get hex 7 bits addresses */ +static int +skip_get_tokens(char *skip_addr, int *sk_addr, int max_index) +{ + char *token; + int i; + + for (i = 0; i < max_index; i++) { + token = strsep(&skip_addr, ":"); + if (token == NULL) + break; + sk_addr[i] = strtoul(token, 0, 16); + } + return (i); +} + +static int +scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr) +{ + struct skip_range addr_range = { 0, 0 }; + int *tokens, fd, error, i, index, j; + int len = 0, do_skip = 0, no_range = 1; + + fd = open(dev, O_RDWR); + if (fd == -1) { + fprintf(stderr, "Error opening I2C controller (%s) for " + "scanning: %s\n", dev, strerror(errno)); + return (EX_NOINPUT); + } + + if (skip) { + len = strlen(skip_addr); + if (strstr(skip_addr, "..") != NULL) { + addr_range = skip_get_range(skip_addr); + no_range = 0; + } else { + tokens = (int *)malloc((len / 2 + 1) * sizeof(int)); + if (tokens == NULL) { + fprintf(stderr, "Error allocating tokens " + "buffer\n"); + goto out; + } + index = skip_get_tokens(skip_addr, tokens, + len / 2 + 1); + } + + if (!no_range && (addr_range.start > addr_range.end)) { + fprintf(stderr, "Skip address out of range\n"); + goto out; + } + } + + 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; + + } else if (skip && no_range) + for (j = 0; j < index; j++) { + if (tokens[j] == i) { + do_skip = 1; + break; + } + } + + if (do_skip) { + do_skip = 0; + continue; + } + + 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"); + + error = ioctl(fd, I2CRSTCARD, &cmd); +out: + close(fd); + if (skip && no_range) + free(tokens); + + if (error) { + fprintf(stderr, "Error scanning I2C controller (%s): %s\n", + dev, strerror(errno)); + return (EX_NOINPUT); + } else + return (EX_OK); +} + +static int +reset_bus(struct iiccmd cmd, char *dev) +{ + int fd, error; + + fd = open(dev, O_RDWR); + if (fd == -1) { + fprintf(stderr, "Error opening I2C controller (%s) for " + "resetting: %s\n", dev, strerror(errno)); + return (EX_NOINPUT); + } + + printf("Resetting I2C controller on %s: ", dev); + error = ioctl(fd, I2CRSTCARD, &cmd); + close (fd); + + if (error) { + printf("error: %s\n", strerror(errno)); + return (EX_IOERR); + } else { + printf("OK\n"); + return (EX_OK); + } +} + +static char * +prepare_buf(int size, uint32_t off) +{ + char *buf; + + buf = malloc(size); + if (buf == NULL) + return (buf); + + if (size == 1) + buf[0] = off & 0xff; + else if (size == 2) { + buf[0] = (off >> 8) & 0xff; + buf[1] = off & 0xff; + } + + return (buf); +} + +static int +i2c_write(char *dev, struct options i2c_opt, char *i2c_buf) +{ + struct iiccmd cmd; + int ch, i, 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); + err(1, "open failed"); + } + + /* + * Write offset where the data will go + */ + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTART, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending start condition"; + goto err1; + } + + if (i2c_opt.width) { + bufsize = i2c_opt.width / 8; + buf = prepare_buf(bufsize, i2c_opt.off); + if (buf == NULL) { + err_msg = "error: offset malloc"; + goto err1; + } + + cmd.count = bufsize; + cmd.buf = buf; + error = ioctl(fd, I2CWRITE, &cmd); + free(buf); + if (error == -1) { + err_msg = "ioctl: error when write offset"; + goto err1; + } + } + + /* Mode - stop start */ + if (i2c_opt.mode == I2C_MODE_STOP_START) { + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTOP, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending stop condition"; + goto err2; + } + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTART, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending start condition"; + goto err1; + } + } + /* Mode - repeated start */ + if (i2c_opt.mode == I2C_MODE_REPEATED_START) { + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CRPTSTART, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending repeated start " + "condition"; + goto err1; + } + } + + /* + * Write the data + */ + cmd.count = i2c_opt.count; + cmd.buf = i2c_buf; + cmd.last = 0; + error = ioctl(fd, I2CWRITE, &cmd); + if (error == -1) { + err_msg = "ioctl: error when write"; + goto err1; + } + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTOP, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending stop condition"; + goto err2; + } + + close(fd); + return (0); + +err1: + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTOP, &cmd); + if (error == -1) + fprintf(stderr, "error sending stop condtion\n"); +err2: + if (err_msg) + fprintf(stderr, err_msg); + + close(fd); + return (1); +} + +static int +i2c_read(char *dev, struct options i2c_opt, char *i2c_buf) +{ + struct iiccmd cmd; + int i, fd, error, bufsize; + char *err_msg, data = 0, *buf; + + fd = open(dev, O_RDWR); + if (fd == -1) + err(1, "open failed"); + + bzero(&cmd, sizeof(cmd)); + + if (i2c_opt.width) { + cmd.slave = i2c_opt.addr; + cmd.count = 1; + cmd.last = 0; + cmd.buf = &data; + error = ioctl(fd, I2CSTART, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending start condition"; + goto err1; + } + bufsize = i2c_opt.width / 8; + buf = prepare_buf(bufsize, i2c_opt.off); + if (buf == NULL) { + err_msg = "error: offset malloc"; + goto err1; + } + + cmd.count = bufsize; + cmd.buf = buf; + cmd.last = 0; + error = ioctl(fd, I2CWRITE, &cmd); + free(buf); + if (error == -1) { + err_msg = "ioctl: error when write offset"; + goto err1; + } + + if (i2c_opt.mode == I2C_MODE_STOP_START) { + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTOP, &cmd); + if (error == -1) + goto err2; + } + } + cmd.slave = i2c_opt.addr; + cmd.count = 1; + cmd.last = 0; + cmd.buf = &data; + if (i2c_opt.mode == I2C_MODE_STOP_START) { + error = ioctl(fd, I2CSTART, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending start condition"; + goto err1; + } + } else if (i2c_opt.mode == I2C_MODE_REPEATED_START) { + error = ioctl(fd, I2CRPTSTART, &cmd); + if (error == -1) { + err_msg = "ioctl: error sending repeated start " + "condition"; + goto err1; + } + } + error = ioctl(fd, I2CSTOP, &cmd); + if (error == -1) + goto err2; + + for (i = 0; i < i2c_opt.count; i++) { + error = read(fd, &i2c_buf[i], 1); + if (error == -1) { + err_msg = "ioctl: error while reading"; + goto err1; + } + } + + close(fd); + return (0); + +err1: + cmd.slave = i2c_opt.addr; + error = ioctl(fd, I2CSTOP, &cmd); + if (error == -1) + fprintf(stderr, "error sending stop condtion\n"); +err2: + if (err_msg) + fprintf(stderr, err_msg); + + close(fd); + return (1); +} + +int +main(int argc, char** argv) +{ + struct iiccmd cmd; + struct options i2c_opt; + char *dev, *skip_addr, *err_msg, *i2c_buf; + int error, chunk_size, i, j, ch; + + errno = 0; + error = 0; + + /* Line-break the output every chunk_size bytes */ + chunk_size = 16; + + dev = I2C_DEV; + err_msg = NULL; + + /* Default values */ + i2c_opt.addr_set = 0; + i2c_opt.off = 0; + i2c_opt.verbose = 0; + i2c_opt.dir = 'r'; /* direction = read */ + i2c_opt.width = 8; + i2c_opt.count = 1; + i2c_opt.binary = 0; /* ASCII text output */ + i2c_opt.scan = 0; /* no bus scan */ + i2c_opt.skip = 0; /* scan all addresses */ + i2c_opt.reset = 0; /* no bus reset */ + i2c_opt.mode = I2C_MODE_NOTSET; + + while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) { + switch(ch) { + case 'a': + i2c_opt.addr = (strtoul(optarg, 0, 16) << 1); + if (i2c_opt.addr == 0 && errno == EINVAL) + i2c_opt.addr_set = 0; + else + i2c_opt.addr_set = 1; + break; + case 'f': + dev = optarg; + break; + case 'd': + i2c_opt.dir = optarg[0]; + break; + case 'o': + i2c_opt.off = strtoul(optarg, 0, 16); + if (i2c_opt.off == 0 && errno == EINVAL) + error = 1; + break; + case 'w': + i2c_opt.width = atoi(optarg); + break; + case 'c': + i2c_opt.count = atoi(optarg); + break; + case 'm': + if (!strcmp(optarg, "no")) + i2c_opt.mode = I2C_MODE_NONE; + else if (!strcmp(optarg, "ss")) + i2c_opt.mode = I2C_MODE_STOP_START; + else if (!strcmp(optarg, "rs")) + i2c_opt.mode = I2C_MODE_REPEATED_START; + else + usage(); + break; + case 'n': + i2c_opt.skip = 1; + skip_addr = optarg; + break; + case 's': + i2c_opt.scan = 1; + break; + case 'b': + i2c_opt.binary = 1; + break; + case 'v': + i2c_opt.verbose = 1; + break; + case 'r': + i2c_opt.reset = 1; + break; + case 'h': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + /* Set default mode if option -m is not specified */ + if (i2c_opt.mode == I2C_MODE_NOTSET) { + if (i2c_opt.dir == 'r') + i2c_opt.mode = I2C_MODE_STOP_START; + else if (i2c_opt.dir == 'w') + i2c_opt.mode = I2C_MODE_NONE; + } + + /* Basic sanity check of command line arguments */ + if (i2c_opt.scan) { + if (i2c_opt.addr_set) + usage(); + } else if (i2c_opt.reset) { + if (i2c_opt.addr_set) + usage(); + } else if (error) { + usage(); + } else if ((i2c_opt.dir == 'r' || i2c_opt.dir == 'w')) { + if ((i2c_opt.addr_set == 0) || + !(i2c_opt.width == 0 || i2c_opt.width == 8 || + i2c_opt.width == 16)) + usage(); + } + + 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.width, i2c_opt.count); + + if (i2c_opt.scan) + exit(scan_bus(cmd, dev, i2c_opt.skip, skip_addr)); + + if (i2c_opt.reset) + exit(reset_bus(cmd, dev)); + + i2c_buf = malloc(i2c_opt.count); + 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.dir == 'r') { + error = i2c_read(dev, i2c_opt, i2c_buf); + if (error) { + free(i2c_buf); + return (1); + } + } + + if (i2c_opt.verbose) + fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ? + "read" : "written"); + + i = 0; + j = 0; + while (i < i2c_opt.count) { + if (i2c_opt.verbose || (i2c_opt.dir == 'r' && + !i2c_opt.binary)) + fprintf (stderr, "%02hhx ", i2c_buf[i++]); + + if (i2c_opt.dir == 'r' && i2c_opt.binary) { + fprintf(stdout, "%c", i2c_buf[j++]); + if(!i2c_opt.verbose) + i++; + } + if (!i2c_opt.verbose && (i2c_opt.dir == 'w')) + break; + if ((i % chunk_size) == 0) + fprintf(stderr, "\n"); + } + if ((i % chunk_size) != 0) + fprintf(stderr, "\n"); + + free(i2c_buf); + return (0); +}