Index: head/share/man/man4/smb.4 =================================================================== --- head/share/man/man4/smb.4 (revision 281984) +++ head/share/man/man4/smb.4 (revision 281985) @@ -1,204 +1,243 @@ .\" Copyright (c) 1998, Nicolas Souchu .\" Copyright (c) 2004, Joerg Wunsch +.\" Copyright (c) 2015, Michael Gmelin .\" 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 February 6, 2009 +.Dd April 25, 2015 .Dt SMB 4 .Os .Sh NAME .Nm smb .Nd SMB generic I/O device driver .Sh SYNOPSIS .Cd "device smb" .Sh DESCRIPTION The .Em smb character device driver provides generic i/o to any .Xr smbus 4 instance. In order to control SMB devices, use .Pa /dev/smb? with the ioctls described below. Any of these ioctl commands takes a pointer to .Vt struct smbcmd as its argument. .Bd -literal #include struct smbcmd { - char cmd; - int count; - u_char slave; + u_char cmd; + u_char reserved; + u_short op; union { - char byte; - short word; - - char *byte_ptr; - short *word_ptr; - - struct { - short sdata; - short *rdata; - } process; - } data; + char byte; + char buf[2]; + short word; + } wdata; + union { + char byte; + char buf[2]; + short word; + } rdata; + int slave; + char *wbuf; /* use wdata if NULL */ + int wcount; + char *rbuf; /* use rdata if NULL */ + int rcount; }; .Ed .Pp The .Fa slave field is always used, and provides the address of the SMBus slave device to talk to. The slave address is specified in the seven most significant bits .Pq i.e. Dq "left-justified" . The least significant bit of the slave address must be zero. .Pp .Bl -column ".Dv SMB_QUICK_WRITE" -compact .It Em Ioctl Ta Em Description .Pp .It Dv SMB_QUICK_WRITE Ta The .Em QuickWrite command just issues the device address with write intent to the bus, without transferring any data. .It Dv SMB_QUICK_READ Ta The .Em QuickRead command just issues the device address with read intent to the bus, without transferring any data. .It Dv SMB_SENDB Ta The .Em SendByte command sends the byte provided in the .Fa cmd field to the device. .It Dv SMB_RECVB Ta The .Em ReceiveByte command reads a single byte from the device which will be returned in the .Fa cmd field. .It Dv SMB_WRITEB Ta The .Em WriteByte command first sends the byte from the .Fa cmd field to the device, followed by the byte given in -.Fa data.byte . +.Fa wdata.byte . .It Dv SMB_WRITEW Ta The .Em WriteWord command first sends the byte from the .Fa cmd field to the device, followed by the word given in -.Fa data.word . +.Fa wdata.word . Note that the SMBus byte-order is little-endian by definition. .It Dv SMB_READB Ta The .Em ReadByte command first sends the byte from the .Fa cmd field to the device, and then reads one byte of data from the device. -The returned data will be stored in the location pointed to by -.Fa data.byte_ptr . +The returned data will be stored in +.Fa rdata.byte . .It Dv SMB_READW Ta The .Em ReadWord command first sends the byte from the .Fa cmd field to the device, and then reads one word of data from the device. -The returned data will be stored in the location pointed to by -.Fa data.word_ptr . +The returned data will be stored in +.Fa rdata.word . .It Dv SMB_PCALL Ta The .Em ProcedureCall command first sends the byte from the .Fa cmd field to the device, followed by the word provided in -.Fa data.process.sdata . +.Fa wdata.word . It then reads one word of data from the device, and returns it -in the location pointed to by -.Fa data.process.rdata . +in +.Fa rdata.word . .It Dv SMB_BWRITE Ta The .Em BlockWrite command first sends the byte from the .Fa cmd field to the device, followed by -.Fa count +.Fa wcount bytes of data that are taken from the buffer pointed to by -.Fa data.byte_ptr . +.Fa wbuf . The SMBus specification mandates that no more than 32 bytes of -data can be transferred in a single block read or write command. +data can be transferred in a single block read or write command, +but since +.Xr smbus 4 +is also used to access I2C devices, the limit has been increased +to 1024. This value is available in the constant .Dv SMB_MAXBLOCKSIZE . .It Dv SMB_BREAD Ta The .Em BlockRead command first sends the byte from the .Fa cmd field to the device, and then reads -.Fa count +.Fa rcount bytes of data that from the device. These data will be returned in the buffer pointed to by -.Fa data.byte_ptr . +.Fa rbuf . +.It Dv SMB_TRANS Ta +The +.Em Trans +command sends an SMB roll-up transaction with flags that also allow it to +be used for (mostly) I2C pass-through and with with 10-bit addresses. +This function can be used to roll up all of the above functions. +It first sends the byte from the +.Fa cmd +field to the device, followed by +.Fa wcount +bytes of data that are taken from the buffer pointed to by +.Fa wbuf , +then reads +.Fa rcount +bytes of data that from the device. +These data will be returned in the buffer pointed to by +.Fa rbuf . +.Pp +The following flags are allowed in +.Fa op : +.Pp +.Bd -literal -compact +SMB_TRANS_NOSTOP Do not send STOP at end +SMB_TRANS_NOCMD Ignore cmd field (do not tx) +SMB_TRANS_NOCNT Do not tx or rx count field +SMB_TRANS_7BIT Change address mode to 7-bit +SMB_TRANS_10BIT Change address mode to 10-bit +.Ed .El .Pp The .Xr read 2 and .Xr write 2 system calls are not implemented by this driver. .Sh ERRORS The .Xr ioctl 2 commands can cause the following driver-specific errors: .Bl -tag -width Er .It Bq Er ENXIO Device did not respond to selection. .It Bq Er EBUSY Device still in use. .It Bq Er ENODEV Operation not supported by device (not supposed to happen). .It Bq Er EINVAL General argument error. .It Bq Er EWOULDBLOCK SMBus transaction timed out. .El .Sh SEE ALSO .Xr ioctl 2 , .Xr smbus 4 .Sh HISTORY The .Nm manual page first appeared in .Fx 3.0 . .Sh AUTHORS This manual page was written by -.An Nicolas Souchu . +.An Nicolas Souchu +and extended by +.An Michael Gmelin Aq freebsd@grem.de +. Index: head/sys/dev/smbus/smb.c =================================================================== --- head/sys/dev/smbus/smb.c (revision 281984) +++ head/sys/dev/smbus/smb.c (revision 281985) @@ -1,293 +1,325 @@ /*- * Copyright (c) 1998, 2001 Nicolas Souchu * 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$ */ -#ifdef HAVE_KERNEL_OPTION_HEADERS -#include "opt_compat.h" -#endif - #include #include #include #include #include #include #include #include #include #include #include #include "smbus_if.h" #define BUFSIZE 1024 struct smb_softc { device_t sc_dev; int sc_count; /* >0 if device opened */ struct cdev *sc_devnode; struct mtx sc_lock; }; static void smb_identify(driver_t *driver, device_t parent); static int smb_probe(device_t); static int smb_attach(device_t); static int smb_detach(device_t); static devclass_t smb_devclass; static device_method_t smb_methods[] = { /* device interface */ DEVMETHOD(device_identify, smb_identify), DEVMETHOD(device_probe, smb_probe), DEVMETHOD(device_attach, smb_attach), DEVMETHOD(device_detach, smb_detach), /* smbus interface */ DEVMETHOD(smbus_intr, smbus_generic_intr), { 0, 0 } }; static driver_t smb_driver = { "smb", smb_methods, sizeof(struct smb_softc), }; static d_open_t smbopen; static d_close_t smbclose; static d_ioctl_t smbioctl; static struct cdevsw smb_cdevsw = { .d_version = D_VERSION, .d_flags = D_TRACKCLOSE, .d_open = smbopen, .d_close = smbclose, .d_ioctl = smbioctl, .d_name = "smb", }; static void smb_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "smb", -1) == NULL) BUS_ADD_CHILD(parent, 0, "smb", -1); } static int smb_probe(device_t dev) { - device_set_desc(dev, "SMBus generic I/O"); + if (smbus_get_addr(dev) != -1) + return (ENXIO); - return (0); + device_set_desc(dev, "SMBus generic I/O"); + return (BUS_PROBE_NOWILDCARD); } static int smb_attach(device_t dev) { struct smb_softc *sc = device_get_softc(dev); - + int unit; + + unit = device_get_unit(dev); sc->sc_dev = dev; - sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev), - UID_ROOT, GID_WHEEL, 0600, "smb%d", device_get_unit(dev)); + + sc->sc_devnode = make_dev(&smb_cdevsw, unit, UID_ROOT, GID_WHEEL, + 0600, "smb%d", unit); sc->sc_devnode->si_drv1 = sc; mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF); return (0); } static int smb_detach(device_t dev) { struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev); if (sc->sc_devnode) destroy_dev(sc->sc_devnode); mtx_destroy(&sc->sc_lock); return (0); } static int smbopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct smb_softc *sc = dev->si_drv1; mtx_lock(&sc->sc_lock); if (sc->sc_count != 0) { mtx_unlock(&sc->sc_lock); return (EBUSY); } sc->sc_count++; mtx_unlock(&sc->sc_lock); return (0); } static int smbclose(struct cdev *dev, int flags, int fmt, struct thread *td) { struct smb_softc *sc = dev->si_drv1; mtx_lock(&sc->sc_lock); KASSERT(sc->sc_count == 1, ("device not busy")); sc->sc_count--; mtx_unlock(&sc->sc_lock); return (0); } static int smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { char buf[SMB_MAXBLOCKSIZE]; device_t parent; struct smbcmd *s = (struct smbcmd *)data; struct smb_softc *sc = dev->si_drv1; device_t smbdev = sc->sc_dev; int error; - short w; - u_char count; - char c; + int unit; + u_char bcount; + /* + * If a specific slave device is being used, override any passed-in + * slave. + */ + unit = dev2unit(dev); + if (unit & 0x0400) + s->slave = unit & 0x03ff; + parent = device_get_parent(smbdev); /* Make sure that LSB bit is cleared. */ if (s->slave & 0x1) return (EINVAL); /* Allocate the bus. */ if ((error = smbus_request_bus(parent, smbdev, (flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR)))) return (error); switch (cmd) { case SMB_QUICK_WRITE: error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE)); break; case SMB_QUICK_READ: error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD)); break; case SMB_SENDB: error = smbus_error(smbus_sendb(parent, s->slave, s->cmd)); break; case SMB_RECVB: error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd)); break; case SMB_WRITEB: error = smbus_error(smbus_writeb(parent, s->slave, s->cmd, - s->data.byte)); + s->wdata.byte)); break; case SMB_WRITEW: error = smbus_error(smbus_writew(parent, s->slave, - s->cmd, s->data.word)); + s->cmd, s->wdata.word)); break; case SMB_READB: - if (s->data.byte_ptr) { - error = smbus_error(smbus_readb(parent, s->slave, - s->cmd, &c)); - if (error) - break; - error = copyout(&c, s->data.byte_ptr, - sizeof(*(s->data.byte_ptr))); + error = smbus_error(smbus_readb(parent, s->slave, s->cmd, + &s->rdata.byte)); + if (error) + break; + if (s->rbuf && s->rcount >= 1) { + error = copyout(&s->rdata.byte, s->rbuf, 1); + s->rcount = 1; } break; case SMB_READW: - if (s->data.word_ptr) { - error = smbus_error(smbus_readw(parent, s->slave, - s->cmd, &w)); - if (error == 0) { - error = copyout(&w, s->data.word_ptr, - sizeof(*(s->data.word_ptr))); - } + error = smbus_error(smbus_readw(parent, s->slave, s->cmd, + &s->rdata.word)); + if (error) + break; + if (s->rbuf && s->rcount >= 2) { + buf[0] = (u_char)s->rdata.word; + buf[1] = (u_char)(s->rdata.word >> 8); + error = copyout(buf, s->rbuf, 2); + s->rcount = 2; } break; case SMB_PCALL: - if (s->data.process.rdata) { - - error = smbus_error(smbus_pcall(parent, s->slave, s->cmd, - s->data.process.sdata, &w)); - if (error) - break; - error = copyout(&w, s->data.process.rdata, - sizeof(*(s->data.process.rdata))); + error = smbus_error(smbus_pcall(parent, s->slave, s->cmd, + s->wdata.word, &s->rdata.word)); + if (error) + break; + if (s->rbuf && s->rcount >= 2) { + buf[0] = (u_char)s->rdata.word; + buf[1] = (u_char)(s->rdata.word >> 8); + error = copyout(buf, s->rbuf, 2); + s->rcount = 2; } - + break; case SMB_BWRITE: - if (s->count && s->data.byte_ptr) { - if (s->count > SMB_MAXBLOCKSIZE) - s->count = SMB_MAXBLOCKSIZE; - error = copyin(s->data.byte_ptr, buf, s->count); - if (error) - break; - error = smbus_error(smbus_bwrite(parent, s->slave, - s->cmd, s->count, buf)); + if (s->wcount < 0) { + error = EINVAL; + break; } + if (s->wcount > SMB_MAXBLOCKSIZE) + s->wcount = SMB_MAXBLOCKSIZE; + if (s->wcount) + error = copyin(s->wbuf, buf, s->wcount); + if (error) + break; + error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd, + s->wcount, buf)); break; -#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6) - case SMB_OLD_BREAD: -#endif case SMB_BREAD: - if (s->count && s->data.byte_ptr) { - count = min(s->count, SMB_MAXBLOCKSIZE); - error = smbus_error(smbus_bread(parent, s->slave, - s->cmd, &count, buf)); - if (error) - break; - error = copyout(buf, s->data.byte_ptr, - min(count, s->count)); - s->count = count; + if (s->rcount < 0) { + error = EINVAL; + break; } + if (s->rcount > SMB_MAXBLOCKSIZE) + s->rcount = SMB_MAXBLOCKSIZE; + error = smbus_error(smbus_bread(parent, s->slave, s->cmd, + &bcount, buf)); + if (error) + break; + if (s->rcount > bcount) + s->rcount = bcount; + error = copyout(buf, s->rbuf, s->rcount); break; - + + case SMB_TRANS: + if (s->rcount < 0 || s->wcount < 0) { + error = EINVAL; + break; + } + if (s->rcount > SMB_MAXBLOCKSIZE) + s->rcount = SMB_MAXBLOCKSIZE; + if (s->wcount > SMB_MAXBLOCKSIZE) + s->wcount = SMB_MAXBLOCKSIZE; + if (s->wcount) + error = copyin(s->wbuf, buf, s->wcount); + if (error) + break; + error = smbus_error(smbus_trans(parent, s->slave, s->cmd, + s->op, buf, s->wcount, buf, s->rcount, &s->rcount)); + if (error == 0) + error = copyout(buf, s->rbuf, s->rcount); + break; default: error = ENOTTY; } smbus_release_bus(parent, smbdev); return (error); } DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0); MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); MODULE_VERSION(smb, 1); Index: head/sys/dev/smbus/smb.h =================================================================== --- head/sys/dev/smbus/smb.h (revision 281984) +++ head/sys/dev/smbus/smb.h (revision 281985) @@ -1,70 +1,77 @@ /*- * Copyright (c) 1998 Nicolas Souchu * 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$ * */ #ifndef __SMB_H #define __SMB_H #include struct smbcmd { - char cmd; - int count; - u_char slave; + u_char cmd; + u_char reserved; + u_short op; union { - char byte; - short word; - - char *byte_ptr; - short *word_ptr; - - struct { - short sdata; - short *rdata; - } process; - } data; + char byte; + char buf[2]; + short word; + } wdata; + union { + char byte; + char buf[2]; + short word; + } rdata; + int slave; + char *wbuf; /* use wdata if NULL */ + int wcount; + char *rbuf; /* use rdata if NULL */ + int rcount; }; /* * SMBus spec 2.0 says block transfers may be at most 32 bytes. + * We use SMBus for i2c as well, make the size limit something more + * reasonable. Keep in mind that a char buf array is declared on the + * kernel stack. */ -#define SMB_MAXBLOCKSIZE 32 +#define SMB_MAXBLOCKSIZE 1024 #define SMB_QUICK_WRITE _IOW('i', 1, struct smbcmd) #define SMB_QUICK_READ _IOW('i', 2, struct smbcmd) #define SMB_SENDB _IOW('i', 3, struct smbcmd) #define SMB_RECVB _IOWR('i', 4, struct smbcmd) #define SMB_WRITEB _IOW('i', 5, struct smbcmd) #define SMB_WRITEW _IOW('i', 6, struct smbcmd) #define SMB_READB _IOW('i', 7, struct smbcmd) #define SMB_READW _IOW('i', 8, struct smbcmd) #define SMB_PCALL _IOW('i', 9, struct smbcmd) #define SMB_BWRITE _IOW('i', 10, struct smbcmd) #define SMB_OLD_BREAD _IOW('i', 11, struct smbcmd) #define SMB_BREAD _IOWR('i', 11, struct smbcmd) +#define SMB_TRANS _IOWR('i', 12, struct smbcmd) #endif Index: head/sys/dev/smbus/smbconf.h =================================================================== --- head/sys/dev/smbus/smbconf.h (revision 281984) +++ head/sys/dev/smbus/smbconf.h (revision 281985) @@ -1,113 +1,142 @@ /*- * Copyright (c) 1998 Nicolas Souchu * 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$ */ #ifndef __SMBONF_H #define __SMBONF_H #include #define SMBPRI (PZERO+8) /* XXX sleep/wakeup queue priority */ #define n(flags) (~(flags) & (flags)) /* * How tsleep() is called in smb_request_bus(). */ #define SMB_DONTWAIT 0 #define SMB_NOINTR 0 #define SMB_WAIT 0x1 #define SMB_INTR 0x2 /* * callback index */ #define SMB_REQUEST_BUS 0x1 #define SMB_RELEASE_BUS 0x2 /* * SMB bus errors */ #define SMB_ENOERR 0x0 #define SMB_EBUSERR 0x1 #define SMB_ENOTSUPP 0x2 #define SMB_ENOACK 0x4 #define SMB_ECOLLI 0x8 #define SMB_EABORT 0x10 #define SMB_ETIMEOUT 0x20 #define SMB_EBUSY 0x40 #define SMB_EINVAL 0x100 /* * How Quick command is executed */ #define SMB_QWRITE 0x0 #define SMB_QREAD 0x1 /* + * smbus transction op with pass-thru capabilities + * + * This smbus function is capable of doing a smbus command transaction + * (read or write), and can be flagged to not issue the 'cmd' and/or + * issue or expect a count field as well as flagged for chaining (no STOP), + * which gives it an i2c pass-through capability. + * + * NOSTOP- Caller chaining transactions, do not issue STOP + * NOCMD- Do not transmit the command field + * NOCNT- Do not transmit (wr) or expect (rd) the count field + */ +#define SMB_TRANS_NOSTOP 0x0001 /* do not send STOP at end */ +#define SMB_TRANS_NOCMD 0x0002 /* ignore cmd field (do not tx) */ +#define SMB_TRANS_NOCNT 0x0004 /* do not tx or rx count field */ +#define SMB_TRANS_7BIT 0x0008 /* change address mode to 7-bit */ +#define SMB_TRANS_10BIT 0x0010 /* change address mode to 10-bit */ +#define SMB_TRANS_NOREPORT 0x0020 /* do not report errors */ + +/* * ivars codes */ -#define SMBUS_IVAR_ADDR 0x1 /* slave address of the device */ +enum smbus_ivars { + SMBUS_IVAR_ADDR, /* slave address of the device */ +}; int smbus_request_bus(device_t, device_t, int); int smbus_release_bus(device_t, device_t); device_t smbus_alloc_bus(device_t); int smbus_error(int error); void smbus_intr(device_t, u_char, char low, char high, int error); -u_char smbus_get_addr(device_t); +#define SMBUS_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(smbus, var, SMBUS, ivar, type) +SMBUS_ACCESSOR(addr, ADDR, int) + +#undef SMBUS_ACCESSOR + extern driver_t smbus_driver; extern devclass_t smbus_devclass; #define smbus_quick(bus,slave,how) \ (SMBUS_QUICK(device_get_parent(bus), slave, how)) #define smbus_sendb(bus,slave,byte) \ (SMBUS_SENDB(device_get_parent(bus), slave, byte)) #define smbus_recvb(bus,slave,byte) \ (SMBUS_RECVB(device_get_parent(bus), slave, byte)) #define smbus_writeb(bus,slave,cmd,byte) \ (SMBUS_WRITEB(device_get_parent(bus), slave, cmd, byte)) #define smbus_writew(bus,slave,cmd,word) \ (SMBUS_WRITEW(device_get_parent(bus), slave, cmd, word)) #define smbus_readb(bus,slave,cmd,byte) \ (SMBUS_READB(device_get_parent(bus), slave, cmd, byte)) #define smbus_readw(bus,slave,cmd,word) \ (SMBUS_READW(device_get_parent(bus), slave, cmd, word)) #define smbus_pcall(bus,slave,cmd,sdata,rdata) \ (SMBUS_PCALL(device_get_parent(bus), slave, cmd, sdata, rdata)) #define smbus_bwrite(bus,slave,cmd,count,buf) \ (SMBUS_BWRITE(device_get_parent(bus), slave, cmd, count, buf)) #define smbus_bread(bus,slave,cmd,count,buf) \ (SMBUS_BREAD(device_get_parent(bus), slave, cmd, count, buf)) +#define smbus_trans(bus,slave,cmd,op,wbuf,wcount,rbuf,rcount,actualp) \ + (SMBUS_TRANS(device_get_parent(bus), slave, cmd, op, \ + wbuf, wcount, rbuf, rcount, actualp)) #define SMBUS_MODVER 1 #define SMBUS_MINVER 1 #define SMBUS_MAXVER 1 #define SMBUS_PREFVER SMBUS_MODVER #endif Index: head/sys/dev/smbus/smbus.c =================================================================== --- head/sys/dev/smbus/smbus.c (revision 281984) +++ head/sys/dev/smbus/smbus.c (revision 281985) @@ -1,117 +1,203 @@ /*- * Copyright (c) 1998, 2001 Nicolas Souchu * 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. * * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include -#include +#include #include #include +#include "smbus_if.h" +#include "bus_if.h" + + /* * Autoconfiguration and support routines for System Management bus */ /* * Device methods */ static int smbus_probe(device_t); static int smbus_attach(device_t); static int smbus_detach(device_t); +static int smbus_child_location_str(device_t parent, device_t child, + char *buf, size_t buflen); +static int smbus_print_child(device_t parent, device_t child); +static void smbus_probe_device(device_t dev, u_char* addr); +static int smbus_read_ivar(device_t parent, device_t child, int which, + uintptr_t *result); + static device_method_t smbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, smbus_probe), DEVMETHOD(device_attach, smbus_attach), DEVMETHOD(device_detach, smbus_detach), /* bus interface */ DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_child_location_str, smbus_child_location_str), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_print_child, smbus_print_child), + DEVMETHOD(bus_read_ivar, smbus_read_ivar), DEVMETHOD_END }; driver_t smbus_driver = { "smbus", smbus_methods, sizeof(struct smbus_softc), }; devclass_t smbus_devclass; /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int smbus_probe(device_t dev) { device_set_desc(dev, "System Management Bus"); return (0); } static int smbus_attach(device_t dev) { struct smbus_softc *sc = device_get_softc(dev); + unsigned char addr; mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF); bus_generic_probe(dev); + for (addr = SMBUS_ADDR_MIN; addr < SMBUS_ADDR_MAX; ++addr) { + sc->addrs[addr] = addr; + smbus_probe_device(dev, &sc->addrs[addr]); + } bus_generic_attach(dev); return (0); } static int smbus_detach(device_t dev) { struct smbus_softc *sc = device_get_softc(dev); int error; error = bus_generic_detach(dev); if (error) return (error); mtx_destroy(&sc->lock); return (0); } void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err) { +} + +static void +smbus_probe_device(device_t dev, u_char* addr) +{ + device_t child; + int error; + u_char cmd; + u_char buf[2]; + + cmd = 0x01; + error = smbus_trans(dev, *addr, cmd, + SMB_TRANS_NOCNT | SMB_TRANS_NOREPORT, + NULL, 0, buf, 1, NULL); + if (error == 0) { + if (bootverbose) + device_printf(dev, "Probed address 0x%02x\n", *addr); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, addr); + } +} + +static int +smbus_child_location_str(device_t parent, device_t child, char *buf, + size_t buflen) +{ + unsigned char *addr; + + addr = device_get_ivars(child); + if (addr) + snprintf(buf, buflen, "addr=0x%x", *addr); + else if (buflen) + buf[0] = 0; + return (0); +} + +static int +smbus_print_child(device_t parent, device_t child) +{ + unsigned char *addr; + int retval; + + addr = device_get_ivars(child); + retval = bus_print_child_header(parent, child); + if (addr) + retval += printf(" at addr 0x%x", *addr); + retval += bus_print_child_footer(parent, child); + + return (retval); +} + +static int +smbus_read_ivar(device_t parent, device_t child, int which, + uintptr_t *result) +{ + unsigned char *addr; + + addr = device_get_ivars(child); + switch (which) { + case SMBUS_IVAR_ADDR: + *result = (addr == NULL) ? -1 : *addr; + break; + default: + return (ENOENT); + } + return (0); } MODULE_VERSION(smbus, SMBUS_MODVER); Index: head/sys/dev/smbus/smbus.h =================================================================== --- head/sys/dev/smbus/smbus.h (revision 281984) +++ head/sys/dev/smbus/smbus.h (revision 281985) @@ -1,39 +1,43 @@ /*- * Copyright (c) 1998 Nicolas Souchu * 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$ * */ #ifndef __SMBUS_H #define __SMBUS_H +#define SMBUS_ADDR_MIN 0x10 +#define SMBUS_ADDR_MAX 0x70 + struct smbus_softc { device_t owner; /* smbus owner device structure */ struct mtx lock; + unsigned char addrs[SMBUS_ADDR_MAX]; }; void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err); #endif Index: head/sys/dev/smbus/smbus_if.m =================================================================== --- head/sys/dev/smbus/smbus_if.m (revision 281984) +++ head/sys/dev/smbus/smbus_if.m (revision 281985) @@ -1,151 +1,168 @@ #- # Copyright (c) 1998 Nicolas Souchu # 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$ # #include INTERFACE smbus; # # Interpret interrupt # METHOD void intr { device_t dev; u_char devaddr; char low; char high; int error; }; # # smbus callback # METHOD int callback { device_t dev; int index; void *data; }; # # Quick command # METHOD int quick { device_t dev; u_char slave; int how; }; # # Send Byte command # METHOD int sendb { device_t dev; u_char slave; char byte; }; # # Receive Byte command # METHOD int recvb { device_t dev; u_char slave; char *byte; }; # # Write Byte command # METHOD int writeb { device_t dev; u_char slave; char cmd; char byte; }; # # Write Word command # METHOD int writew { device_t dev; u_char slave; char cmd; short word; }; # # Read Byte command # METHOD int readb { device_t dev; u_char slave; char cmd; char *byte; }; # # Read Word command # METHOD int readw { device_t dev; u_char slave; char cmd; short *word; }; # # Process Call command # METHOD int pcall { device_t dev; u_char slave; char cmd; short sdata; short *rdata; }; # # Block Write command # METHOD int bwrite { device_t dev; u_char slave; char cmd; u_char count; char *buf; }; # # Block Read command # METHOD int bread { device_t dev; u_char slave; char cmd; u_char *count; char *buf; }; + +# +# SMB roll-up transaction with flags that also allow it to be +# used for (mostly) i2c pass-through and with 10-bit addresses. +# This function can be used to roll-up all of the above functions. +# +METHOD int trans { + device_t dev; + int slave; + char cmd; + int op; + char *wbuf; + int wcount; + char *rbuf; + int rcount; + int *actualp; +}; Index: head/usr.sbin/smbmsg/smbmsg.c =================================================================== --- head/usr.sbin/smbmsg/smbmsg.c (revision 281984) +++ head/usr.sbin/smbmsg/smbmsg.c (revision 281985) @@ -1,344 +1,347 @@ /*- * 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 int do_io(void); static int getnum(const char *s); static void probe_i2c(void); static void usage(void); 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; + c.rbuf = ibuf; + c.rcount = iflag; 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]; + c.wdata.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; + c.rbuf = (char*) &iword; + c.rcount = iflag; 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; + c.wdata.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; + c.wdata.word = oword; + c.rbuf = (char*) &iword; + c.rcount = iflag; 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; + c.rbuf = ibuf; + c.rcount = 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; + c.wbuf = obuf; + c.wcount = 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); }