mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
Allow iic bridges to support a generalized transfer, rather than
forcing all transfers to do the start read/write stop by hand. Some smart bridges prefer this sort of operation, and this allows us to support their features more easily. When bridges don't support it, we fall back to using the old-style opertaions. Expand the ioctl interface to expose this function. Unlike the old-style interface, this interface is thread safe, even on old bridges.
This commit is contained in:
parent
a9a5ae2d62
commit
d7fac9732b
7 changed files with 117 additions and 12 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue