diff options
author | jah <jah@FreeBSD.org> | 2015-04-21 11:50:31 +0000 |
---|---|---|
committer | jah <jah@FreeBSD.org> | 2015-04-21 11:50:31 +0000 |
commit | 25e5e803e77bc9385fbd54fcaf14eae06251d6f8 (patch) | |
tree | bb835543b8a361d83645cb2c61c7e198c0c33f65 /sys/dev/iicbus/iiconf.c | |
parent | 1c8b5426f3f10c739d5a27e6db5e0a9f247224e8 (diff) | |
download | FreeBSD-src-25e5e803e77bc9385fbd54fcaf14eae06251d6f8.zip FreeBSD-src-25e5e803e77bc9385fbd54fcaf14eae06251d6f8.tar.gz |
Fix numerous issues in iic(4) and iicbus(4):
--Allow multiple open iic fds by storing addressing state in cdevpriv
--Fix, as much as possible, the baked-in race conditions in the iic
ioctl interface by requesting bus ownership on I2CSTART, releasing it on
I2CSTOP/I2CRSTCARD, and requiring bus ownership by the current cdevpriv
to use the I/O ioctls
--Reduce internal iic buffer size and remove 1K read/write limit by
iteratively calling iicbus_read/iicbus_write
--Eliminate dynamic allocation in I2CWRITE/I2CREAD
--Move handling of I2CRDWR to separate function and improve error handling
--Add new I2CSADDR ioctl to store address in current cdevpriv so that
I2CSTART is not needed for read(2)/write(2) to work
--Redesign iicbus_request_bus() and iicbus_release_bus():
--iicbus_request_bus() no longer falls through if the bus is already
owned by the requesting device. Multiple threads on the same device may
want exclusive access. Also, iicbus_release_bus() was never
device-recursive anyway.
--Previously, if IICBUS_CALLBACK failed in iicbus_release_bus(), but
the following iicbus_poll() call succeeded, IICBUS_CALLBACK would not be
issued again
--Do not hold iicbus mtx during IICBUS_CALLBACK call. There are
several drivers that may sleep in IICBUS_CALLBACK, if IIC_WAIT is passed.
--Do not loop in iicbus_request_bus if IICBUS_CALLBACK returns
EWOULDBLOCK; instead pass that to the caller so that it can retry if so
desired.
Differential Revision: https://reviews.freebsd.org/D2140
Reviewed by: imp, jhb, loos
Approved by: kib (mentor)
Diffstat (limited to 'sys/dev/iicbus/iiconf.c')
-rw-r--r-- | sys/dev/iicbus/iiconf.c | 71 |
1 files changed, 39 insertions, 32 deletions
diff --git a/sys/dev/iicbus/iiconf.c b/sys/dev/iicbus/iiconf.c index 940760b..09edb39 100644 --- a/sys/dev/iicbus/iiconf.c +++ b/sys/dev/iicbus/iiconf.c @@ -71,7 +71,6 @@ iicbus_poll(struct iicbus_softc *sc, int how) default: return (EWOULDBLOCK); - break; } return (error); @@ -90,31 +89,32 @@ iicbus_request_bus(device_t bus, device_t dev, int how) struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error = 0; - /* first, ask the underlying layers if the request is ok */ IICBUS_LOCK(sc); - do { - error = IICBUS_CALLBACK(device_get_parent(bus), - IIC_REQUEST_BUS, (caddr_t)&how); - if (error) - error = iicbus_poll(sc, how); - } while (error == EWOULDBLOCK); - while (!error) { - if (sc->owner && sc->owner != dev) { - - error = iicbus_poll(sc, how); - } else { - sc->owner = dev; + while ((error == 0) && (sc->owner != NULL)) + error = iicbus_poll(sc, how); + + if (error == 0) { + sc->owner = dev; + /* + * Drop the lock around the call to the bus driver. + * This call should be allowed to sleep in the IIC_WAIT case. + * Drivers might also need to grab locks that would cause LOR + * if our lock is held. + */ + IICBUS_UNLOCK(sc); + /* Ask the underlying layers if the request is ok */ + error = IICBUS_CALLBACK(device_get_parent(bus), + IIC_REQUEST_BUS, (caddr_t)&how); + IICBUS_LOCK(sc); - IICBUS_UNLOCK(sc); - return (0); + if (error != 0) { + sc->owner = NULL; + wakeup_one(sc); } - - /* free any allocated resource */ - if (error) - IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, - (caddr_t)&how); } + + IICBUS_UNLOCK(sc); return (error); @@ -131,12 +131,6 @@ iicbus_release_bus(device_t bus, device_t dev) struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); int error; - /* first, ask the underlying layers if the release is ok */ - error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); - - if (error) - return (error); - IICBUS_LOCK(sc); if (sc->owner != dev) { @@ -144,13 +138,26 @@ iicbus_release_bus(device_t bus, device_t dev) return (EACCES); } - sc->owner = NULL; - - /* wakeup waiting processes */ - wakeup(sc); + /* + * Drop the lock around the call to the bus driver. + * This call should be allowed to sleep in the IIC_WAIT case. + * Drivers might also need to grab locks that would cause LOR + * if our lock is held. + */ IICBUS_UNLOCK(sc); + /* Ask the underlying layers if the release is ok */ + error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); - return (0); + if (error == 0) { + IICBUS_LOCK(sc); + sc->owner = NULL; + + /* wakeup a waiting thread */ + wakeup_one(sc); + IICBUS_UNLOCK(sc); + } + + return (error); } /* |