summaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-core.c
diff options
context:
space:
mode:
authorAndrew de los Reyes <adlr@chromium.org>2013-02-18 18:20:21 +0100
committerJiri Kosina <jkosina@suse.cz>2013-03-01 14:13:07 +0100
commitc849a6143bec520aff2a6646518b0d041402428b (patch)
tree95da329533d67acdd1ee0152a6212346062707a5 /drivers/hid/hid-core.c
parentMerge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jik... (diff)
downloadlinux-c849a6143bec520aff2a6646518b0d041402428b.tar.xz
linux-c849a6143bec520aff2a6646518b0d041402428b.zip
HID: Separate struct hid_device's driver_lock into two locks.
This patch separates struct hid_device's driver_lock into two. The goal is to allow hid device drivers to receive input during their probe() or remove() function calls. This is necessary because some drivers need to communicate with the device to determine parameters needed during probe (e.g., size of a multi-touch surface), and if possible, may perfer to communicate with a device on host-initiated disconnect (e.g., to put it into a low-power state). Historically, three functions used driver_lock: - hid_device_probe: blocks to acquire lock - hid_device_remove: blocks to acquire lock - hid_input_report: if locked returns -EBUSY, else acquires lock This patch adds another lock (driver_input_lock) which is used to block input from occurring. The lock behavior is now: - hid_device_probe: blocks to acq. driver_lock, then driver_input_lock - hid_device_remove: blocks to acq. driver_lock, then driver_input_lock - hid_input_report: if driver_input_lock locked returns -EBUSY, else acquires driver_input_lock This patch also adds two helper functions to be called during probe() or remove(): hid_device_io_start() and hid_device_io_stop(). These functions lock and unlock, respectively, driver_input_lock; they also make a note of whether they did so that hid-core knows if a driver has changed the lock state. This patch results in no behavior change for existing devices and drivers. However, during a probe() or remove() function call in a driver, that driver may now selectively call hid_device_io_start() to let input events come through, then optionally call hid_device_io_stop() to stop them. Signed-off-by: Andrew de los Reyes <adlr@chromium.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-core.c')
-rw-r--r--drivers/hid/hid-core.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index ff75cabf7393..680068c0c46a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1267,7 +1267,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
if (!hid)
return -ENODEV;
- if (down_trylock(&hid->driver_lock))
+ if (down_trylock(&hid->driver_input_lock))
return -EBUSY;
if (!hid->driver) {
@@ -1324,7 +1324,7 @@ nomem:
ret = hid_report_raw_event(hid, type, data, size, interrupt);
unlock:
- up(&hid->driver_lock);
+ up(&hid->driver_input_lock);
return ret;
}
EXPORT_SYMBOL_GPL(hid_input_report);
@@ -1845,6 +1845,11 @@ static int hid_device_probe(struct device *dev)
if (down_interruptible(&hdev->driver_lock))
return -EINTR;
+ if (down_interruptible(&hdev->driver_input_lock)) {
+ ret = -EINTR;
+ goto unlock_driver_lock;
+ }
+ hdev->io_started = false;
if (!hdev->driver) {
id = hid_match_device(hdev, hdrv);
@@ -1867,6 +1872,9 @@ static int hid_device_probe(struct device *dev)
}
}
unlock:
+ if (!hdev->io_started)
+ up(&hdev->driver_input_lock);
+unlock_driver_lock:
up(&hdev->driver_lock);
return ret;
}
@@ -1875,9 +1883,15 @@ static int hid_device_remove(struct device *dev)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct hid_driver *hdrv;
+ int ret = 0;
if (down_interruptible(&hdev->driver_lock))
return -EINTR;
+ if (down_interruptible(&hdev->driver_input_lock)) {
+ ret = -EINTR;
+ goto unlock_driver_lock;
+ }
+ hdev->io_started = false;
hdrv = hdev->driver;
if (hdrv) {
@@ -1889,8 +1903,11 @@ static int hid_device_remove(struct device *dev)
hdev->driver = NULL;
}
+ if (!hdev->io_started)
+ up(&hdev->driver_input_lock);
+unlock_driver_lock:
up(&hdev->driver_lock);
- return 0;
+ return ret;
}
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
@@ -2329,6 +2346,7 @@ struct hid_device *hid_allocate_device(void)
init_waitqueue_head(&hdev->debug_wait);
INIT_LIST_HEAD(&hdev->debug_list);
sema_init(&hdev->driver_lock, 1);
+ sema_init(&hdev->driver_input_lock, 1);
return hdev;
}