diff options
-rw-r--r-- | sys/dev/iicbus/iic.c | 29 | ||||
-rw-r--r-- | sys/dev/iicbus/iic.h | 16 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbus.c | 23 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbus.h | 3 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbus_if.m | 10 | ||||
-rw-r--r-- | sys/dev/iicbus/iiconf.c | 42 | ||||
-rw-r--r-- | sys/dev/iicbus/iiconf.h | 6 |
7 files changed, 117 insertions, 12 deletions
diff --git a/sys/dev/iicbus/iic.c b/sys/dev/iicbus/iic.c index 28339fa..b50d2e0 100644 --- a/sys/dev/iicbus/iic.c +++ b/sys/dev/iicbus/iic.c @@ -240,8 +240,11 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t struct iic_softc *sc = IIC_SOFTC(minor(dev)); device_t parent = device_get_parent(iicdev); struct iiccmd *s = (struct iiccmd *)data; - int error, count; + struct iic_rdwr_data *d = (struct iic_rdwr_data *)data; + struct iic_msg *m; + int error, count, i; char *buf = NULL; + void **usrbufs = NULL; if (!sc) return (EINVAL); @@ -297,6 +300,30 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t error = copyout(buf, s->buf, s->count); break; + case I2CRDWR: + buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_TEMP, M_WAITOK); + usrbufs = malloc(sizeof(void *) * d->nmsgs, M_TEMP, M_ZERO | M_WAITOK); + error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs); + if (error) + break; + /* Allocate kernel buffers for userland data, copyin write data */ + for (i = 0; i < d->nmsgs; i++) { + m = &((struct iic_msg *)buf)[i]; + usrbufs[i] = m->buf; + m->buf = malloc(m->len, M_TEMP, M_WAITOK); + if (!(m->flags & IIC_M_RD)) + copyin(usrbufs[i], m->buf, m->len); + } + error = iicbus_transfer(parent, (struct iic_msg *)buf, d->nmsgs); + /* Copyout all read segments, free up kernel buffers */ + for (i = 0; i < d->nmsgs; i++) { + m = &((struct iic_msg *)buf)[i]; + if (!(m->flags & IIC_M_RD)) + copyout(m->buf, usrbufs[i], m->len); + free(m->buf, M_TEMP); + } + free(usrbufs, M_TEMP); + break; default: error = ENOTTY; } diff --git a/sys/dev/iicbus/iic.h b/sys/dev/iicbus/iic.h index 5d362c1..ed22d85 100644 --- a/sys/dev/iicbus/iic.h +++ b/sys/dev/iicbus/iic.h @@ -31,6 +31,16 @@ #include <sys/ioccom.h> +/* Designed to be compatible with linux's struct i2c_msg */ +struct iic_msg +{ + uint16_t slave; + uint16_t flags; +#define IIC_M_RD 0x0001 /* read vs write */ + uint16_t len; /* msg legnth */ + uint8_t * buf; +}; + struct iiccmd { u_char slave; int count; @@ -38,10 +48,16 @@ struct iiccmd { char *buf; }; +struct iic_rdwr_data { + struct iic_msg *msgs; + uint32_t nmsgs; +}; + #define I2CSTART _IOW('i', 1, struct iiccmd) /* start condition */ #define I2CSTOP _IO('i', 2) /* stop condition */ #define I2CRSTCARD _IOW('i', 3, struct iiccmd) /* reset the card */ #define I2CWRITE _IOW('i', 4, struct iiccmd) /* send data */ #define I2CREAD _IOW('i', 5, struct iiccmd) /* receive data */ +#define I2CRDWR _IOW('i', 6, struct iic_rdwr_data) /* General read/write interface */ #endif diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c index 6181698..d2f9ef0 100644 --- a/sys/dev/iicbus/iicbus.c +++ b/sys/dev/iicbus/iicbus.c @@ -45,7 +45,7 @@ __FBSDID("$FreeBSD$"); #define DEVTOIICBUS(dev) ((struct iicbus_device*)device_get_ivars(dev)) -static devclass_t iicbus_devclass; +devclass_t iicbus_devclass; /* See comments below for why auto-scanning is a bad idea. */ #define SCAN_IICBUS 0 @@ -72,7 +72,7 @@ static device_method_t iicbus_methods[] = { { 0, 0 } }; -static driver_t iicbus_driver = { +driver_t iicbus_driver = { "iicbus", iicbus_methods, sizeof(struct iicbus_softc), @@ -81,8 +81,8 @@ static driver_t iicbus_driver = { static int iicbus_probe(device_t dev) { - device_set_desc(dev, "Philips I2C bus"); + device_set_desc(dev, "Philips I2C bus"); return (0); } @@ -139,51 +139,52 @@ iicbus_attach(device_t dev) printf("\n"); #endif - /* attach any known device */ device_add_child(dev, "ic", -1); - device_add_child(dev, "iic", -1); device_add_child(dev, "iicsmb", -1); - +#if 0 + /* attach any known device */ + device_add_child(dev, "iic", -1); +#endif bus_generic_attach(dev); - return (0); } static int iicbus_detach(device_t dev) { + iicbus_reset(dev, IIC_FASTEST, 0, NULL); - bus_generic_detach(dev); - return (0); } static int iicbus_add_child(device_t dev, int order, const char *name, int unit) { - device_add_child_ordered(dev, order, name, unit); + device_add_child_ordered(dev, order, name, unit); bus_generic_attach(dev); - return (0); } int iicbus_generic_intr(device_t dev, int event, char *buf) { + return (0); } int iicbus_null_callback(device_t dev, int index, caddr_t data) { + return (0); } int iicbus_null_repeated_start(device_t dev, u_char addr) { + return (IIC_ENOTSUPP); } diff --git a/sys/dev/iicbus/iicbus.h b/sys/dev/iicbus/iicbus.h index fcf758b..f0c061c 100644 --- a/sys/dev/iicbus/iicbus.h +++ b/sys/dev/iicbus/iicbus.h @@ -38,4 +38,7 @@ struct iicbus_softc { extern int iicbus_generic_intr(device_t dev, int event, char *buf); +extern driver_t iicbus_driver; +extern devclass_t iicbus_devclass; + #endif diff --git a/sys/dev/iicbus/iicbus_if.m b/sys/dev/iicbus/iicbus_if.m index 068cf4d..7c1c537 100644 --- a/sys/dev/iicbus/iicbus_if.m +++ b/sys/dev/iicbus/iicbus_if.m @@ -27,6 +27,7 @@ # #include <sys/bus.h> +#include <dev/iicbus/iic.h> INTERFACE iicbus; @@ -105,3 +106,12 @@ METHOD int reset { u_char addr; u_char *oldaddr; }; + +# +# Generalized Read/Write interface +# +METHOD int transfer { + device_t dev; + struct iic_msg *msgs; + uint32_t nmsgs; +}; diff --git a/sys/dev/iicbus/iiconf.c b/sys/dev/iicbus/iiconf.c index 6753db9..da050b0 100644 --- a/sys/dev/iicbus/iiconf.c +++ b/sys/dev/iicbus/iiconf.c @@ -331,3 +331,45 @@ iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read) return (error); } + +/* + * iicbus_trasnfer() + * + * Do an aribtrary number of transfers on the iicbus. We pass these + * raw requests to the bridge driver. If the bridge driver supports + * them directly, then it manages all the details. If not, it can use + * the helper function iicbus_transfer_gen() which will do the + * transfers at a low level. + * + * Pointers passed in as part of iic_msg must be kernel pointers. + * Callers that have user addresses to manage must do so on their own. + */ +int +iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs) +{ + return (IICBUS_TRANSFER(device_get_parent(bus), msgs, nmsgs)); +} + +/* + * Generic version of iicbus_transfer that calls the appropriate + * routines to accomplish this. See note above about acceptable + * buffer addresses. + */ +int +iicbus_transfer_gen(device_t bus, struct iic_msg *msgs, uint32_t nmsgs) +{ + int i, error, max, lenread, lenwrote; + + for (i = 0, max = 0; i < nmsgs; i++) + if (max < msgs[i].len) + max = msgs[i].len; + for (i = 0, error = 0; i < nmsgs && error == 0; i++) { + if (msgs[i].flags & IIC_M_RD) + error = iicbus_block_read(bus, msgs[i].slave, + msgs[i].buf, msgs[i].len, &lenread); + else + error = iicbus_block_write(bus, msgs[i].slave, + msgs[i].buf, msgs[i].len, &lenwrote); + } + return (error); +} diff --git a/sys/dev/iicbus/iiconf.h b/sys/dev/iicbus/iiconf.h index 3a9e7e3..bbf4907 100644 --- a/sys/dev/iicbus/iiconf.h +++ b/sys/dev/iicbus/iiconf.h @@ -29,6 +29,8 @@ #define __IICONF_H #include <sys/queue.h> +#include <dev/iicbus/iic.h> + #define IICPRI (PZERO+8) /* XXX sleep/wakeup queue priority */ @@ -127,6 +129,10 @@ extern int iicbus_block_read(device_t, u_char, char *, int, int *); extern u_char iicbus_get_addr(device_t); +/* vectors of iic operations to pass to bridge */ +int iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); +int iicbus_transfer_gen(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); + #define IICBUS_MODVER 1 #define IICBUS_MINVER 1 #define IICBUS_MAXVER 1 |