aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-core.c
diff options
context:
space:
mode:
authorAndrew de los Reyes <adlr@chromium.org>2013-02-18 12:20:21 -0500
committerJiri Kosina <jkosina@suse.cz>2013-03-01 08:13:07 -0500
commitc849a6143bec520aff2a6646518b0d041402428b (patch)
tree95da329533d67acdd1ee0152a6212346062707a5 /drivers/hid/hid-core.c
parent48a732dfaa77a4dfec803aa8f248373998704f76 (diff)
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
1267 if (!hid) 1267 if (!hid)
1268 return -ENODEV; 1268 return -ENODEV;
1269 1269
1270 if (down_trylock(&hid->driver_lock)) 1270 if (down_trylock(&hid->driver_input_lock))
1271 return -EBUSY; 1271 return -EBUSY;
1272 1272
1273 if (!hid->driver) { 1273 if (!hid->driver) {
@@ -1324,7 +1324,7 @@ nomem:
1324 ret = hid_report_raw_event(hid, type, data, size, interrupt); 1324 ret = hid_report_raw_event(hid, type, data, size, interrupt);
1325 1325
1326unlock: 1326unlock:
1327 up(&hid->driver_lock); 1327 up(&hid->driver_input_lock);
1328 return ret; 1328 return ret;
1329} 1329}
1330EXPORT_SYMBOL_GPL(hid_input_report); 1330EXPORT_SYMBOL_GPL(hid_input_report);
@@ -1845,6 +1845,11 @@ static int hid_device_probe(struct device *dev)
1845 1845
1846 if (down_interruptible(&hdev->driver_lock)) 1846 if (down_interruptible(&hdev->driver_lock))
1847 return -EINTR; 1847 return -EINTR;
1848 if (down_interruptible(&hdev->driver_input_lock)) {
1849 ret = -EINTR;
1850 goto unlock_driver_lock;
1851 }
1852 hdev->io_started = false;
1848 1853
1849 if (!hdev->driver) { 1854 if (!hdev->driver) {
1850 id = hid_match_device(hdev, hdrv); 1855 id = hid_match_device(hdev, hdrv);
@@ -1867,6 +1872,9 @@ static int hid_device_probe(struct device *dev)
1867 } 1872 }
1868 } 1873 }
1869unlock: 1874unlock:
1875 if (!hdev->io_started)
1876 up(&hdev->driver_input_lock);
1877unlock_driver_lock:
1870 up(&hdev->driver_lock); 1878 up(&hdev->driver_lock);
1871 return ret; 1879 return ret;
1872} 1880}
@@ -1875,9 +1883,15 @@ static int hid_device_remove(struct device *dev)
1875{ 1883{
1876 struct hid_device *hdev = container_of(dev, struct hid_device, dev); 1884 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
1877 struct hid_driver *hdrv; 1885 struct hid_driver *hdrv;
1886 int ret = 0;
1878 1887
1879 if (down_interruptible(&hdev->driver_lock)) 1888 if (down_interruptible(&hdev->driver_lock))
1880 return -EINTR; 1889 return -EINTR;
1890 if (down_interruptible(&hdev->driver_input_lock)) {
1891 ret = -EINTR;
1892 goto unlock_driver_lock;
1893 }
1894 hdev->io_started = false;
1881 1895
1882 hdrv = hdev->driver; 1896 hdrv = hdev->driver;
1883 if (hdrv) { 1897 if (hdrv) {
@@ -1889,8 +1903,11 @@ static int hid_device_remove(struct device *dev)
1889 hdev->driver = NULL; 1903 hdev->driver = NULL;
1890 } 1904 }
1891 1905
1906 if (!hdev->io_started)
1907 up(&hdev->driver_input_lock);
1908unlock_driver_lock:
1892 up(&hdev->driver_lock); 1909 up(&hdev->driver_lock);
1893 return 0; 1910 return ret;
1894} 1911}
1895 1912
1896static ssize_t modalias_show(struct device *dev, struct device_attribute *a, 1913static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
@@ -2329,6 +2346,7 @@ struct hid_device *hid_allocate_device(void)
2329 init_waitqueue_head(&hdev->debug_wait); 2346 init_waitqueue_head(&hdev->debug_wait);
2330 INIT_LIST_HEAD(&hdev->debug_list); 2347 INIT_LIST_HEAD(&hdev->debug_list);
2331 sema_init(&hdev->driver_lock, 1); 2348 sema_init(&hdev->driver_lock, 1);
2349 sema_init(&hdev->driver_input_lock, 1);
2332 2350
2333 return hdev; 2351 return hdev;
2334} 2352}