aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/uhid.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-28 14:30:16 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-28 14:30:16 -0700
commit884316deb4c9fdf9becfa31831a9e40717e3026c (patch)
tree9c0af0dd0ab659acd24c00f69c89b04058fe8f47 /drivers/hid/uhid.c
parent69c4289449b954f87ce33904bbb1b27dc075dcfa (diff)
parent8c2f421c1f0fa7768767ecaad497aa676fc9015a (diff)
downloadkernel_replicant_linux-884316deb4c9fdf9becfa31831a9e40717e3026c.tar.gz
kernel_replicant_linux-884316deb4c9fdf9becfa31831a9e40717e3026c.tar.bz2
kernel_replicant_linux-884316deb4c9fdf9becfa31831a9e40717e3026c.zip
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: - new hid-alps driver for ALPS Touchpad-Stick device, from Masaki Ota - much improved and generalized HID led handling, and merge of specialized hid-thingm driver into this generic hid-led one, from Heiner Kallweit - i2c-hid power management improvements from Fu Zhonghui and Guohua Zhong - uhid initialization race fix from Roderick Colenbrander * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (21 commits) HID: add usb device id for Apple Magic Keyboard HID: hid-led: fix Delcom support on big endian systems HID: hid-led: add support for Greynut Luxafor HID: hid-led: add support for Delcom Visual Signal Indicator G2 HID: hid-led: remove report id from struct hidled_config HID: alps: a few cleanups HID: remove ThingM blink(1) driver HID: hid-led: add support for ThingM blink(1) HID: hid-led: add support for reading from LED devices HID: hid-led: add support for devices with multiple independent LEDs HID: i2c-hid: set power sleep before shutdown HID: alps: match alps devices in core HID: thingm: simplify debug output code HID: alps: pass correct sizes to hid_hw_raw_request() HID: alps: struct u1_dev *priv is internal to the driver HID: add Alps I2C HID Touchpad-Stick support HID: led: fix config usb: misc: remove outdated USB LED driver HID: migrate USB LED driver from usb misc to hid HID: i2c_hid: enable i2c-hid devices to suspend/resume asynchronously ...
Diffstat (limited to 'drivers/hid/uhid.c')
-rw-r--r--drivers/hid/uhid.c33
1 files changed, 24 insertions, 9 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 16b6f11a0700..99ec3ff7563b 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -51,10 +51,26 @@ struct uhid_device {
u32 report_id;
u32 report_type;
struct uhid_event report_buf;
+ struct work_struct worker;
};
static struct miscdevice uhid_misc;
+static void uhid_device_add_worker(struct work_struct *work)
+{
+ struct uhid_device *uhid = container_of(work, struct uhid_device, worker);
+ int ret;
+
+ ret = hid_add_device(uhid->hid);
+ if (ret) {
+ hid_err(uhid->hid, "Cannot register HID device: error %d\n", ret);
+
+ hid_destroy_device(uhid->hid);
+ uhid->hid = NULL;
+ uhid->running = false;
+ }
+}
+
static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev)
{
__u8 newhead;
@@ -498,18 +514,14 @@ static int uhid_dev_create2(struct uhid_device *uhid,
uhid->hid = hid;
uhid->running = true;
- ret = hid_add_device(hid);
- if (ret) {
- hid_err(hid, "Cannot register HID device\n");
- goto err_hid;
- }
+ /* Adding of a HID device is done through a worker, to allow HID drivers
+ * which use feature requests during .probe to work, without they would
+ * be blocked on devlock, which is held by uhid_char_write.
+ */
+ schedule_work(&uhid->worker);
return 0;
-err_hid:
- hid_destroy_device(hid);
- uhid->hid = NULL;
- uhid->running = false;
err_free:
kfree(uhid->rd_data);
uhid->rd_data = NULL;
@@ -550,6 +562,8 @@ static int uhid_dev_destroy(struct uhid_device *uhid)
uhid->running = false;
wake_up_interruptible(&uhid->report_wait);
+ cancel_work_sync(&uhid->worker);
+
hid_destroy_device(uhid->hid);
kfree(uhid->rd_data);
@@ -612,6 +626,7 @@ static int uhid_char_open(struct inode *inode, struct file *file)
init_waitqueue_head(&uhid->waitq);
init_waitqueue_head(&uhid->report_wait);
uhid->running = false;
+ INIT_WORK(&uhid->worker, uhid_device_add_worker);
file->private_data = uhid;
nonseekable_open(inode, file);