aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@googlemail.com>2012-06-10 09:16:16 -0400
committerJiri Kosina <jkosina@suse.cz>2012-06-18 07:42:00 -0400
commitd937ae5fae17e63aaa97f029be221a6516b25475 (patch)
tree2ef5c99a73fe3d9c9ef2e85bdf72d0807b170d5b /drivers/hid
parent1f9dec1e0164b48da9b268a02197f38caa69b118 (diff)
HID: uhid: implement read() on uhid devices
User-space can use read() to get a single event from uhid devices. read() does never return multiple events. This allows us to extend the event structure and still keep backwards compatibility. If user-space wants to get multiple events in one syscall, they should use the readv()/writev() syscalls which are supported by uhid. This introduces a new lock which helps us synchronizing simultaneous reads from user-space. We also correctly return -EINVAL/-EFAULT only on errors and retry the read() when some other thread captured the event faster than we did. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/uhid.c46
1 files changed, 45 insertions, 1 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index b1a477f8260c..93860826d629 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -28,6 +28,7 @@
28#define UHID_BUFSIZE 32 28#define UHID_BUFSIZE 32
29 29
30struct uhid_device { 30struct uhid_device {
31 struct mutex devlock;
31 struct hid_device *hid; 32 struct hid_device *hid;
32 33
33 wait_queue_head_t waitq; 34 wait_queue_head_t waitq;
@@ -81,6 +82,7 @@ static int uhid_char_open(struct inode *inode, struct file *file)
81 if (!uhid) 82 if (!uhid)
82 return -ENOMEM; 83 return -ENOMEM;
83 84
85 mutex_init(&uhid->devlock);
84 spin_lock_init(&uhid->qlock); 86 spin_lock_init(&uhid->qlock);
85 init_waitqueue_head(&uhid->waitq); 87 init_waitqueue_head(&uhid->waitq);
86 88
@@ -106,7 +108,49 @@ static int uhid_char_release(struct inode *inode, struct file *file)
106static ssize_t uhid_char_read(struct file *file, char __user *buffer, 108static ssize_t uhid_char_read(struct file *file, char __user *buffer,
107 size_t count, loff_t *ppos) 109 size_t count, loff_t *ppos)
108{ 110{
109 return 0; 111 struct uhid_device *uhid = file->private_data;
112 int ret;
113 unsigned long flags;
114 size_t len;
115
116 /* they need at least the "type" member of uhid_event */
117 if (count < sizeof(__u32))
118 return -EINVAL;
119
120try_again:
121 if (file->f_flags & O_NONBLOCK) {
122 if (uhid->head == uhid->tail)
123 return -EAGAIN;
124 } else {
125 ret = wait_event_interruptible(uhid->waitq,
126 uhid->head != uhid->tail);
127 if (ret)
128 return ret;
129 }
130
131 ret = mutex_lock_interruptible(&uhid->devlock);
132 if (ret)
133 return ret;
134
135 if (uhid->head == uhid->tail) {
136 mutex_unlock(&uhid->devlock);
137 goto try_again;
138 } else {
139 len = min(count, sizeof(**uhid->outq));
140 if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) {
141 ret = -EFAULT;
142 } else {
143 kfree(uhid->outq[uhid->tail]);
144 uhid->outq[uhid->tail] = NULL;
145
146 spin_lock_irqsave(&uhid->qlock, flags);
147 uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE;
148 spin_unlock_irqrestore(&uhid->qlock, flags);
149 }
150 }
151
152 mutex_unlock(&uhid->devlock);
153 return ret ? ret : len;
110} 154}
111 155
112static ssize_t uhid_char_write(struct file *file, const char __user *buffer, 156static ssize_t uhid_char_write(struct file *file, const char __user *buffer,