From b3e6cdfb71b60c2acc521cb807a955100081b7b8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:16 +0200 Subject: [PATCH] --- yaml --- r: 316068 b: refs/heads/master c: d937ae5fae17e63aaa97f029be221a6516b25475 h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/drivers/hid/uhid.c | 46 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/[refs] b/[refs] index e85aac3413dc..f72b0c2bff9a 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 1f9dec1e0164b48da9b268a02197f38caa69b118 +refs/heads/master: d937ae5fae17e63aaa97f029be221a6516b25475 diff --git a/trunk/drivers/hid/uhid.c b/trunk/drivers/hid/uhid.c index b1a477f8260c..93860826d629 100644 --- a/trunk/drivers/hid/uhid.c +++ b/trunk/drivers/hid/uhid.c @@ -28,6 +28,7 @@ #define UHID_BUFSIZE 32 struct uhid_device { + struct mutex devlock; struct hid_device *hid; wait_queue_head_t waitq; @@ -81,6 +82,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) if (!uhid) return -ENOMEM; + mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); @@ -106,7 +108,49 @@ static int uhid_char_release(struct inode *inode, struct file *file) static ssize_t uhid_char_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + unsigned long flags; + size_t len; + + /* they need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + +try_again: + if (file->f_flags & O_NONBLOCK) { + if (uhid->head == uhid->tail) + return -EAGAIN; + } else { + ret = wait_event_interruptible(uhid->waitq, + uhid->head != uhid->tail); + if (ret) + return ret; + } + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + if (uhid->head == uhid->tail) { + mutex_unlock(&uhid->devlock); + goto try_again; + } else { + len = min(count, sizeof(**uhid->outq)); + if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) { + ret = -EFAULT; + } else { + kfree(uhid->outq[uhid->tail]); + uhid->outq[uhid->tail] = NULL; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; + spin_unlock_irqrestore(&uhid->qlock, flags); + } + } + + mutex_unlock(&uhid->devlock); + return ret ? ret : len; } static ssize_t uhid_char_write(struct file *file, const char __user *buffer,