From 6f9cbc08f9a726fc43b31e78130ebc64a248574c Mon Sep 17 00:00:00 2001 From: joerg Date: Mon, 17 May 2004 10:56:05 +0000 Subject: This is smbmsg(8), a small utility to send/receive SMBus messages. Also included is a `bus-probe' modus of operation to help scanning an I2C bus. --- usr.sbin/smbmsg/Makefile | 8 ++ usr.sbin/smbmsg/pathnames.h | 29 ++++ usr.sbin/smbmsg/smbmsg.8 | 286 +++++++++++++++++++++++++++++++++++++ usr.sbin/smbmsg/smbmsg.c | 339 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 662 insertions(+) create mode 100644 usr.sbin/smbmsg/Makefile create mode 100644 usr.sbin/smbmsg/pathnames.h create mode 100644 usr.sbin/smbmsg/smbmsg.8 create mode 100644 usr.sbin/smbmsg/smbmsg.c (limited to 'usr.sbin/smbmsg') diff --git a/usr.sbin/smbmsg/Makefile b/usr.sbin/smbmsg/Makefile new file mode 100644 index 0000000..1fc7d3e --- /dev/null +++ b/usr.sbin/smbmsg/Makefile @@ -0,0 +1,8 @@ +# +# $FreeBSD$ + +PROG= smbmsg +MAN8= smbmsg.8 +WARNS= 6 + +.include diff --git a/usr.sbin/smbmsg/pathnames.h b/usr.sbin/smbmsg/pathnames.h new file mode 100644 index 0000000..aff7335 --- /dev/null +++ b/usr.sbin/smbmsg/pathnames.h @@ -0,0 +1,29 @@ +/*- + * Copyright (C) 2004 Joerg Wunsch + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define PATH_DEFAULTSMBDEV "/dev/smb0" diff --git a/usr.sbin/smbmsg/smbmsg.8 b/usr.sbin/smbmsg/smbmsg.8 new file mode 100644 index 0000000..f25da7b --- /dev/null +++ b/usr.sbin/smbmsg/smbmsg.8 @@ -0,0 +1,286 @@ +.\" Copyright (c) 2004 Joerg Wunsch +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 16, 2004 +.Dt SMBMSG 8 +.Os +.Sh NAME +.Nm smbmsg +.Nd "send or receive messages over an SMBus" +.Sh SYNOPSIS +.Nm +.Op Fl f Ar dev +.Fl p +.Pp +.Nm +.Op Fl f Ar dev +.Fl s Ar slave +.Op Fl F Ar fmt +.Op Fl c Ar cmd +.Op Fl w +.Op Fl i Ar incnt +.Op Fl o Ar outcnt +.Op Ar outdata ... +.Sh DESCRIPTION +The +.Nm +command can be used to send or receive messages over an +SMBus, see +.Xr smbus 4 . +.Pp +The +.Nm +command has two different modi of operation. +The first form shown in the synopsis can be used to +.Dq probe +the devices on the SMBus. +This is done by sending each valid device address one +receive byte, and one quick read message, respectively. +Devices that respond to these requests will by displayed +by their device address, followed by the strings +.Ql r , +.Ql w , +or +.Ql rw , +for devices that are readable, writeable, or both, readable +and writeable, respectively. +The only valid additional option for this modus of operation (besides +the +.Fl p +option that choses the modus) is +.Fl f Ar dev . +See below for a description. +.Pp +Note that probing the bus is risky, since individual devices could +perform unwanted actions upon receiving one of the mentioned messages. +For example, if a particular SMBus device considers +.Em any +write operation issued to it as a request to power off the system, +the probing would trigger this action. +.Pp +The second form shown in the synopsis can be used to send or receive +arbitrary messages to or from individual devices. +This might be useful to explore individual devices on the SMBus, or +maybe even to write short shell scripts performing maintenance +operations on the bus. +.Pp +Any data values on the command-line are integer values in the +range 0 through 255 for byte values, or 0 through 65535 for +word values. +They can be specified using standard +.Ql C +notation (prefix 0 for octal interpretation, or 0x for +hexadecimal interpretation). +.Pp +Since the low-order bit of the device address of SMBus devices +selects between read and write operations, only even-numbered +slave addresses can exist on the bus. +.Pp +The options are as follows: +.Bl -tag -width ".Fl o Ar outcnt" +.It Fl F Ar fmt +Specify the +.Xr printf 3 +format to be used for displaying input data. +This option is ignored in messages that do not read any input +from the SMBus device. +The format defaults to +.Ql 0x%02x +for byte input operations, and to +.Ql 0x%04x +for word input operations. +For multi-byte input (block read), the same format is used for +each individual byte read from the SMBus. +.It Fl c Ar cmd +This is the value of the +.Em command +byte to be issued as part of the SMBus message. +.It Fl f Ar dev +This specifies that +.Ar dev +should be used as the connection to the SMBus, rather than the +default of +.Pa /dev/smb0 . +.It Fl i Ar incnt +An SMBus message should be generated to read +.Ar incnt +bytes from the device. +.It Fl o Ar outcnt +An SMBus message should be generated to write +.Ar outcnt +bytes to the device. +The data values to write are expected to follow all of the options +(and their arguments) on the command-line, where the number of data +bytes must match the +.Ar outcnt +value. +.It Fl p +This selects the +.Em probe bus +modus of operation. +.It Fl s Ar slave +The +.Ar slave +parameter specifies which SMBus device to connect to. +This option also selects the +.Em transfer messages from/to device +modus of operation, where a slave address is mandatory. +.It Fl w +This option specifies that IO operations are word operations, +rather than byte operations. +Either +.Ar incnt , +or +.Ar outcnt +(or both) must be equal 2 in this case. +Note that the SMBus byte order is defined to be little-endian +(low byte first, high byte follows). +.El +.Pp +Not all argument combinations make sense in order to form valid SMBus +messages. +If no +.Fl c Ar cmd +option has been provided, the following messages can be +issued: +.Pp +.Bd -unfilled -offset indent +.TS +l r r. +\fBmessage incnt outcnt\fR +quick read 0 \&- +quick write \&- 0 +receive byte 1 \&- +send byte \&- 1 +.TE +.Ed +.Pp +Note in particular that specifying 0 as a count value +has a different meaning than omitting the respective +option entirely. +.Pp +If a command value has been given using the +.Fl c Ar cmd +option, the following messages can be generated: +.Pp +.Bd -unfilled -offset indent +.TS +l l r r. +\fBmessage \&-w incnt outcnt\fR +read byte no 1 \&- +write byte no \&- 1 +read word yes 2 \&- +write word yes \&- 2 +process call yes 2 2 +block read no \*(Ge 2 \&- +block write no \&- \*(Ge 2 +.TE +.Ed +.Sh FILES +.Bl -tag -width ".Pa /dev/smb0" -compact +.It Pa /dev/smb0 +The default device to connect to, unless +.Fl f Ar dev +has been provided. +.El +.Sh EXAMPLES +Typical usage examples of the +.Nm +command include: +.Pp +.Dl "smbmsg -f /dev/smb1 -p" +.Pp +Probe all devices on the SMBus attached to +.Pa /dev/smb1 . +.Pp +.Dl "smbmsg -s 0x70 -i 1" +.Pp +Issue a +.Em receive byte +message to the device at address 0x70, and display +the received byte using the default format. +.Pp +.Dl "smbmsg -s 0x70 -c 0xff -i 1 -F %d" +.Pp +Issue a +.Em read byte +message to the device at slave address 0x70, using +255 (0xff) as the command-byte to send to the device, +and display the result using the custom format %d. +.Pp +.Dl "smbmsg -s 0xa0 -c 0 -o 1 0x80" +.Pp +Send a +.Em write byte +message to the slave device at address 0xa0, using +0 as the command-byte value, and 0x80 as the byte to +send (after the command). +Assuming this might be a Philips PCF8583 real-time clock, +this would stop the clock. +.Pp +.Dl "smbmsg -s 0xa0 -c 1 -i 6 -F %02x" +.Pp +Send a +.Em block read +command to device at address 0xa0, and read 6 bytes from +it, using hexadecimal display. +Again, assuming a PCF8583 RTC, this would display the +fractions of second, seconds, minutes, hours, year/date, +and weekday/month values. +Since this RTC uses BCD notation, the actual values displayed +were decimal then. +.Pp +.Dl "smbmsg -s 0xa0 -c 2 -o 5 0x00 0x07 0x22 0x16 0x05" +.Pp +Send a +.Em block write +command to device at address 0xa0. +For the PCF8583 RTC, this would set the clock to Sunday (2004%4)-05-16 +22:07:00. +.Sh DIAGNOSTICS +Exit status is 0 on success, or according to +.Xr sysexits 3 +in case of failure. +Diagnostic messages issued are supposed to be self-explanatory. +.Sh SEE ALSO +.Xr printf 3 , +.Xr sysexits 3 , +.Xr smb 4 , +.Xr smbus 4 +.Rs +.%T "The SMBus specification" +.%O http://www.smbus.org/specs/ +.Re +.Sh HISTORY +The +.Nm +command first appeared in +.Fx 5.3 . +.Sh AUTHORS +The +.Nm +utility and this manual page were written by +.An J\(:org Wunsch . diff --git a/usr.sbin/smbmsg/smbmsg.c b/usr.sbin/smbmsg/smbmsg.c new file mode 100644 index 0000000..6d1ca94 --- /dev/null +++ b/usr.sbin/smbmsg/smbmsg.c @@ -0,0 +1,339 @@ +/*- + * Copyright (C) 2004 Joerg Wunsch + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Send or receive messages over an SMBus. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "pathnames.h" + +static const char *dev = PATH_DEFAULTSMBDEV; +static const char *bytefmt = "0x%02x"; +static const char *wordfmt = "0x%04x"; +static const char *fmt; + +static int fd; /* file descriptor for /dev/smbX */ +static int cflag = -1; /* SMBus cmd */ +static int iflag = -1; /* input data */ +static int oflag = -1; /* output data */ +static int pflag; /* probe bus */ +static int slave = -1; /* slave address */ +static int wflag; /* word IO */ + +static unsigned char ibuf[SMB_MAXBLOCKSIZE]; +static unsigned char obuf[SMB_MAXBLOCKSIZE]; +static unsigned short oword, iword; + +/* + * The I2C specs say that all addresses below 16 and above or equal + * 240 are reserved. Address 0 is the global address, but we do not + * care for this detail. + */ +#define MIN_I2C_ADDR 16 +#define MAX_I2C_ADDR 240 + +static void +usage(void) +{ + fprintf(stderr, + "usage: smbmsg [-f dev] -p\n" + " smbmsg [-f dev] -s slave [-F fmt] [-c cmd] [-w] " + "[-i incnt] [-o outcnt] [outdata ...]\n"); + exit (EX_USAGE); +} + +static int +getnum(const char *s) +{ + char *endp; + unsigned long l; + + l = strtoul(s, &endp, 0); + if (*s != '\0' && *endp == '\0') + return (int)l; + return -1; +} + +static void +probe_i2c(void) +{ + unsigned char addr; + int flags; +#define IS_READABLE 1 +#define IS_WRITEABLE 2 + struct smbcmd c; + + printf("Probing for devices on %s:\n", dev); + + for (addr = MIN_I2C_ADDR; addr < MAX_I2C_ADDR; addr += 2) { + c.slave = addr; + flags = 0; + if (ioctl(fd, SMB_RECVB, &c) != -1) + flags = IS_READABLE; + if (ioctl(fd, SMB_QUICK_WRITE, &c) != -1) + flags |= IS_WRITEABLE; + if (flags != 0) { + printf("Device @0x%02x: ", addr); + if (flags & IS_READABLE) + putchar('r'); + if (flags & IS_WRITEABLE) + putchar('w'); + putchar('\n'); + } + } +} + +static int +do_io(void) +{ + struct smbcmd c; + int i; + + c.slave = slave; + c.cmd = cflag; + + if (fmt == NULL && iflag > 0) + fmt = wflag? wordfmt: bytefmt; + + if (cflag == -1) { + /* operations that do not require a command byte */ + if (iflag == -1 && oflag == 0) + /* 0 bytes output: quick write operation */ + return (ioctl(fd, SMB_QUICK_WRITE, &c)); + else if (iflag == 0 && oflag == -1) + /* 0 bytes input: quick read operation */ + return (ioctl(fd, SMB_QUICK_READ, &c)); + else if (iflag == 1 && oflag == -1) { + /* no command, 1 byte input: receive byte op. */ + if (ioctl(fd, SMB_RECVB, &c) == -1) + return (-1); + printf(fmt, (unsigned char)c.cmd); + putchar('\n'); + return (0); + } else if (iflag == -1 && oflag == 1) { + /* no command, 1 byte output: send byte op. */ + c.cmd = obuf[0]; + return (ioctl(fd, SMB_SENDB, &c)); + } else + return (-2); + } + if (iflag == 1 && oflag == -1) { + /* command + 1 byte input: read byte op. */ + c.data.byte_ptr = ibuf; + if (ioctl(fd, SMB_READB, &c) == -1) + return (-1); + printf(fmt, (int)(unsigned char)ibuf[0]); + putchar('\n'); + return (0); + } else if (iflag == -1 && oflag == 1) { + /* command + 1 byte output: write byte op. */ + c.data.byte = obuf[0]; + return (ioctl(fd, SMB_WRITEB, &c)); + } else if (wflag && iflag == 2 && oflag == -1) { + /* command + 2 bytes input: read word op. */ + c.data.word_ptr = &iword; + if (ioctl(fd, SMB_READW, &c) == -1) + return (-1); + printf(fmt, (int)(unsigned short)iword); + putchar('\n'); + return (0); + } else if (wflag && iflag == -1 && oflag == 2) { + /* command + 2 bytes output: write word op. */ + c.data.word = oword; + return (ioctl(fd, SMB_WRITEW, &c)); + } else if (wflag && iflag == 2 && oflag == 2) { + /* + * command + 2 bytes output + 2 bytes input: + * "process call" op. + */ + c.data.process.sdata = oword; + c.data.process.rdata = &iword; + if (ioctl(fd, SMB_PCALL, &c) == -1) + return (-1); + printf(fmt, (int)(unsigned short)iword); + putchar('\n'); + return (0); + } else if (iflag > 1 && oflag == -1) { + /* command + > 1 bytes of input: block read */ + c.data.byte_ptr = ibuf; + c.count = iflag; + if (ioctl(fd, SMB_BREAD, &c) == -1) + return (-1); + for (i = 0; i < iflag; i++) { + if (i != 0) + putchar(' '); + printf(fmt, ibuf[i]); + } + putchar('\n'); + return (0); + } else if (iflag == -1 && oflag > 1) { + /* command + > 1 bytes of output: block write */ + c.data.byte_ptr = obuf; + c.count = oflag; + return (ioctl(fd, SMB_BWRITE, &c)); + } + + return (-2); +} + + +int +main(int argc, char **argv) +{ + int i, n, errs = 0; + int savederrno; + + while ((i = getopt(argc, argv, "F:c:f:i:o:ps:w")) != -1) + switch (i) { + case 'F': + fmt = optarg; + break; + + case 'c': + if ((cflag = getnum(optarg)) == -1) + errx(EX_USAGE, "Invalid number: %s", optarg); + if (cflag < 0 || cflag >= 256) + errx(EX_USAGE, + "CMD out of range: %d", + cflag); + break; + + case 'f': + dev = optarg; + break; + + case 'i': + if ((iflag = getnum(optarg)) == -1) + errx(EX_USAGE, "Invalid number: %s", optarg); + if (iflag < 0 || iflag >= SMB_MAXBLOCKSIZE) + errx(EX_USAGE, + "# input bytes out of range: %d", + iflag); + break; + + case 'o': + if ((oflag = getnum(optarg)) == -1) + errx(EX_USAGE, "Invalid number: %s", optarg); + if (oflag < 0 || oflag >= SMB_MAXBLOCKSIZE) + errx(EX_USAGE, + "# output bytes out of range: %d", + oflag); + break; + + case 'p': + pflag = 1; + break; + + case 's': + if ((slave = getnum(optarg)) == -1) + errx(EX_USAGE, "Invalid number: %s", optarg); + + if (slave < MIN_I2C_ADDR || slave >= MAX_I2C_ADDR) + errx(EX_USAGE, + "Slave address out of range: %d", + slave); + break; + + case 'w': + wflag = 1; + break; + + default: + errs++; + } + argc -= optind; + argv += optind; + if (errs || (slave != -1 && pflag) || (slave == -1 && !pflag)) + usage(); + if (wflag && + !((iflag == 2 && oflag == -1) || + (iflag == -1 && oflag == 2) || + (iflag == 2 && oflag == 2))) + errx(EX_USAGE, "Illegal # IO bytes for word IO"); + if (!pflag && iflag == -1 && oflag == -1) + errx(EX_USAGE, "Nothing to do"); + if (pflag && (cflag != -1 || iflag != -1 || oflag != -1 || wflag != 0)) + usage(); + if (oflag > 0) { + if (oflag == 2 && wflag) { + if (argc == 0) + errx(EX_USAGE, "Too few arguments for -o count"); + if ((n = getnum(*argv)) == -1) + errx(EX_USAGE, "Invalid number: %s", *argv); + if (n < 0 || n >= 65535) + errx(EX_USAGE, "Value out of range: %d", n); + oword = n; + argc--; + argv++; + } else for (i = 0; i < oflag; i++, argv++, argc--) { + if (argc == 0) + errx(EX_USAGE, "Too few arguments for -o count"); + if ((n = getnum(*argv)) == -1) + errx(EX_USAGE, "Invalid number: %s", *argv); + if (n < 0 || n >= 256) + errx(EX_USAGE, "Value out of range: %d", n); + obuf[i] = n; + } + } + if (argc != 0) + usage(); + + if ((fd = open(dev, O_RDWR)) == -1) + err(EX_UNAVAILABLE, "Cannot open %s", dev); + + i = 0; + if (pflag) + probe_i2c(); + else + i = do_io(); + + savederrno = errno; + close(fd); + errno = savederrno; + + if (i == -1) + err(EX_UNAVAILABLE, "Error performing SMBus IO"); + else if (i == -2) + errx(EX_USAGE, "Invalid option combination"); + + return 0; +} -- cgit v1.1