1129330Sjoerg/*-
2129330Sjoerg * Copyright (C) 2004 Joerg Wunsch
3129330Sjoerg * All Rights Reserved.
4129330Sjoerg *
5129330Sjoerg * Redistribution and use in source and binary forms, with or without
6129330Sjoerg * modification, are permitted provided that the following conditions
7129330Sjoerg * are met:
8129330Sjoerg * 1. Redistributions of source code must retain the above copyright
9129330Sjoerg *    notice, this list of conditions and the following disclaimer.
10129330Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
11129330Sjoerg *    notice, this list of conditions and the following disclaimer in the
12129330Sjoerg *    documentation and/or other materials provided with the distribution.
13129330Sjoerg *
14129330Sjoerg * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15129330Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16129330Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17129330Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18129330Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19129330Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20129330Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21129330Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22129330Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23129330Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24129330Sjoerg * SUCH DAMAGE.
25129330Sjoerg *
26129330Sjoerg * $FreeBSD$
27129330Sjoerg */
28129330Sjoerg
29129330Sjoerg/*
30129330Sjoerg * Send or receive messages over an SMBus.
31129330Sjoerg */
32129330Sjoerg
33129330Sjoerg#include <err.h>
34129330Sjoerg#include <errno.h>
35129330Sjoerg#include <fcntl.h>
36129330Sjoerg#include <stdio.h>
37129330Sjoerg#include <stdlib.h>
38129330Sjoerg#include <string.h>
39129330Sjoerg#include <sysexits.h>
40129330Sjoerg#include <unistd.h>
41129330Sjoerg
42129330Sjoerg#include <sys/types.h>
43129330Sjoerg#include <sys/ioctl.h>
44129330Sjoerg
45129330Sjoerg#include <dev/smbus/smb.h>
46129330Sjoerg
47129330Sjoerg#include "pathnames.h"
48129330Sjoerg
49129330Sjoergstatic const char *dev = PATH_DEFAULTSMBDEV;
50129330Sjoergstatic const char *bytefmt = "0x%02x";
51129330Sjoergstatic const char *wordfmt = "0x%04x";
52129330Sjoergstatic const char *fmt;
53129330Sjoerg
54129330Sjoergstatic int fd;			/* file descriptor for /dev/smbX */
55129330Sjoergstatic int cflag = -1;		/* SMBus cmd */
56129330Sjoergstatic int iflag = -1;		/* input data */
57129330Sjoergstatic int oflag = -1;		/* output data */
58129330Sjoergstatic int pflag;		/* probe bus */
59129330Sjoergstatic int slave = -1;		/* slave address */
60129330Sjoergstatic int wflag;		/* word IO */
61129330Sjoerg
62129330Sjoergstatic unsigned char ibuf[SMB_MAXBLOCKSIZE];
63129330Sjoergstatic unsigned char obuf[SMB_MAXBLOCKSIZE];
64129330Sjoergstatic unsigned short oword, iword;
65129330Sjoerg
66129330Sjoerg/*
67129330Sjoerg * The I2C specs say that all addresses below 16 and above or equal
68129330Sjoerg * 240 are reserved.  Address 0 is the global address, but we do not
69129330Sjoerg * care for this detail.
70129330Sjoerg */
71129330Sjoerg#define MIN_I2C_ADDR	16
72129330Sjoerg#define MAX_I2C_ADDR	240
73129330Sjoerg
74129353Sjoergstatic int	do_io(void);
75129353Sjoergstatic int	getnum(const char *s);
76129353Sjoergstatic void	probe_i2c(void);
77129353Sjoergstatic void	usage(void);
78129353Sjoerg
79129330Sjoergstatic void
80129330Sjoergusage(void)
81129330Sjoerg{
82129330Sjoerg	fprintf(stderr,
83129330Sjoerg		"usage: smbmsg [-f dev] -p\n"
84129330Sjoerg		"       smbmsg [-f dev] -s slave [-F fmt] [-c cmd] [-w] "
85129330Sjoerg		"[-i incnt] [-o outcnt] [outdata ...]\n");
86129353Sjoerg	exit(EX_USAGE);
87129330Sjoerg}
88129330Sjoerg
89129330Sjoergstatic int
90129330Sjoerggetnum(const char *s)
91129330Sjoerg{
92129330Sjoerg	char *endp;
93129330Sjoerg	unsigned long l;
94129330Sjoerg
95129330Sjoerg	l = strtoul(s, &endp, 0);
96129330Sjoerg	if (*s != '\0' && *endp == '\0')
97129330Sjoerg		return (int)l;
98129353Sjoerg	return (-1);
99129330Sjoerg}
100129330Sjoerg
101129330Sjoergstatic void
102129330Sjoergprobe_i2c(void)
103129330Sjoerg{
104129330Sjoerg	unsigned char addr;
105129330Sjoerg	int flags;
106129330Sjoerg#define IS_READABLE	1
107129330Sjoerg#define IS_WRITEABLE	2
108129330Sjoerg	struct smbcmd c;
109129330Sjoerg
110129330Sjoerg	printf("Probing for devices on %s:\n", dev);
111129330Sjoerg
112129330Sjoerg	for (addr = MIN_I2C_ADDR; addr < MAX_I2C_ADDR; addr += 2) {
113129330Sjoerg		c.slave = addr;
114129330Sjoerg		flags = 0;
115129330Sjoerg		if (ioctl(fd, SMB_RECVB, &c) != -1)
116129330Sjoerg			flags = IS_READABLE;
117129330Sjoerg		if (ioctl(fd, SMB_QUICK_WRITE, &c) != -1)
118129330Sjoerg			flags |= IS_WRITEABLE;
119129330Sjoerg		if (flags != 0) {
120129330Sjoerg			printf("Device @0x%02x: ", addr);
121129330Sjoerg			if (flags & IS_READABLE)
122129330Sjoerg				putchar('r');
123129330Sjoerg			if (flags & IS_WRITEABLE)
124129330Sjoerg				putchar('w');
125129330Sjoerg			putchar('\n');
126129330Sjoerg		}
127129330Sjoerg	}
128129330Sjoerg}
129129330Sjoerg
130129330Sjoergstatic int
131129330Sjoergdo_io(void)
132129330Sjoerg{
133129330Sjoerg	struct smbcmd c;
134129330Sjoerg	int i;
135129330Sjoerg
136129330Sjoerg	c.slave = slave;
137129330Sjoerg	c.cmd = cflag;
138129330Sjoerg
139129330Sjoerg	if (fmt == NULL && iflag > 0)
140129330Sjoerg		fmt = wflag? wordfmt: bytefmt;
141129330Sjoerg
142129330Sjoerg	if (cflag == -1) {
143129330Sjoerg		/* operations that do not require a command byte */
144129330Sjoerg		if (iflag == -1 && oflag == 0)
145129330Sjoerg			/* 0 bytes output: quick write operation */
146129330Sjoerg			return (ioctl(fd, SMB_QUICK_WRITE, &c));
147129330Sjoerg		else if (iflag == 0 && oflag == -1)
148129330Sjoerg			/* 0 bytes input: quick read operation */
149129330Sjoerg			return (ioctl(fd, SMB_QUICK_READ, &c));
150129330Sjoerg		else if (iflag == 1 && oflag == -1) {
151129330Sjoerg			/* no command, 1 byte input: receive byte op. */
152129330Sjoerg			if (ioctl(fd, SMB_RECVB, &c) == -1)
153129330Sjoerg				return (-1);
154129330Sjoerg			printf(fmt, (unsigned char)c.cmd);
155129330Sjoerg			putchar('\n');
156129330Sjoerg			return (0);
157129330Sjoerg		} else if (iflag == -1 && oflag == 1) {
158129330Sjoerg			/* no command, 1 byte output: send byte op. */
159129330Sjoerg			c.cmd = obuf[0];
160129330Sjoerg			return (ioctl(fd, SMB_SENDB, &c));
161129330Sjoerg		} else
162129330Sjoerg			return (-2);
163129330Sjoerg	}
164129330Sjoerg	if (iflag == 1 && oflag == -1) {
165129330Sjoerg		/* command + 1 byte input: read byte op. */
166129330Sjoerg		c.data.byte_ptr = ibuf;
167129330Sjoerg		if (ioctl(fd, SMB_READB, &c) == -1)
168129330Sjoerg			return (-1);
169129330Sjoerg		printf(fmt, (int)(unsigned char)ibuf[0]);
170129330Sjoerg		putchar('\n');
171129330Sjoerg		return (0);
172129330Sjoerg	} else if (iflag == -1 && oflag == 1) {
173129330Sjoerg		/* command + 1 byte output: write byte op. */
174129330Sjoerg		c.data.byte = obuf[0];
175129330Sjoerg		return (ioctl(fd, SMB_WRITEB, &c));
176129330Sjoerg	} else if (wflag && iflag == 2 && oflag == -1) {
177129330Sjoerg		/* command + 2 bytes input: read word op. */
178129330Sjoerg		c.data.word_ptr = &iword;
179129330Sjoerg		if (ioctl(fd, SMB_READW, &c) == -1)
180129330Sjoerg			return (-1);
181129330Sjoerg		printf(fmt, (int)(unsigned short)iword);
182129330Sjoerg		putchar('\n');
183129330Sjoerg		return (0);
184129330Sjoerg	} else if (wflag && iflag == -1 && oflag == 2) {
185129330Sjoerg		/* command + 2 bytes output: write word op. */
186129330Sjoerg		c.data.word = oword;
187129330Sjoerg		return (ioctl(fd, SMB_WRITEW, &c));
188129330Sjoerg	} else if (wflag && iflag == 2 && oflag == 2) {
189129330Sjoerg		/*
190129330Sjoerg		 * command + 2 bytes output + 2 bytes input:
191129330Sjoerg		 * "process call" op.
192129330Sjoerg		 */
193129330Sjoerg		c.data.process.sdata = oword;
194129330Sjoerg		c.data.process.rdata = &iword;
195129330Sjoerg		if (ioctl(fd, SMB_PCALL, &c) == -1)
196129330Sjoerg			return (-1);
197129330Sjoerg		printf(fmt, (int)(unsigned short)iword);
198129330Sjoerg		putchar('\n');
199129330Sjoerg		return (0);
200129330Sjoerg	} else if (iflag > 1 && oflag == -1) {
201129330Sjoerg		/* command + > 1 bytes of input: block read */
202129330Sjoerg		c.data.byte_ptr = ibuf;
203129330Sjoerg		c.count = iflag;
204129330Sjoerg		if (ioctl(fd, SMB_BREAD, &c) == -1)
205129330Sjoerg			return (-1);
206129330Sjoerg		for (i = 0; i < iflag; i++) {
207129330Sjoerg			if (i != 0)
208129330Sjoerg				putchar(' ');
209129330Sjoerg			printf(fmt, ibuf[i]);
210129330Sjoerg		}
211129330Sjoerg		putchar('\n');
212129330Sjoerg		return (0);
213129330Sjoerg	} else if (iflag == -1 && oflag > 1) {
214129330Sjoerg		/* command + > 1 bytes of output: block write */
215129330Sjoerg		c.data.byte_ptr = obuf;
216129330Sjoerg		c.count = oflag;
217129330Sjoerg		return (ioctl(fd, SMB_BWRITE, &c));
218129330Sjoerg	}
219129330Sjoerg
220129330Sjoerg	return (-2);
221129330Sjoerg}
222129330Sjoerg
223129330Sjoerg
224129330Sjoergint
225129330Sjoergmain(int argc, char **argv)
226129330Sjoerg{
227129330Sjoerg	int i, n, errs = 0;
228129330Sjoerg	int savederrno;
229129330Sjoerg
230129330Sjoerg	while ((i = getopt(argc, argv, "F:c:f:i:o:ps:w")) != -1)
231129330Sjoerg		switch (i) {
232129330Sjoerg		case 'F':
233129330Sjoerg			fmt = optarg;
234129330Sjoerg			break;
235129330Sjoerg
236129330Sjoerg		case 'c':
237129330Sjoerg			if ((cflag = getnum(optarg)) == -1)
238129330Sjoerg				errx(EX_USAGE, "Invalid number: %s", optarg);
239129330Sjoerg			if (cflag < 0 || cflag >= 256)
240129330Sjoerg				errx(EX_USAGE,
241129330Sjoerg				     "CMD out of range: %d",
242129330Sjoerg				     cflag);
243129330Sjoerg			break;
244129330Sjoerg
245129330Sjoerg		case 'f':
246129330Sjoerg			dev = optarg;
247129330Sjoerg			break;
248129330Sjoerg
249129330Sjoerg		case 'i':
250129330Sjoerg			if ((iflag = getnum(optarg)) == -1)
251129330Sjoerg				errx(EX_USAGE, "Invalid number: %s", optarg);
252129779Sjoerg			if (iflag < 0 || iflag > SMB_MAXBLOCKSIZE)
253129330Sjoerg				errx(EX_USAGE,
254129330Sjoerg				     "# input bytes out of range: %d",
255129330Sjoerg				     iflag);
256129330Sjoerg			break;
257129330Sjoerg
258129330Sjoerg		case 'o':
259129330Sjoerg			if ((oflag = getnum(optarg)) == -1)
260129330Sjoerg				errx(EX_USAGE, "Invalid number: %s", optarg);
261129779Sjoerg			if (oflag < 0 || oflag > SMB_MAXBLOCKSIZE)
262129330Sjoerg				errx(EX_USAGE,
263129330Sjoerg				     "# output bytes out of range: %d",
264129330Sjoerg				     oflag);
265129330Sjoerg			break;
266129330Sjoerg
267129330Sjoerg		case 'p':
268129330Sjoerg			pflag = 1;
269129330Sjoerg			break;
270129330Sjoerg
271129330Sjoerg		case 's':
272129330Sjoerg			if ((slave = getnum(optarg)) == -1)
273129330Sjoerg				errx(EX_USAGE, "Invalid number: %s", optarg);
274129330Sjoerg
275129330Sjoerg			if (slave < MIN_I2C_ADDR || slave >= MAX_I2C_ADDR)
276129330Sjoerg				errx(EX_USAGE,
277129330Sjoerg				     "Slave address out of range: %d",
278129330Sjoerg				     slave);
279129330Sjoerg			break;
280129330Sjoerg
281129330Sjoerg		case 'w':
282129330Sjoerg			wflag = 1;
283129330Sjoerg			break;
284129330Sjoerg
285129330Sjoerg		default:
286129330Sjoerg			errs++;
287129330Sjoerg		}
288129330Sjoerg	argc -= optind;
289129330Sjoerg	argv += optind;
290129330Sjoerg	if (errs || (slave != -1 && pflag) || (slave == -1 && !pflag))
291129330Sjoerg		usage();
292129330Sjoerg	if (wflag &&
293129330Sjoerg	    !((iflag == 2 && oflag == -1) ||
294129330Sjoerg	      (iflag == -1 && oflag == 2) ||
295129330Sjoerg	      (iflag == 2 && oflag == 2)))
296129330Sjoerg		errx(EX_USAGE, "Illegal # IO bytes for word IO");
297129330Sjoerg	if (!pflag && iflag == -1 && oflag == -1)
298129330Sjoerg		errx(EX_USAGE, "Nothing to do");
299129330Sjoerg	if (pflag && (cflag != -1 || iflag != -1 || oflag != -1 || wflag != 0))
300129330Sjoerg		usage();
301129330Sjoerg	if (oflag > 0) {
302129330Sjoerg		if (oflag == 2 && wflag) {
303129330Sjoerg			if (argc == 0)
304129330Sjoerg				errx(EX_USAGE, "Too few arguments for -o count");
305129330Sjoerg			if ((n = getnum(*argv)) == -1)
306129330Sjoerg				errx(EX_USAGE, "Invalid number: %s", *argv);
307129330Sjoerg			if (n < 0 || n >= 65535)
308129330Sjoerg				errx(EX_USAGE, "Value out of range: %d", n);
309129330Sjoerg			oword = n;
310129330Sjoerg			argc--;
311129330Sjoerg			argv++;
312129330Sjoerg		} else for (i = 0; i < oflag; i++, argv++, argc--) {
313129330Sjoerg			if (argc == 0)
314129330Sjoerg				errx(EX_USAGE, "Too few arguments for -o count");
315129330Sjoerg			if ((n = getnum(*argv)) == -1)
316129330Sjoerg				errx(EX_USAGE, "Invalid number: %s", *argv);
317129330Sjoerg			if (n < 0 || n >= 256)
318129330Sjoerg				errx(EX_USAGE, "Value out of range: %d", n);
319129330Sjoerg			obuf[i] = n;
320129330Sjoerg		}
321129330Sjoerg	}
322129330Sjoerg	if (argc != 0)
323129330Sjoerg		usage();
324129330Sjoerg
325129330Sjoerg	if ((fd = open(dev, O_RDWR)) == -1)
326129330Sjoerg		err(EX_UNAVAILABLE, "Cannot open %s", dev);
327129330Sjoerg
328129330Sjoerg	i = 0;
329129330Sjoerg	if (pflag)
330129330Sjoerg		probe_i2c();
331129330Sjoerg	else
332129330Sjoerg		i = do_io();
333129330Sjoerg
334129330Sjoerg	savederrno = errno;
335129330Sjoerg	close(fd);
336129330Sjoerg	errno = savederrno;
337129330Sjoerg
338129330Sjoerg	if (i == -1)
339129330Sjoerg		err(EX_UNAVAILABLE, "Error performing SMBus IO");
340129330Sjoerg	else if (i == -2)
341129330Sjoerg		errx(EX_USAGE, "Invalid option combination");
342129330Sjoerg
343129353Sjoerg	return (0);
344129330Sjoerg}
345