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