summaryrefslogtreecommitdiffstats
path: root/sys/dev/iicbus/iiconf.c
diff options
context:
space:
mode:
authorjah <jah@FreeBSD.org>2015-04-21 11:50:31 +0000
committerjah <jah@FreeBSD.org>2015-04-21 11:50:31 +0000
commit25e5e803e77bc9385fbd54fcaf14eae06251d6f8 (patch)
treebb835543b8a361d83645cb2c61c7e198c0c33f65 /sys/dev/iicbus/iiconf.c
parent1c8b5426f3f10c739d5a27e6db5e0a9f247224e8 (diff)
downloadFreeBSD-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.c71
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);
}
/*
OpenPOWER on IntegriCloud