Index: sys/dev/smbus/smb.h =================================================================== --- sys/dev/smbus/smb.h +++ sys/dev/smbus/smb.h @@ -32,27 +32,33 @@ #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; /* max 255 bytes */ + char *rbuf; /* use rdata if NULL */ + int rcount; /* max 255 bytes */ }; /* * 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) @@ -66,5 +72,6 @@ #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: sys/dev/smbus/smb.c =================================================================== --- sys/dev/smbus/smb.c +++ sys/dev/smbus/smb.c @@ -50,6 +50,7 @@ struct smb_softc { device_t sc_dev; int sc_count; /* >0 if device opened */ + int sc_unit; struct cdev *sc_devnode; struct mtx sc_lock; }; @@ -106,17 +107,22 @@ { device_set_desc(dev, "SMBus generic I/O"); - return (0); + /* Allow other subclasses to override this driver. */ + return (BUS_PROBE_GENERIC); } 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_unit = unit; + + 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); @@ -174,9 +180,16 @@ 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 = sc->sc_unit; + if (unit & 0x0400) + s->slave = unit & 1023; parent = device_get_parent(smbdev); @@ -208,77 +221,95 @@ 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 (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 (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 (s->rbuf && s->rcount >= 2) { + char buf[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) + s->wcount = 0; + 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) + s->rcount = 0; + if (s->rcount > SMB_MAXBLOCKSIZE) + s->rcount = SMB_MAXBLOCKSIZE; + error = smbus_bread(parent, s->slave, s->cmd, &bcount, buf); + error = smbus_error(error); + 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->rcount = 0; + if (s->rcount > SMB_MAXBLOCKSIZE) + s->rcount = SMB_MAXBLOCKSIZE; + if (s->wcount < 0) + s->wcount = 0; + if (s->wcount > SMB_MAXBLOCKSIZE) + s->wcount = SMB_MAXBLOCKSIZE; + if (s->wcount) + error = copyin(s->wbuf, buf, s->wcount); + if (error) + break; + error = smbus_trans(parent, s->slave, s->cmd, s->op, buf, + s->wcount, buf, s->rcount, &s->rcount); + error = smbus_error(error); + if (error == 0) + error = copyout(buf, s->rbuf, s->rcount); break; - default: error = ENOTTY; } Index: sys/dev/smbus/smbconf.h =================================================================== --- sys/dev/smbus/smbconf.h +++ sys/dev/smbus/smbconf.h @@ -68,6 +68,25 @@ #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 */ @@ -104,6 +123,9 @@ (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 Index: sys/dev/smbus/smbus.h =================================================================== --- sys/dev/smbus/smbus.h +++ sys/dev/smbus/smbus.h @@ -29,9 +29,13 @@ #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); Index: sys/dev/smbus/smbus.c =================================================================== --- sys/dev/smbus/smbus.c +++ sys/dev/smbus/smbus.c @@ -38,6 +38,10 @@ #include #include +#include "smbus_if.h" +#include "bus_if.h" + + /* * Autoconfiguration and support routines for System Management bus */ @@ -49,6 +53,10 @@ static int smbus_attach(device_t); static int smbus_detach(device_t); +static device_t smbus_add_child(device_t parent, u_int order, + const char *name, int unit); +static void smbus_probe_device(device_t dev, u_char* addr); + static device_method_t smbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, smbus_probe), @@ -56,7 +64,8 @@ DEVMETHOD(device_detach, smbus_detach), /* bus interface */ - DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_add_child, smbus_add_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD_END }; @@ -87,9 +96,15 @@ 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); + + device_add_child(dev, NULL, -1); + 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); @@ -114,4 +129,35 @@ { } +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) { + device_printf(dev, "Probed address 0x%02x\n", *addr); + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, addr); + } +} + +static device_t +smbus_add_child(device_t parent, u_int order, const char *name, int unit) +{ + device_t child; + + device_printf(parent, "smbus_add_child unit %d at %d\n", unit, order); + child = device_add_child_ordered(parent, order, NULL, unit); + device_probe_and_attach(child); + + return (child); +} + MODULE_VERSION(smbus, SMBUS_MODVER); Index: sys/dev/smbus/smbus_if.m =================================================================== --- sys/dev/smbus/smbus_if.m +++ sys/dev/smbus/smbus_if.m @@ -149,3 +149,20 @@ 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: usr.sbin/smbmsg/smbmsg.c =================================================================== --- usr.sbin/smbmsg/smbmsg.c +++ usr.sbin/smbmsg/smbmsg.c @@ -163,7 +163,8 @@ } 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]); @@ -171,11 +172,12 @@ 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); @@ -183,15 +185,16 @@ 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); @@ -199,8 +202,8 @@ 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++) { @@ -212,8 +215,8 @@ 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)); }