smbmsg.c revision 129353
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: head/usr.sbin/smbmsg/smbmsg.c 129353 2004-05-17 19:19:08Z joerg $ 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); 252129330Sjoerg 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); 261129330Sjoerg 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