xref: /freebsd/usr.sbin/i2c/i2c.c (revision 14980d69ff0fce53cad8188b4d14fd30ad6b93c0)
1477f656bSRafal Jaworowski /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4477f656bSRafal Jaworowski  * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka
5477f656bSRafal Jaworowski  * All rights reserved.
6477f656bSRafal Jaworowski  *
7477f656bSRafal Jaworowski  * Redistribution and use in source and binary forms, with or without
8477f656bSRafal Jaworowski  * modification, are permitted provided that the following conditions
9477f656bSRafal Jaworowski  * are met:
10477f656bSRafal Jaworowski  * 1. Redistributions of source code must retain the above copyright
11477f656bSRafal Jaworowski  *    notice, this list of conditions and the following disclaimer.
12477f656bSRafal Jaworowski  * 2. Redistributions in binary form must reproduce the above copyright
13477f656bSRafal Jaworowski  *    notice, this list of conditions and the following disclaimer in the
14477f656bSRafal Jaworowski  *    documentation and/or other materials provided with the distribution.
15477f656bSRafal Jaworowski  *
16477f656bSRafal Jaworowski  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17477f656bSRafal Jaworowski  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18477f656bSRafal Jaworowski  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19477f656bSRafal Jaworowski  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20477f656bSRafal Jaworowski  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21477f656bSRafal Jaworowski  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22477f656bSRafal Jaworowski  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23477f656bSRafal Jaworowski  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24477f656bSRafal Jaworowski  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25477f656bSRafal Jaworowski  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26477f656bSRafal Jaworowski  * SUCH DAMAGE.
27477f656bSRafal Jaworowski  */
28477f656bSRafal Jaworowski 
29477f656bSRafal Jaworowski #include <sys/cdefs.h>
309db7da1fSPoul-Henning Kamp #include <assert.h>
3163c8d31eSPoul-Henning Kamp #include <ctype.h>
32477f656bSRafal Jaworowski #include <err.h>
33477f656bSRafal Jaworowski #include <errno.h>
34477f656bSRafal Jaworowski #include <sysexits.h>
35477f656bSRafal Jaworowski #include <fcntl.h>
36477f656bSRafal Jaworowski #include <stdio.h>
37477f656bSRafal Jaworowski #include <stdlib.h>
38477f656bSRafal Jaworowski #include <string.h>
39477f656bSRafal Jaworowski #include <unistd.h>
40b2ae176dSPoul-Henning Kamp #include <sys/endian.h>
41477f656bSRafal Jaworowski #include <sys/ioctl.h>
42477f656bSRafal Jaworowski 
43477f656bSRafal Jaworowski #include <dev/iicbus/iic.h>
44477f656bSRafal Jaworowski 
45477f656bSRafal Jaworowski #define	I2C_DEV			"/dev/iic0"
46477f656bSRafal Jaworowski #define	I2C_MODE_NOTSET		0
47477f656bSRafal Jaworowski #define	I2C_MODE_NONE		1
48477f656bSRafal Jaworowski #define	I2C_MODE_STOP_START	2
49477f656bSRafal Jaworowski #define	I2C_MODE_REPEATED_START	3
50a3055557SIan Lepore #define	I2C_MODE_TRANSFER	4
51477f656bSRafal Jaworowski 
52477f656bSRafal Jaworowski struct options {
53e06874f3SPoul-Henning Kamp 	const char	*width;
54b23362afSPoul-Henning Kamp 	unsigned	count;
55477f656bSRafal Jaworowski 	int		verbose;
56477f656bSRafal Jaworowski 	int		binary;
5763c8d31eSPoul-Henning Kamp 	const char	*skip;
58477f656bSRafal Jaworowski 	int		mode;
59477f656bSRafal Jaworowski 	char		dir;
60477f656bSRafal Jaworowski 	uint32_t	addr;
61477f656bSRafal Jaworowski 	uint32_t	off;
62e06874f3SPoul-Henning Kamp 	uint8_t		off_buf[2];
63e06874f3SPoul-Henning Kamp 	size_t		off_len;
64477f656bSRafal Jaworowski };
65477f656bSRafal Jaworowski 
669c10d00bSPoul-Henning Kamp #define N_FDCACHE 128
679c10d00bSPoul-Henning Kamp static int fd_cache[N_FDCACHE];
689c10d00bSPoul-Henning Kamp 
69477f656bSRafal Jaworowski __dead2 static void
usage(const char * msg)705ab41ff8SPoul-Henning Kamp usage(const char *msg)
71477f656bSRafal Jaworowski {
72477f656bSRafal Jaworowski 
735ab41ff8SPoul-Henning Kamp 	if (msg != NULL)
745ab41ff8SPoul-Henning Kamp 		fprintf(stderr, "%s\n", msg);
75477f656bSRafal Jaworowski 	fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] "
7629c6e6e2SPoul-Henning Kamp 	    "[-w [0|8|16|16LE|16BE]] [-c count] [-m [tr|ss|rs|no]] [-b] [-v]\n",
77477f656bSRafal Jaworowski 	    getprogname());
78477f656bSRafal Jaworowski 	fprintf(stderr, "       %s -s [-f device] [-n skip_addr] -v\n",
79477f656bSRafal Jaworowski 	    getprogname());
80477f656bSRafal Jaworowski 	fprintf(stderr, "       %s -r [-f device] -v\n", getprogname());
81477f656bSRafal Jaworowski 	exit(EX_USAGE);
82477f656bSRafal Jaworowski }
83477f656bSRafal Jaworowski 
84b23362afSPoul-Henning Kamp static int
i2c_do_stop(int fd)85b23362afSPoul-Henning Kamp i2c_do_stop(int fd)
86b23362afSPoul-Henning Kamp {
87b23362afSPoul-Henning Kamp 	int i;
88b23362afSPoul-Henning Kamp 
89b23362afSPoul-Henning Kamp 	i = ioctl(fd, I2CSTOP);
90b23362afSPoul-Henning Kamp 	if (i < 0)
91b23362afSPoul-Henning Kamp 		fprintf(stderr, "ioctl: error sending stop condition: %s\n",
92b23362afSPoul-Henning Kamp 		    strerror(errno));
93b23362afSPoul-Henning Kamp 	return (i);
94b23362afSPoul-Henning Kamp }
95b23362afSPoul-Henning Kamp 
96b23362afSPoul-Henning Kamp static int
i2c_do_start(int fd,struct iiccmd * cmd)97b23362afSPoul-Henning Kamp i2c_do_start(int fd, struct iiccmd *cmd)
98b23362afSPoul-Henning Kamp {
99b23362afSPoul-Henning Kamp 	int i;
100b23362afSPoul-Henning Kamp 
101b23362afSPoul-Henning Kamp 	i = ioctl(fd, I2CSTART, cmd);
102b23362afSPoul-Henning Kamp 	if (i < 0)
103b23362afSPoul-Henning Kamp 		fprintf(stderr, "ioctl: error sending start condition: %s\n",
104b23362afSPoul-Henning Kamp 		    strerror(errno));
105b23362afSPoul-Henning Kamp 	return (i);
106b23362afSPoul-Henning Kamp }
107b23362afSPoul-Henning Kamp 
108b23362afSPoul-Henning Kamp static int
i2c_do_repeatstart(int fd,struct iiccmd * cmd)109b23362afSPoul-Henning Kamp i2c_do_repeatstart(int fd, struct iiccmd *cmd)
110b23362afSPoul-Henning Kamp {
111b23362afSPoul-Henning Kamp 	int i;
112b23362afSPoul-Henning Kamp 
113b23362afSPoul-Henning Kamp 	i = ioctl(fd, I2CRPTSTART, cmd);
114b23362afSPoul-Henning Kamp 	if (i < 0)
115b23362afSPoul-Henning Kamp 		fprintf(stderr, "ioctl: error sending repeated start: %s\n",
116b23362afSPoul-Henning Kamp 		    strerror(errno));
117b23362afSPoul-Henning Kamp 	return (i);
118b23362afSPoul-Henning Kamp }
119b23362afSPoul-Henning Kamp 
120b23362afSPoul-Henning Kamp static int
i2c_do_write(int fd,struct iiccmd * cmd)121b23362afSPoul-Henning Kamp i2c_do_write(int fd, struct iiccmd *cmd)
122b23362afSPoul-Henning Kamp {
123b23362afSPoul-Henning Kamp 	int i;
124b23362afSPoul-Henning Kamp 
125b23362afSPoul-Henning Kamp 	i = ioctl(fd, I2CWRITE, cmd);
126b23362afSPoul-Henning Kamp 	if (i < 0)
127b23362afSPoul-Henning Kamp 		fprintf(stderr, "ioctl: error writing: %s\n",
128b23362afSPoul-Henning Kamp 		    strerror(errno));
129b23362afSPoul-Henning Kamp 	return (i);
130b23362afSPoul-Henning Kamp }
131b23362afSPoul-Henning Kamp 
132b23362afSPoul-Henning Kamp static int
i2c_do_read(int fd,struct iiccmd * cmd)133b23362afSPoul-Henning Kamp i2c_do_read(int fd, struct iiccmd *cmd)
134b23362afSPoul-Henning Kamp {
135b23362afSPoul-Henning Kamp 	int i;
136b23362afSPoul-Henning Kamp 
137b23362afSPoul-Henning Kamp 	i = ioctl(fd, I2CREAD, cmd);
138b23362afSPoul-Henning Kamp 	if (i < 0)
139b23362afSPoul-Henning Kamp 		fprintf(stderr, "ioctl: error reading: %s\n",
140b23362afSPoul-Henning Kamp 		    strerror(errno));
141b23362afSPoul-Henning Kamp 	return (i);
142b23362afSPoul-Henning Kamp }
143b23362afSPoul-Henning Kamp 
144b23362afSPoul-Henning Kamp static int
i2c_do_reset(int fd)145b23362afSPoul-Henning Kamp i2c_do_reset(int fd)
146b23362afSPoul-Henning Kamp {
147b23362afSPoul-Henning Kamp 	struct iiccmd cmd;
148b23362afSPoul-Henning Kamp 	int i;
149b23362afSPoul-Henning Kamp 
150b23362afSPoul-Henning Kamp 	memset(&cmd, 0, sizeof cmd);
151b23362afSPoul-Henning Kamp 	i = ioctl(fd, I2CRSTCARD, &cmd);
152b23362afSPoul-Henning Kamp 	if (i < 0)
153b23362afSPoul-Henning Kamp 		fprintf(stderr, "ioctl: error resetting controller: %s\n",
154b23362afSPoul-Henning Kamp 		    strerror(errno));
155b23362afSPoul-Henning Kamp 	return (i);
156b23362afSPoul-Henning Kamp }
157b23362afSPoul-Henning Kamp 
15863c8d31eSPoul-Henning Kamp static void
parse_skip(const char * skip,char blacklist[128])15963c8d31eSPoul-Henning Kamp parse_skip(const char *skip, char blacklist[128])
160477f656bSRafal Jaworowski {
16163c8d31eSPoul-Henning Kamp 	const char *p;
16263c8d31eSPoul-Henning Kamp 	unsigned x, y;
163477f656bSRafal Jaworowski 
16463c8d31eSPoul-Henning Kamp 	for (p = skip; *p != '\0';) {
16563c8d31eSPoul-Henning Kamp 		if (*p == '0' && p[1] == 'x')
16663c8d31eSPoul-Henning Kamp 			p += 2;
16763c8d31eSPoul-Henning Kamp 		if (!isxdigit(*p))
16863c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, expected (first) hex-digit");
16963c8d31eSPoul-Henning Kamp 		x = digittoint(*p++) << 4;
17063c8d31eSPoul-Henning Kamp 		if (!isxdigit(*p))
17163c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, expected (second) hex-digit");
17263c8d31eSPoul-Henning Kamp 		x |= digittoint(*p++);
17363c8d31eSPoul-Henning Kamp 		if (x == 0 || x > 0x7f)
17463c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, (01..7f)");
17563c8d31eSPoul-Henning Kamp 		if (*p == ':' || *p == ',' || *p == '\0') {
17663c8d31eSPoul-Henning Kamp 			blacklist[x] = 1;
17763c8d31eSPoul-Henning Kamp 			if (*p != '\0')
17863c8d31eSPoul-Henning Kamp 				p++;
17963c8d31eSPoul-Henning Kamp 			continue;
180477f656bSRafal Jaworowski 		}
18163c8d31eSPoul-Henning Kamp 		if (*p == '-') {
18263c8d31eSPoul-Henning Kamp 			p++;
18363c8d31eSPoul-Henning Kamp 		} else if (*p == '.' && p[1] == '.') {
18463c8d31eSPoul-Henning Kamp 			p += 2;
18563c8d31eSPoul-Henning Kamp 		} else {
18663c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, ([:|,|..|-])");
187477f656bSRafal Jaworowski 		}
18863c8d31eSPoul-Henning Kamp 		if (*p == '0' && p[1] == 'x')
18963c8d31eSPoul-Henning Kamp 			p += 2;
19063c8d31eSPoul-Henning Kamp 		if (!isxdigit(*p))
19163c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, expected (first) hex-digit");
19263c8d31eSPoul-Henning Kamp 		y = digittoint(*p++) << 4;
19363c8d31eSPoul-Henning Kamp 		if (!isxdigit(*p))
19463c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, expected (second) hex-digit");
19563c8d31eSPoul-Henning Kamp 		y |= digittoint(*p++);
19663c8d31eSPoul-Henning Kamp 		if (y == 0 || y > 0x7f)
19763c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, (01..7f)");
19863c8d31eSPoul-Henning Kamp 		if (y < x)
19963c8d31eSPoul-Henning Kamp 			usage("Bad -n argument, (end < start)");
20063c8d31eSPoul-Henning Kamp 		for (;x <= y; x++)
20163c8d31eSPoul-Henning Kamp 			blacklist[x] = 1;
20263c8d31eSPoul-Henning Kamp 		if (*p == ':' || *p == ',')
20363c8d31eSPoul-Henning Kamp 			p++;
204477f656bSRafal Jaworowski 	}
205477f656bSRafal Jaworowski }
206477f656bSRafal Jaworowski 
207477f656bSRafal Jaworowski static int
scan_bus(const char * dev,int fd,const char * skip,int verbose)208b23362afSPoul-Henning Kamp scan_bus(const char *dev, int fd, const char *skip, int verbose)
209477f656bSRafal Jaworowski {
2109db7da1fSPoul-Henning Kamp 	struct iiccmd cmd;
211c3c16dceSIan Lepore 	struct iic_msg rdmsg;
212c3c16dceSIan Lepore 	struct iic_rdwr_data rdwrdata;
21363c8d31eSPoul-Henning Kamp 	int error;
21463c8d31eSPoul-Henning Kamp 	unsigned u;
21563c8d31eSPoul-Henning Kamp 	int num_found = 0, use_read_xfer;
216c3c16dceSIan Lepore 	uint8_t rdbyte;
21763c8d31eSPoul-Henning Kamp 	char blacklist[128];
218b23362afSPoul-Henning Kamp 	const char *sep = "";
219477f656bSRafal Jaworowski 
22063c8d31eSPoul-Henning Kamp 	memset(blacklist, 0, sizeof blacklist);
221477f656bSRafal Jaworowski 
22263c8d31eSPoul-Henning Kamp 	if (skip != NULL)
22363c8d31eSPoul-Henning Kamp 		parse_skip(skip, blacklist);
224477f656bSRafal Jaworowski 
22563c8d31eSPoul-Henning Kamp 	for (use_read_xfer = 0; use_read_xfer < 2; use_read_xfer++) {
22663c8d31eSPoul-Henning Kamp 		for (u = 1; u < 127; u++) {
22763c8d31eSPoul-Henning Kamp 			if (blacklist[u])
228477f656bSRafal Jaworowski 				continue;
229477f656bSRafal Jaworowski 
23063c8d31eSPoul-Henning Kamp 			cmd.slave = u << 1;
231477f656bSRafal Jaworowski 			cmd.last = 1;
232477f656bSRafal Jaworowski 			cmd.count = 0;
233b23362afSPoul-Henning Kamp 			if (i2c_do_reset(fd))
23463c8d31eSPoul-Henning Kamp 				return (EX_NOINPUT);
235b23362afSPoul-Henning Kamp 
236c3c16dceSIan Lepore 			if (use_read_xfer) {
237c3c16dceSIan Lepore 				rdmsg.buf = &rdbyte;
238c3c16dceSIan Lepore 				rdmsg.len = 1;
239c3c16dceSIan Lepore 				rdmsg.flags = IIC_M_RD;
24063c8d31eSPoul-Henning Kamp 				rdmsg.slave = u << 1;
241c3c16dceSIan Lepore 				rdwrdata.msgs = &rdmsg;
242c3c16dceSIan Lepore 				rdwrdata.nmsgs = 1;
243c3c16dceSIan Lepore 				error = ioctl(fd, I2CRDWR, &rdwrdata);
244c3c16dceSIan Lepore 			} else {
245477f656bSRafal Jaworowski 				error = ioctl(fd, I2CSTART, &cmd);
24663c8d31eSPoul-Henning Kamp 				if (errno == ENODEV || errno == EOPNOTSUPP)
24763c8d31eSPoul-Henning Kamp 					break; /* Try reads instead */
24838a4732fSPoul-Henning Kamp 				(void)ioctl(fd, I2CSTOP);
249c3c16dceSIan Lepore 			}
250c3c16dceSIan Lepore 			if (error == 0) {
251b23362afSPoul-Henning Kamp 				if (!num_found++ && verbose) {
252b23362afSPoul-Henning Kamp 					fprintf(stderr,
253b23362afSPoul-Henning Kamp 					    "Scanning I2C devices on %s:\n",
254b23362afSPoul-Henning Kamp 					    dev);
255b23362afSPoul-Henning Kamp 				}
256b23362afSPoul-Henning Kamp 				printf("%s%02x", sep, u);
257b23362afSPoul-Henning Kamp 				sep = " ";
258c3c16dceSIan Lepore 			}
259c3c16dceSIan Lepore 		}
26063c8d31eSPoul-Henning Kamp 		if (num_found > 0)
26163c8d31eSPoul-Henning Kamp 			break;
262b23362afSPoul-Henning Kamp 		if (verbose && !use_read_xfer)
26363c8d31eSPoul-Henning Kamp 			fprintf(stderr,
26463c8d31eSPoul-Henning Kamp 			    "Hardware may not support START/STOP scanning; "
26563c8d31eSPoul-Henning Kamp 			    "trying less-reliable read method.\n");
266c3c16dceSIan Lepore 	}
267b23362afSPoul-Henning Kamp 	if (num_found == 0 && verbose)
268b23362afSPoul-Henning Kamp 		printf("<Nothing Found>");
26963c8d31eSPoul-Henning Kamp 
270477f656bSRafal Jaworowski 	printf("\n");
271477f656bSRafal Jaworowski 
272b23362afSPoul-Henning Kamp 	return (i2c_do_reset(fd));
273477f656bSRafal Jaworowski }
274477f656bSRafal Jaworowski 
275477f656bSRafal Jaworowski static int
reset_bus(const char * dev,int fd,int verbose)276b23362afSPoul-Henning Kamp reset_bus(const char *dev, int fd, int verbose)
277477f656bSRafal Jaworowski {
278477f656bSRafal Jaworowski 
279b23362afSPoul-Henning Kamp 	if (verbose)
280b23362afSPoul-Henning Kamp 		fprintf(stderr, "Resetting I2C controller on %s\n", dev);
281b23362afSPoul-Henning Kamp 	return (i2c_do_reset(fd));
282477f656bSRafal Jaworowski }
283477f656bSRafal Jaworowski 
284e06874f3SPoul-Henning Kamp static const char *
encode_offset(const char * width,unsigned offset,uint8_t * dst,size_t * len)285f4583ebaSPoul-Henning Kamp encode_offset(const char *width, unsigned offset, uint8_t *dst, size_t *len)
286477f656bSRafal Jaworowski {
287477f656bSRafal Jaworowski 
288e06874f3SPoul-Henning Kamp 	if (!strcmp(width, "0")) {
289e06874f3SPoul-Henning Kamp 		*len = 0;
290e06874f3SPoul-Henning Kamp 		return (NULL);
291e06874f3SPoul-Henning Kamp 	}
292e06874f3SPoul-Henning Kamp 	if (!strcmp(width, "8")) {
293f4583ebaSPoul-Henning Kamp 		if (offset > 0xff)
294f4583ebaSPoul-Henning Kamp 			return ("Invalid 8-bit offset\n");
295f4583ebaSPoul-Henning Kamp 		*dst = offset;
296e06874f3SPoul-Henning Kamp 		*len = 1;
297e06874f3SPoul-Henning Kamp 		return (NULL);
298e06874f3SPoul-Henning Kamp 	}
299f4583ebaSPoul-Henning Kamp 	if (offset > 0xffff)
300f4583ebaSPoul-Henning Kamp 		return ("Invalid 16-bit offset\n");
301e06874f3SPoul-Henning Kamp 	if (!strcmp(width, "16LE") || !strcmp(width, "16")) {
302f4583ebaSPoul-Henning Kamp 		le16enc(dst, offset);
303e06874f3SPoul-Henning Kamp 		*len = 2;
304e06874f3SPoul-Henning Kamp 		return (NULL);
305e06874f3SPoul-Henning Kamp 	}
306e06874f3SPoul-Henning Kamp 	if (!strcmp(width, "16BE")) {
307f4583ebaSPoul-Henning Kamp 		be16enc(dst, offset);
308e06874f3SPoul-Henning Kamp 		*len = 2;
309e06874f3SPoul-Henning Kamp 		return (NULL);
310e06874f3SPoul-Henning Kamp 	}
311e06874f3SPoul-Henning Kamp 	return ("Invalid offset width, must be: 0|8|16|16LE|16BE\n");
312477f656bSRafal Jaworowski }
313477f656bSRafal Jaworowski 
314b23362afSPoul-Henning Kamp static int
write_offset(int fd,struct options i2c_opt,struct iiccmd * cmd)315e06874f3SPoul-Henning Kamp write_offset(int fd, struct options i2c_opt, struct iiccmd *cmd)
316e06874f3SPoul-Henning Kamp {
317e06874f3SPoul-Henning Kamp 
318e06874f3SPoul-Henning Kamp 	if (i2c_opt.off_len > 0) {
319e06874f3SPoul-Henning Kamp 		cmd->count = i2c_opt.off_len;
3207183d96eSPoul-Henning Kamp 		cmd->buf = (void*)i2c_opt.off_buf;
321b23362afSPoul-Henning Kamp 		return (i2c_do_write(fd, cmd));
322e06874f3SPoul-Henning Kamp 	}
323b23362afSPoul-Henning Kamp 	return (0);
324477f656bSRafal Jaworowski }
325477f656bSRafal Jaworowski 
326477f656bSRafal Jaworowski static int
i2c_write(int fd,struct options i2c_opt,uint8_t * i2c_buf)327b23362afSPoul-Henning Kamp i2c_write(int fd, struct options i2c_opt, uint8_t *i2c_buf)
328477f656bSRafal Jaworowski {
329477f656bSRafal Jaworowski 	struct iiccmd cmd;
330b23362afSPoul-Henning Kamp 	char buf[i2c_opt.off_len + i2c_opt.count];
331477f656bSRafal Jaworowski 
332b23362afSPoul-Henning Kamp 	memset(&cmd, 0, sizeof(cmd));
333477f656bSRafal Jaworowski 	cmd.slave = i2c_opt.addr;
334b23362afSPoul-Henning Kamp 
335b23362afSPoul-Henning Kamp 	if (i2c_do_start(fd, &cmd))
336b23362afSPoul-Henning Kamp 		return (i2c_do_stop(fd) | 1);
337477f656bSRafal Jaworowski 
33858172d8cSZbigniew Bodek 	switch(i2c_opt.mode) {
33958172d8cSZbigniew Bodek 	case I2C_MODE_STOP_START:
340b23362afSPoul-Henning Kamp 		if (write_offset(fd, i2c_opt, &cmd))
341b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
342477f656bSRafal Jaworowski 
343b23362afSPoul-Henning Kamp 		if (i2c_do_stop(fd))
344b23362afSPoul-Henning Kamp 			return (1);
345b23362afSPoul-Henning Kamp 
346b23362afSPoul-Henning Kamp 		if (i2c_do_start(fd, &cmd))
347b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
348477f656bSRafal Jaworowski 
349477f656bSRafal Jaworowski 		/*
350477f656bSRafal Jaworowski 		 * Write the data
351477f656bSRafal Jaworowski 		 */
352477f656bSRafal Jaworowski 		cmd.count = i2c_opt.count;
353b23362afSPoul-Henning Kamp 		cmd.buf = (void*)i2c_buf;
354477f656bSRafal Jaworowski 		cmd.last = 0;
355b23362afSPoul-Henning Kamp 		if (i2c_do_write(fd, &cmd))
356b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
35758172d8cSZbigniew Bodek 		break;
35858172d8cSZbigniew Bodek 
35958172d8cSZbigniew Bodek 	case I2C_MODE_REPEATED_START:
360b23362afSPoul-Henning Kamp 		if (write_offset(fd, i2c_opt, &cmd))
361b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
36258172d8cSZbigniew Bodek 
363b23362afSPoul-Henning Kamp 		if (i2c_do_repeatstart(fd, &cmd))
364b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
36558172d8cSZbigniew Bodek 
36658172d8cSZbigniew Bodek 		/*
36758172d8cSZbigniew Bodek 		 * Write the data
36858172d8cSZbigniew Bodek 		 */
36958172d8cSZbigniew Bodek 		cmd.count = i2c_opt.count;
370b23362afSPoul-Henning Kamp 		cmd.buf = (void*)i2c_buf;
37158172d8cSZbigniew Bodek 		cmd.last = 0;
372b23362afSPoul-Henning Kamp 		if (i2c_do_write(fd, &cmd))
373b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
37458172d8cSZbigniew Bodek 		break;
37558172d8cSZbigniew Bodek 
37658172d8cSZbigniew Bodek 	case I2C_MODE_NONE: /* fall through */
37758172d8cSZbigniew Bodek 	default:
378e06874f3SPoul-Henning Kamp 		memcpy(buf, i2c_opt.off_buf, i2c_opt.off_len);
379e06874f3SPoul-Henning Kamp 		memcpy(buf + i2c_opt.off_len, i2c_buf, i2c_opt.count);
38058172d8cSZbigniew Bodek 		/*
38158172d8cSZbigniew Bodek 		 * Write offset and data
38258172d8cSZbigniew Bodek 		 */
383e06874f3SPoul-Henning Kamp 		cmd.count = i2c_opt.off_len + i2c_opt.count;
384b23362afSPoul-Henning Kamp 		cmd.buf = (void*)buf;
38558172d8cSZbigniew Bodek 		cmd.last = 0;
386b23362afSPoul-Henning Kamp 		if (i2c_do_write(fd, &cmd))
387b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
38858172d8cSZbigniew Bodek 		break;
38958172d8cSZbigniew Bodek 	}
390477f656bSRafal Jaworowski 
391b23362afSPoul-Henning Kamp 	return (i2c_do_stop(fd));
392477f656bSRafal Jaworowski }
393477f656bSRafal Jaworowski 
394477f656bSRafal Jaworowski static int
i2c_read(int fd,struct options i2c_opt,uint8_t * i2c_buf)395b23362afSPoul-Henning Kamp i2c_read(int fd, struct options i2c_opt, uint8_t *i2c_buf)
396477f656bSRafal Jaworowski {
397477f656bSRafal Jaworowski 	struct iiccmd cmd;
398e06874f3SPoul-Henning Kamp 	char data = 0;
399477f656bSRafal Jaworowski 
400b23362afSPoul-Henning Kamp 	memset(&cmd, 0, sizeof(cmd));
401b23362afSPoul-Henning Kamp 	cmd.slave = i2c_opt.addr;
402477f656bSRafal Jaworowski 
403e06874f3SPoul-Henning Kamp 	if (i2c_opt.off_len) {
404477f656bSRafal Jaworowski 		cmd.count = 1;
405477f656bSRafal Jaworowski 		cmd.last = 0;
406477f656bSRafal Jaworowski 		cmd.buf = &data;
407b23362afSPoul-Henning Kamp 		if (i2c_do_start(fd, &cmd))
408b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
409477f656bSRafal Jaworowski 
410b23362afSPoul-Henning Kamp 		if (write_offset(fd, i2c_opt, &cmd))
411b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
412477f656bSRafal Jaworowski 
413b23362afSPoul-Henning Kamp 		if (i2c_opt.mode == I2C_MODE_STOP_START && i2c_do_stop(fd))
414b23362afSPoul-Henning Kamp 			return (1);
41582a2ce40SLuiz Otavio O Souza 	}
416668f9cbeSAndriy Gapon 	cmd.slave = i2c_opt.addr | 1;
417477f656bSRafal Jaworowski 	cmd.count = 1;
418477f656bSRafal Jaworowski 	cmd.last = 0;
419477f656bSRafal Jaworowski 	cmd.buf = &data;
420e06874f3SPoul-Henning Kamp 	if (i2c_opt.mode == I2C_MODE_STOP_START || i2c_opt.off_len == 0) {
421b23362afSPoul-Henning Kamp 		if (i2c_do_start(fd, &cmd))
422b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
423477f656bSRafal Jaworowski 	} else if (i2c_opt.mode == I2C_MODE_REPEATED_START) {
424b23362afSPoul-Henning Kamp 		if (i2c_do_repeatstart(fd, &cmd))
425b23362afSPoul-Henning Kamp 			return (i2c_do_stop(fd) | 1);
426477f656bSRafal Jaworowski 	}
427477f656bSRafal Jaworowski 
428668f9cbeSAndriy Gapon 	cmd.count = i2c_opt.count;
429b23362afSPoul-Henning Kamp 	cmd.buf = (void*)i2c_buf;
430668f9cbeSAndriy Gapon 	cmd.last = 1;
431b23362afSPoul-Henning Kamp 	if (i2c_do_read(fd, &cmd))
432b23362afSPoul-Henning Kamp 		return (i2c_do_stop(fd) | 1);
433668f9cbeSAndriy Gapon 
434b23362afSPoul-Henning Kamp 	return (i2c_do_stop(fd));
435477f656bSRafal Jaworowski }
436477f656bSRafal Jaworowski 
437a3055557SIan Lepore /*
438a3055557SIan Lepore  * i2c_rdwr_transfer() - use I2CRDWR to conduct a complete i2c transfer.
439a3055557SIan Lepore  *
440a3055557SIan Lepore  * Some i2c hardware is unable to provide direct control over START, REPEAT-
441a3055557SIan Lepore  * START, and STOP operations.  Such hardware can only perform a complete
442a3055557SIan Lepore  * START-<data>-STOP or START-<data>-REPEAT-START-<data>-STOP sequence as a
443a3055557SIan Lepore  * single operation.  The driver framework refers to this sequence as a
444a3055557SIan Lepore  * "transfer" so we call it "transfer mode".  We assemble either one or two
445a3055557SIan Lepore  * iic_msg structures to describe the IO operations, and hand them off to the
446a3055557SIan Lepore  * driver to be handled as a single transfer.
447a3055557SIan Lepore  */
448a3055557SIan Lepore static int
i2c_rdwr_transfer(int fd,struct options i2c_opt,uint8_t * i2c_buf)449b23362afSPoul-Henning Kamp i2c_rdwr_transfer(int fd, struct options i2c_opt, uint8_t *i2c_buf)
450a3055557SIan Lepore {
451e06874f3SPoul-Henning Kamp 	struct iic_msg msgs[2], *msgp = msgs;
452a3055557SIan Lepore 	struct iic_rdwr_data xfer;
453e06874f3SPoul-Henning Kamp 	int flag = 0;
454a3055557SIan Lepore 
455e06874f3SPoul-Henning Kamp 	if (i2c_opt.off_len) {
456e06874f3SPoul-Henning Kamp 		msgp->flags = IIC_M_WR | IIC_M_NOSTOP;
457e06874f3SPoul-Henning Kamp 		msgp->slave = i2c_opt.addr;
458e06874f3SPoul-Henning Kamp 		msgp->buf   = i2c_opt.off_buf;
459e06874f3SPoul-Henning Kamp 		msgp->len   = i2c_opt.off_len;
460e06874f3SPoul-Henning Kamp 		msgp++;
461e06874f3SPoul-Henning Kamp 		flag = IIC_M_NOSTART;
462a3055557SIan Lepore 	}
463a3055557SIan Lepore 
464a3055557SIan Lepore 	/*
465a3055557SIan Lepore 	 * If the transfer direction is write and we did a write of the offset
466a3055557SIan Lepore 	 * above, then we need to elide the start; this transfer is just more
467a3055557SIan Lepore 	 * writing that follows the one started above.  For a read, we always do
468a3055557SIan Lepore 	 * a start; if we did an offset write above it'll be a repeat-start
469a3055557SIan Lepore 	 * because of the NOSTOP flag used above.
470a3055557SIan Lepore 	 */
471a3055557SIan Lepore 	if (i2c_opt.dir == 'w')
472e06874f3SPoul-Henning Kamp 		msgp->flags = IIC_M_WR | flag;
473a3055557SIan Lepore 	else
474e06874f3SPoul-Henning Kamp 		msgp->flags = IIC_M_RD;
475e06874f3SPoul-Henning Kamp 	msgp->slave = i2c_opt.addr;
476e06874f3SPoul-Henning Kamp 	msgp->len   = i2c_opt.count;
477e06874f3SPoul-Henning Kamp 	msgp->buf   = i2c_buf;
478e06874f3SPoul-Henning Kamp 	msgp++;
479a3055557SIan Lepore 
480a3055557SIan Lepore 	xfer.msgs = msgs;
481e06874f3SPoul-Henning Kamp 	xfer.nmsgs = msgp - msgs;
482a3055557SIan Lepore 
483a3055557SIan Lepore 	if (ioctl(fd, I2CRDWR, &xfer) == -1 )
484a3055557SIan Lepore 		err(1, "ioctl(I2CRDWR) failed");
485a3055557SIan Lepore 
486a3055557SIan Lepore 	return (0);
487a3055557SIan Lepore }
488a3055557SIan Lepore 
4895ab41ff8SPoul-Henning Kamp static int
access_bus(int fd,struct options i2c_opt)4905ab41ff8SPoul-Henning Kamp access_bus(int fd, struct options i2c_opt)
491477f656bSRafal Jaworowski {
492b23362afSPoul-Henning Kamp 	uint8_t i2c_buf[i2c_opt.count];
493b23362afSPoul-Henning Kamp 	int error;
494b23362afSPoul-Henning Kamp 	unsigned u, chunk_size = 16;
495477f656bSRafal Jaworowski 
496a3055557SIan Lepore 	/*
497a3055557SIan Lepore 	 * For a write, read the data to be written to the chip from stdin.
498a3055557SIan Lepore 	 */
499477f656bSRafal Jaworowski 	if (i2c_opt.dir == 'w') {
500a3055557SIan Lepore 		if (i2c_opt.verbose && !i2c_opt.binary)
501b23362afSPoul-Henning Kamp 			fprintf(stderr, "Enter %u bytes of data: ",
502a3055557SIan Lepore 			    i2c_opt.count);
503b23362afSPoul-Henning Kamp 		if (fread(i2c_buf, 1, i2c_opt.count, stdin) != i2c_opt.count)
504a3055557SIan Lepore 			err(1, "not enough data, exiting\n");
505a3055557SIan Lepore 	}
506a3055557SIan Lepore 
507a3055557SIan Lepore 	if (i2c_opt.mode == I2C_MODE_TRANSFER)
50838a4732fSPoul-Henning Kamp 		error = i2c_rdwr_transfer(fd, i2c_opt, i2c_buf);
509a3055557SIan Lepore 	else if (i2c_opt.dir == 'w')
51038a4732fSPoul-Henning Kamp 		error = i2c_write(fd, i2c_opt, i2c_buf);
511a3055557SIan Lepore 	else
51238a4732fSPoul-Henning Kamp 		error = i2c_read(fd, i2c_opt, i2c_buf);
513a3055557SIan Lepore 
5145ab41ff8SPoul-Henning Kamp 	if (error == 0) {
515477f656bSRafal Jaworowski 		if (i2c_opt.dir == 'r' && i2c_opt.binary) {
5165ab41ff8SPoul-Henning Kamp 			(void)fwrite(i2c_buf, 1, i2c_opt.count, stdout);
5175ab41ff8SPoul-Henning Kamp 		} else if (i2c_opt.verbose || i2c_opt.dir == 'r') {
5185ab41ff8SPoul-Henning Kamp 			if (i2c_opt.verbose)
5195ab41ff8SPoul-Henning Kamp 				fprintf(stderr, "\nData %s (hex):\n",
5205ab41ff8SPoul-Henning Kamp 				    i2c_opt.dir == 'r' ?  "read" : "written");
5215ab41ff8SPoul-Henning Kamp 
522b23362afSPoul-Henning Kamp 			for (u = 0; u < i2c_opt.count; u++) {
523b23362afSPoul-Henning Kamp 				printf("%02hhx ", i2c_buf[u]);
524b23362afSPoul-Henning Kamp 				if ((u % chunk_size) == chunk_size - 1)
525b23362afSPoul-Henning Kamp 					printf("\n");
526477f656bSRafal Jaworowski 			}
527b23362afSPoul-Henning Kamp 			if ((u % chunk_size) != 0)
528b23362afSPoul-Henning Kamp 				printf("\n");
5295ab41ff8SPoul-Henning Kamp 		}
5305ab41ff8SPoul-Henning Kamp 	}
531477f656bSRafal Jaworowski 
5325ab41ff8SPoul-Henning Kamp 	return (error);
5335ab41ff8SPoul-Henning Kamp }
5345ab41ff8SPoul-Henning Kamp 
5359c10d00bSPoul-Henning Kamp static const char *widths[] = {
5369c10d00bSPoul-Henning Kamp 	"0",
5379c10d00bSPoul-Henning Kamp 	"8",
5389c10d00bSPoul-Henning Kamp 	"16LE",
5399c10d00bSPoul-Henning Kamp 	"16BE",
5409c10d00bSPoul-Henning Kamp 	"16",
5419c10d00bSPoul-Henning Kamp 	NULL,
5429c10d00bSPoul-Henning Kamp };
5439c10d00bSPoul-Henning Kamp 
5449c10d00bSPoul-Henning Kamp static int
command_bus(struct options i2c_opt,char * cmd)5459c10d00bSPoul-Henning Kamp command_bus(struct options i2c_opt, char *cmd)
5469c10d00bSPoul-Henning Kamp {
5479c10d00bSPoul-Henning Kamp 	int error, fd;
5489c10d00bSPoul-Henning Kamp 	char devbuf[64];
5499c10d00bSPoul-Henning Kamp 	uint8_t dbuf[BUFSIZ];
5509c10d00bSPoul-Henning Kamp 	unsigned bus;
5519c10d00bSPoul-Henning Kamp 	const char *width = NULL;
5529c10d00bSPoul-Henning Kamp 	const char *err_msg;
5539c10d00bSPoul-Henning Kamp 	unsigned offset;
5549c10d00bSPoul-Henning Kamp 	unsigned u;
5559c10d00bSPoul-Henning Kamp 	size_t length;
5569c10d00bSPoul-Henning Kamp 
5579c10d00bSPoul-Henning Kamp 	while (isspace(*cmd))
5589c10d00bSPoul-Henning Kamp 		cmd++;
5599c10d00bSPoul-Henning Kamp 
5609c10d00bSPoul-Henning Kamp 	switch(*cmd) {
5619c10d00bSPoul-Henning Kamp 	case 0:
5629c10d00bSPoul-Henning Kamp 	case '#':
5639c10d00bSPoul-Henning Kamp 		return (0);
5649c10d00bSPoul-Henning Kamp 	case 'p':
5659c10d00bSPoul-Henning Kamp 	case 'P':
5669c10d00bSPoul-Henning Kamp 		printf("%s", cmd);
5679c10d00bSPoul-Henning Kamp 		return (0);
5689c10d00bSPoul-Henning Kamp 	case 'r':
5699c10d00bSPoul-Henning Kamp 	case 'R':
5709c10d00bSPoul-Henning Kamp 		i2c_opt.dir = 'r';
5719c10d00bSPoul-Henning Kamp 		break;
5729c10d00bSPoul-Henning Kamp 	case 'w':
5739c10d00bSPoul-Henning Kamp 	case 'W':
5749c10d00bSPoul-Henning Kamp 		i2c_opt.dir = 'w';
5759c10d00bSPoul-Henning Kamp 		break;
5769c10d00bSPoul-Henning Kamp 	default:
5779c10d00bSPoul-Henning Kamp 		fprintf(stderr,
5789c10d00bSPoul-Henning Kamp 		    "Did not understand command: 0x%02x ", *cmd);
5799c10d00bSPoul-Henning Kamp 		if (isgraph(*cmd))
5809c10d00bSPoul-Henning Kamp 			fprintf(stderr, "'%c'", *cmd);
5819c10d00bSPoul-Henning Kamp 		fprintf(stderr, "\n");
5829c10d00bSPoul-Henning Kamp 		return(-1);
5839c10d00bSPoul-Henning Kamp 	}
5849c10d00bSPoul-Henning Kamp 	cmd++;
5859c10d00bSPoul-Henning Kamp 
5869c10d00bSPoul-Henning Kamp 	bus = strtoul(cmd, &cmd, 0);
5879c10d00bSPoul-Henning Kamp 	if (bus == 0 && errno == EINVAL) {
5889c10d00bSPoul-Henning Kamp 		fprintf(stderr, "Could not translate bus number\n");
5899c10d00bSPoul-Henning Kamp 		return(-1);
5909c10d00bSPoul-Henning Kamp 	}
5919c10d00bSPoul-Henning Kamp 
5929c10d00bSPoul-Henning Kamp 	i2c_opt.addr = strtoul(cmd, &cmd, 0);
5939c10d00bSPoul-Henning Kamp 	if (i2c_opt.addr == 0 && errno == EINVAL) {
5949c10d00bSPoul-Henning Kamp 		fprintf(stderr, "Could not translate device\n");
5959c10d00bSPoul-Henning Kamp 		return(-1);
5969c10d00bSPoul-Henning Kamp 	}
5979c10d00bSPoul-Henning Kamp 	if (i2c_opt.addr < 1 || i2c_opt.addr > 0x7f) {
5989c10d00bSPoul-Henning Kamp 		fprintf(stderr, "Invalid device (0x%x)\n", i2c_opt.addr);
5999c10d00bSPoul-Henning Kamp 		return(-1);
6009c10d00bSPoul-Henning Kamp 	}
6019c10d00bSPoul-Henning Kamp 	i2c_opt.addr <<= 1;
6029c10d00bSPoul-Henning Kamp 
6039c10d00bSPoul-Henning Kamp 	while(isspace(*cmd))
6049c10d00bSPoul-Henning Kamp 		cmd++;
6059c10d00bSPoul-Henning Kamp 
6069c10d00bSPoul-Henning Kamp 	for(u = 0; widths[u]; u++) {
6079c10d00bSPoul-Henning Kamp 		length = strlen(widths[u]);
6089c10d00bSPoul-Henning Kamp 		if (memcmp(cmd, widths[u], length))
6099c10d00bSPoul-Henning Kamp 			continue;
6109c10d00bSPoul-Henning Kamp 		if (!isspace(cmd[length]))
6119c10d00bSPoul-Henning Kamp 			continue;
6129c10d00bSPoul-Henning Kamp 		width = widths[u];
6139c10d00bSPoul-Henning Kamp 		cmd += length;
6149c10d00bSPoul-Henning Kamp 		break;
6159c10d00bSPoul-Henning Kamp 	}
6169c10d00bSPoul-Henning Kamp 	if (width == NULL) {
6179c10d00bSPoul-Henning Kamp 		fprintf(stderr, "Invalid width\n");
6189c10d00bSPoul-Henning Kamp 		return(-1);
6199c10d00bSPoul-Henning Kamp 	}
6209c10d00bSPoul-Henning Kamp 
6219c10d00bSPoul-Henning Kamp 	offset = strtoul(cmd, &cmd, 0);
6229c10d00bSPoul-Henning Kamp 	if (offset == 0 && errno == EINVAL) {
6239c10d00bSPoul-Henning Kamp 		fprintf(stderr, "Could not translate offset\n");
6249c10d00bSPoul-Henning Kamp 		return(-1);
6259c10d00bSPoul-Henning Kamp 	}
6269c10d00bSPoul-Henning Kamp 
6279c10d00bSPoul-Henning Kamp 	err_msg = encode_offset(width, offset,
6289c10d00bSPoul-Henning Kamp 	    i2c_opt.off_buf, &i2c_opt.off_len);
6299c10d00bSPoul-Henning Kamp 	if (err_msg) {
6309c10d00bSPoul-Henning Kamp 		fprintf(stderr, "%s", err_msg);
6319c10d00bSPoul-Henning Kamp 		return(-1);
6329c10d00bSPoul-Henning Kamp 	}
6339c10d00bSPoul-Henning Kamp 
6349c10d00bSPoul-Henning Kamp 	if (i2c_opt.dir == 'r') {
6359c10d00bSPoul-Henning Kamp 		i2c_opt.count = strtoul(cmd, &cmd, 0);
6369c10d00bSPoul-Henning Kamp 		if (i2c_opt.count == 0 && errno == EINVAL) {
6379c10d00bSPoul-Henning Kamp 			fprintf(stderr, "Could not translate length\n");
6389c10d00bSPoul-Henning Kamp 			return(-1);
6399c10d00bSPoul-Henning Kamp 		}
6409c10d00bSPoul-Henning Kamp 	} else {
6419c10d00bSPoul-Henning Kamp 		i2c_opt.count = 0;
6429c10d00bSPoul-Henning Kamp 		while (1) {
6439c10d00bSPoul-Henning Kamp 			while(isspace(*cmd))
6449c10d00bSPoul-Henning Kamp 				cmd++;
6459c10d00bSPoul-Henning Kamp 			if (!*cmd)
6469c10d00bSPoul-Henning Kamp 				break;
6479c10d00bSPoul-Henning Kamp 			if (!isxdigit(*cmd)) {
6489c10d00bSPoul-Henning Kamp 				fprintf(stderr, "Not a hex digit.\n");
6499c10d00bSPoul-Henning Kamp 				return(-1);
6509c10d00bSPoul-Henning Kamp 			}
6519c10d00bSPoul-Henning Kamp 			dbuf[i2c_opt.count] = digittoint(*cmd++) << 4;
6529c10d00bSPoul-Henning Kamp 			while(isspace(*cmd))
6539c10d00bSPoul-Henning Kamp 				cmd++;
6549c10d00bSPoul-Henning Kamp 			if (!*cmd) {
6559c10d00bSPoul-Henning Kamp 				fprintf(stderr,
6569c10d00bSPoul-Henning Kamp 				    "Uneven number of hex digits.\n");
6579c10d00bSPoul-Henning Kamp 				return(-1);
6589c10d00bSPoul-Henning Kamp 			}
6599c10d00bSPoul-Henning Kamp 			if (!isxdigit(*cmd)) {
6609c10d00bSPoul-Henning Kamp 				fprintf(stderr, "Not a hex digit.\n");
6619c10d00bSPoul-Henning Kamp 				return(-1);
6629c10d00bSPoul-Henning Kamp 			}
6639c10d00bSPoul-Henning Kamp 			dbuf[i2c_opt.count++] |= digittoint(*cmd++);
6649c10d00bSPoul-Henning Kamp 		}
6659c10d00bSPoul-Henning Kamp 	}
6669c10d00bSPoul-Henning Kamp 	assert(bus < N_FDCACHE);
6679c10d00bSPoul-Henning Kamp 	fd = fd_cache[bus];
6689c10d00bSPoul-Henning Kamp 	if (fd < 0) {
6699c10d00bSPoul-Henning Kamp 		(void)sprintf(devbuf, "/dev/iic%u", bus);
6709c10d00bSPoul-Henning Kamp 		fd = open(devbuf, O_RDWR);
6719c10d00bSPoul-Henning Kamp 		if (fd == -1) {
6729c10d00bSPoul-Henning Kamp 			fprintf(stderr, "Error opening I2C controller (%s): %s\n",
6739c10d00bSPoul-Henning Kamp 			    devbuf, strerror(errno));
6749c10d00bSPoul-Henning Kamp 			return (EX_NOINPUT);
6759c10d00bSPoul-Henning Kamp 		}
6769c10d00bSPoul-Henning Kamp 		fd_cache[bus] = fd;
6779c10d00bSPoul-Henning Kamp 	}
6789c10d00bSPoul-Henning Kamp 
6799c10d00bSPoul-Henning Kamp 	error = i2c_rdwr_transfer(fd, i2c_opt, dbuf);
6809c10d00bSPoul-Henning Kamp 	if (error)
6819c10d00bSPoul-Henning Kamp 		return(-1);
6829c10d00bSPoul-Henning Kamp 
6839c10d00bSPoul-Henning Kamp 	if (i2c_opt.dir == 'r') {
6849c10d00bSPoul-Henning Kamp 		for (u = 0; u < i2c_opt.count; u++)
6859c10d00bSPoul-Henning Kamp 			printf("%02x", dbuf[u]);
6869c10d00bSPoul-Henning Kamp 		printf("\n");
6879c10d00bSPoul-Henning Kamp 	}
6889c10d00bSPoul-Henning Kamp 	return (0);
6899c10d00bSPoul-Henning Kamp }
6909c10d00bSPoul-Henning Kamp 
6919c10d00bSPoul-Henning Kamp static int
exec_bus(struct options i2c_opt,char * cmd)6929c10d00bSPoul-Henning Kamp exec_bus(struct options i2c_opt, char *cmd)
6939c10d00bSPoul-Henning Kamp {
6949c10d00bSPoul-Henning Kamp 	int error;
6959c10d00bSPoul-Henning Kamp 
6969c10d00bSPoul-Henning Kamp 	while (isspace(*cmd))
6979c10d00bSPoul-Henning Kamp 		cmd++;
6989c10d00bSPoul-Henning Kamp 	if (*cmd == '#' || *cmd == '\0')
6999c10d00bSPoul-Henning Kamp 		return (0);
7009c10d00bSPoul-Henning Kamp 	error = command_bus(i2c_opt, cmd);
7019c10d00bSPoul-Henning Kamp 	if (i2c_opt.verbose) {
7029c10d00bSPoul-Henning Kamp 		(void)fflush(stderr);
7039c10d00bSPoul-Henning Kamp 		printf(error ? "ERROR\n" : "OK\n");
7049c10d00bSPoul-Henning Kamp 		error = 0;
7059c10d00bSPoul-Henning Kamp 	} else if (error) {
7069c10d00bSPoul-Henning Kamp 		fprintf(stderr, "  in: %s", cmd);
7079c10d00bSPoul-Henning Kamp 	}
7089c10d00bSPoul-Henning Kamp 	(void)fflush(stdout);
7099c10d00bSPoul-Henning Kamp 	return (error);
7109c10d00bSPoul-Henning Kamp }
7119c10d00bSPoul-Henning Kamp 
7129c10d00bSPoul-Henning Kamp static int
instruct_bus(struct options i2c_opt,int argc,char ** argv)7139c10d00bSPoul-Henning Kamp instruct_bus(struct options i2c_opt, int argc, char **argv)
7149c10d00bSPoul-Henning Kamp {
7159c10d00bSPoul-Henning Kamp 	char buf[BUFSIZ];
7169c10d00bSPoul-Henning Kamp 	int rd_cmds = (argc == 0);
7179c10d00bSPoul-Henning Kamp 	int error;
7189c10d00bSPoul-Henning Kamp 
7199c10d00bSPoul-Henning Kamp 	while (argc-- > 0) {
7209c10d00bSPoul-Henning Kamp 		if (argc == 0 && !strcmp(*argv, "-")) {
7219c10d00bSPoul-Henning Kamp 			rd_cmds = 1;
7229c10d00bSPoul-Henning Kamp 		} else {
7239c10d00bSPoul-Henning Kamp 			error = exec_bus(i2c_opt, *argv);
7249c10d00bSPoul-Henning Kamp 			if (error)
7259c10d00bSPoul-Henning Kamp 				return (error);
7269c10d00bSPoul-Henning Kamp 		}
7279c10d00bSPoul-Henning Kamp 		argv++;
7289c10d00bSPoul-Henning Kamp 	}
7299c10d00bSPoul-Henning Kamp 	if (!rd_cmds)
7309c10d00bSPoul-Henning Kamp 		return (0);
7319c10d00bSPoul-Henning Kamp 	while (fgets(buf, sizeof buf, stdin) != NULL) {
7329c10d00bSPoul-Henning Kamp 		error = exec_bus(i2c_opt, buf);
7339c10d00bSPoul-Henning Kamp 		if (error)
7349c10d00bSPoul-Henning Kamp 			return (error);
7359c10d00bSPoul-Henning Kamp 	}
7369c10d00bSPoul-Henning Kamp 	return (0);
7379c10d00bSPoul-Henning Kamp }
7389c10d00bSPoul-Henning Kamp 
7395ab41ff8SPoul-Henning Kamp int
main(int argc,char ** argv)7405ab41ff8SPoul-Henning Kamp main(int argc, char** argv)
7415ab41ff8SPoul-Henning Kamp {
7425ab41ff8SPoul-Henning Kamp 	struct options i2c_opt;
7435ab41ff8SPoul-Henning Kamp 	const char *dev, *err_msg;
7447183d96eSPoul-Henning Kamp 	int fd, error = 0, ch;
74567bceb38SMartin Birgmeier 	const char *optflags = "a:f:d:o:iw:c:m:n:sbvrh";
7467183d96eSPoul-Henning Kamp 	char do_what = 0;
7475ab41ff8SPoul-Henning Kamp 
7485ab41ff8SPoul-Henning Kamp 	dev = I2C_DEV;
7499c10d00bSPoul-Henning Kamp 	for (ch = 0; ch < N_FDCACHE; ch++)
7509c10d00bSPoul-Henning Kamp 		fd_cache[ch] = -1;
7515ab41ff8SPoul-Henning Kamp 
7525ab41ff8SPoul-Henning Kamp 	/* Default values */
7535ab41ff8SPoul-Henning Kamp 	i2c_opt.off = 0;
754*14980d69SJohn F. Carr 	i2c_opt.addr = 0;
7555ab41ff8SPoul-Henning Kamp 	i2c_opt.verbose = 0;
7565ab41ff8SPoul-Henning Kamp 	i2c_opt.dir = 'r';	/* direction = read */
7575ab41ff8SPoul-Henning Kamp 	i2c_opt.width = "8";
7585ab41ff8SPoul-Henning Kamp 	i2c_opt.count = 1;
7595ab41ff8SPoul-Henning Kamp 	i2c_opt.binary = 0;	/* ASCII text output */
7607183d96eSPoul-Henning Kamp 	i2c_opt.skip = NULL;	/* scan all addresses */
761e32b2bcfSPoul-Henning Kamp 	i2c_opt.mode = I2C_MODE_TRANSFER;
7625ab41ff8SPoul-Henning Kamp 
7637183d96eSPoul-Henning Kamp 	/* Find out what we are going to do */
7647183d96eSPoul-Henning Kamp 
76567bceb38SMartin Birgmeier 	while ((ch = getopt(argc, argv, optflags)) != -1) {
7665ab41ff8SPoul-Henning Kamp 		switch(ch) {
7675ab41ff8SPoul-Henning Kamp 		case 'a':
7689c10d00bSPoul-Henning Kamp 		case 'i':
7697183d96eSPoul-Henning Kamp 		case 'r':
7707183d96eSPoul-Henning Kamp 		case 's':
7717183d96eSPoul-Henning Kamp 			if (do_what)
7727183d96eSPoul-Henning Kamp 				usage("Only one of [-a|-h|-r|-s]");
7737183d96eSPoul-Henning Kamp 			do_what = ch;
7747183d96eSPoul-Henning Kamp 			break;
7757183d96eSPoul-Henning Kamp 		case 'h':
7767183d96eSPoul-Henning Kamp 			usage("Help:");
7777183d96eSPoul-Henning Kamp 			break;
7787183d96eSPoul-Henning Kamp 		default:
7797183d96eSPoul-Henning Kamp 			break;
7807183d96eSPoul-Henning Kamp 		}
7817183d96eSPoul-Henning Kamp 	}
7827183d96eSPoul-Henning Kamp 
7837183d96eSPoul-Henning Kamp 	/* Then handle the legal subset of arguments */
7847183d96eSPoul-Henning Kamp 
7857183d96eSPoul-Henning Kamp 	switch (do_what) {
7869c10d00bSPoul-Henning Kamp 	case 0: usage("Pick one of [-a|-h|-i|-r|-s]"); break;
7877183d96eSPoul-Henning Kamp 	case 'a': optflags = "a:f:d:w:o:c:m:bv"; break;
7889c10d00bSPoul-Henning Kamp 	case 'i': optflags = "iv"; break;
7897183d96eSPoul-Henning Kamp 	case 'r': optflags = "rf:v"; break;
7907183d96eSPoul-Henning Kamp 	case 's': optflags = "sf:n:v"; break;
7917183d96eSPoul-Henning Kamp 	default: assert("Bad do_what");
7927183d96eSPoul-Henning Kamp 	}
7937183d96eSPoul-Henning Kamp 
7947183d96eSPoul-Henning Kamp 	optreset = 1;
7957183d96eSPoul-Henning Kamp 	optind = 1;
7967183d96eSPoul-Henning Kamp 
7977183d96eSPoul-Henning Kamp 	while ((ch = getopt(argc, argv, optflags)) != -1) {
7987183d96eSPoul-Henning Kamp 		switch(ch) {
7997183d96eSPoul-Henning Kamp 		case 'a':
80063c8d31eSPoul-Henning Kamp 			i2c_opt.addr = strtoul(optarg, 0, 16);
8015ab41ff8SPoul-Henning Kamp 			if (i2c_opt.addr == 0 && errno == EINVAL)
8025ab41ff8SPoul-Henning Kamp 				usage("Bad -a argument (hex)");
80363c8d31eSPoul-Henning Kamp 			if (i2c_opt.addr == 0 || i2c_opt.addr > 0x7f)
80463c8d31eSPoul-Henning Kamp 				usage("Bad -a argument (01..7f)");
80563c8d31eSPoul-Henning Kamp 			i2c_opt.addr <<= 1;
8065ab41ff8SPoul-Henning Kamp 			break;
807f4583ebaSPoul-Henning Kamp 		case 'b':
808f4583ebaSPoul-Henning Kamp 			i2c_opt.binary = 1;
809f4583ebaSPoul-Henning Kamp 			break;
810f4583ebaSPoul-Henning Kamp 		case 'c':
811f4583ebaSPoul-Henning Kamp 			i2c_opt.count = (strtoul(optarg, 0, 10));
812f4583ebaSPoul-Henning Kamp 			if (i2c_opt.count == 0 && errno == EINVAL)
813f4583ebaSPoul-Henning Kamp 				usage("Bad -c argument (decimal)");
8145ab41ff8SPoul-Henning Kamp 			break;
8155ab41ff8SPoul-Henning Kamp 		case 'd':
8167183d96eSPoul-Henning Kamp 			if (strcmp(optarg, "r") && strcmp(optarg, "w"))
8177183d96eSPoul-Henning Kamp 				usage("Bad -d argument ([r|w])");
8185ab41ff8SPoul-Henning Kamp 			i2c_opt.dir = optarg[0];
8195ab41ff8SPoul-Henning Kamp 			break;
820f4583ebaSPoul-Henning Kamp 		case 'f':
821f4583ebaSPoul-Henning Kamp 			dev = optarg;
8225ab41ff8SPoul-Henning Kamp 			break;
8239c10d00bSPoul-Henning Kamp 		case 'i': break;
8245ab41ff8SPoul-Henning Kamp 		case 'm':
8255ab41ff8SPoul-Henning Kamp 			if (!strcmp(optarg, "no"))
8265ab41ff8SPoul-Henning Kamp 				i2c_opt.mode = I2C_MODE_NONE;
8275ab41ff8SPoul-Henning Kamp 			else if (!strcmp(optarg, "ss"))
8285ab41ff8SPoul-Henning Kamp 				i2c_opt.mode = I2C_MODE_STOP_START;
8295ab41ff8SPoul-Henning Kamp 			else if (!strcmp(optarg, "rs"))
8305ab41ff8SPoul-Henning Kamp 				i2c_opt.mode = I2C_MODE_REPEATED_START;
8315ab41ff8SPoul-Henning Kamp 			else if (!strcmp(optarg, "tr"))
8325ab41ff8SPoul-Henning Kamp 				i2c_opt.mode = I2C_MODE_TRANSFER;
8335ab41ff8SPoul-Henning Kamp 			else
8345ab41ff8SPoul-Henning Kamp 				usage("Bad -m argument ([no|ss|rs|tr])");
8355ab41ff8SPoul-Henning Kamp 			break;
8365ab41ff8SPoul-Henning Kamp 		case 'n':
8377183d96eSPoul-Henning Kamp 			i2c_opt.skip = optarg;
8385ab41ff8SPoul-Henning Kamp 			break;
839f4583ebaSPoul-Henning Kamp 		case 'o':
840f4583ebaSPoul-Henning Kamp 			i2c_opt.off = strtoul(optarg, 0, 16);
841f4583ebaSPoul-Henning Kamp 			if (i2c_opt.off == 0 && errno == EINVAL)
842f4583ebaSPoul-Henning Kamp 				usage("Bad -o argument (hex)");
8435ab41ff8SPoul-Henning Kamp 			break;
844f4583ebaSPoul-Henning Kamp 		case 'r': break;
845f4583ebaSPoul-Henning Kamp 		case 's': break;
8465ab41ff8SPoul-Henning Kamp 		case 'v':
8475ab41ff8SPoul-Henning Kamp 			i2c_opt.verbose = 1;
8485ab41ff8SPoul-Henning Kamp 			break;
849f4583ebaSPoul-Henning Kamp 		case 'w':
850f4583ebaSPoul-Henning Kamp 			i2c_opt.width = optarg;		// checked later.
851f4583ebaSPoul-Henning Kamp 			break;
8525ab41ff8SPoul-Henning Kamp 		default:
8537183d96eSPoul-Henning Kamp 			fprintf(stderr, "Illegal -%c option", ch);
8547183d96eSPoul-Henning Kamp 			usage(NULL);
8555ab41ff8SPoul-Henning Kamp 		}
8565ab41ff8SPoul-Henning Kamp 	}
8575ab41ff8SPoul-Henning Kamp 	argc -= optind;
8585ab41ff8SPoul-Henning Kamp 	argv += optind;
8599c10d00bSPoul-Henning Kamp 	if (do_what == 'i')
8609c10d00bSPoul-Henning Kamp 		return(instruct_bus(i2c_opt, argc, argv));
8615ab41ff8SPoul-Henning Kamp 	if (argc > 0)
8625ab41ff8SPoul-Henning Kamp 		usage("Too many arguments");
8635ab41ff8SPoul-Henning Kamp 
8645ab41ff8SPoul-Henning Kamp 	/* Set default mode if option -m is not specified */
8655ab41ff8SPoul-Henning Kamp 	if (i2c_opt.mode == I2C_MODE_NOTSET) {
8665ab41ff8SPoul-Henning Kamp 		if (i2c_opt.dir == 'r')
8675ab41ff8SPoul-Henning Kamp 			i2c_opt.mode = I2C_MODE_STOP_START;
8685ab41ff8SPoul-Henning Kamp 		else if (i2c_opt.dir == 'w')
8695ab41ff8SPoul-Henning Kamp 			i2c_opt.mode = I2C_MODE_NONE;
8705ab41ff8SPoul-Henning Kamp 	}
8715ab41ff8SPoul-Henning Kamp 
8725ab41ff8SPoul-Henning Kamp 	err_msg = encode_offset(i2c_opt.width, i2c_opt.off,
8735ab41ff8SPoul-Henning Kamp 	    i2c_opt.off_buf, &i2c_opt.off_len);
8745ab41ff8SPoul-Henning Kamp 	if (err_msg != NULL) {
8755ab41ff8SPoul-Henning Kamp 		fprintf(stderr, "%s", err_msg);
876f4583ebaSPoul-Henning Kamp 		return(EX_USAGE);
8775ab41ff8SPoul-Henning Kamp 	}
8785ab41ff8SPoul-Henning Kamp 
8795ab41ff8SPoul-Henning Kamp 	fd = open(dev, O_RDWR);
8805ab41ff8SPoul-Henning Kamp 	if (fd == -1) {
8815ab41ff8SPoul-Henning Kamp 		fprintf(stderr, "Error opening I2C controller (%s): %s\n",
8825ab41ff8SPoul-Henning Kamp 		    dev, strerror(errno));
8835ab41ff8SPoul-Henning Kamp 		return (EX_NOINPUT);
8845ab41ff8SPoul-Henning Kamp 	}
8855ab41ff8SPoul-Henning Kamp 
8867183d96eSPoul-Henning Kamp 	switch (do_what) {
887f4583ebaSPoul-Henning Kamp 	case 'a':
888*14980d69SJohn F. Carr 		if (i2c_opt.verbose)
889*14980d69SJohn F. Carr 			fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
890*14980d69SJohn F. Carr 			    "offset: 0x%02x, width: %s, count: %u\n", dev,
891*14980d69SJohn F. Carr 			    i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off,
892*14980d69SJohn F. Carr 			    i2c_opt.width, i2c_opt.count);
893f4583ebaSPoul-Henning Kamp 		error = access_bus(fd, i2c_opt);
894f4583ebaSPoul-Henning Kamp 		break;
8957183d96eSPoul-Henning Kamp 	case 's':
896b23362afSPoul-Henning Kamp 		error = scan_bus(dev, fd, i2c_opt.skip, i2c_opt.verbose);
8977183d96eSPoul-Henning Kamp 		break;
8987183d96eSPoul-Henning Kamp 	case 'r':
899b23362afSPoul-Henning Kamp 		error = reset_bus(dev, fd, i2c_opt.verbose);
9007183d96eSPoul-Henning Kamp 		break;
9017183d96eSPoul-Henning Kamp 	default:
9027183d96eSPoul-Henning Kamp 		assert("Bad do_what");
9037183d96eSPoul-Henning Kamp 	}
9045ab41ff8SPoul-Henning Kamp 
9055ab41ff8SPoul-Henning Kamp 	ch = close(fd);
9065ab41ff8SPoul-Henning Kamp 	assert(ch == 0);
9075ab41ff8SPoul-Henning Kamp 	return (error);
908477f656bSRafal Jaworowski }
909