blob: 0226ba3f5307009384e6755e10d0ca83092e2090 [file] [log] [blame]
David Herrmann1ccd7a22012-06-10 15:16:13 +02001/*
2 * User-space I/O driver support for HID subsystem
3 * Copyright (c) 2012 David Herrmann
4 */
5
6/*
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 */
12
13#include <linux/atomic.h>
14#include <linux/device.h>
15#include <linux/fs.h>
16#include <linux/hid.h>
17#include <linux/input.h>
18#include <linux/miscdevice.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/poll.h>
22#include <linux/sched.h>
23#include <linux/spinlock.h>
24#include <linux/uhid.h>
25#include <linux/wait.h>
26
27#define UHID_NAME "uhid"
David Herrmannace3d862012-06-10 15:16:14 +020028#define UHID_BUFSIZE 32
29
30struct uhid_device {
David Herrmannd937ae52012-06-10 15:16:16 +020031 struct mutex devlock;
David Herrmannd365c6c2012-06-10 15:16:18 +020032 bool running;
33
34 __u8 *rd_data;
35 uint rd_size;
36
David Herrmannace3d862012-06-10 15:16:14 +020037 struct hid_device *hid;
David Herrmann6664ef72012-06-10 15:16:17 +020038 struct uhid_event input_buf;
David Herrmannace3d862012-06-10 15:16:14 +020039
40 wait_queue_head_t waitq;
41 spinlock_t qlock;
42 __u8 head;
43 __u8 tail;
44 struct uhid_event *outq[UHID_BUFSIZE];
45};
David Herrmann1ccd7a22012-06-10 15:16:13 +020046
47static struct miscdevice uhid_misc;
48
David Herrmannace3d862012-06-10 15:16:14 +020049static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev)
50{
51 __u8 newhead;
52
53 newhead = (uhid->head + 1) % UHID_BUFSIZE;
54
55 if (newhead != uhid->tail) {
56 uhid->outq[uhid->head] = ev;
57 uhid->head = newhead;
58 wake_up_interruptible(&uhid->waitq);
59 } else {
60 hid_warn(uhid->hid, "Output queue is full\n");
61 kfree(ev);
62 }
63}
64
65static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
66{
67 unsigned long flags;
68 struct uhid_event *ev;
69
70 ev = kzalloc(sizeof(*ev), GFP_KERNEL);
71 if (!ev)
72 return -ENOMEM;
73
74 ev->type = event;
75
76 spin_lock_irqsave(&uhid->qlock, flags);
77 uhid_queue(uhid, ev);
78 spin_unlock_irqrestore(&uhid->qlock, flags);
79
80 return 0;
81}
82
David Herrmannd365c6c2012-06-10 15:16:18 +020083static int uhid_hid_start(struct hid_device *hid)
84{
David Herrmannec4b7de2012-06-10 15:16:21 +020085 struct uhid_device *uhid = hid->driver_data;
86
87 return uhid_queue_event(uhid, UHID_START);
David Herrmannd365c6c2012-06-10 15:16:18 +020088}
89
90static void uhid_hid_stop(struct hid_device *hid)
91{
David Herrmannec4b7de2012-06-10 15:16:21 +020092 struct uhid_device *uhid = hid->driver_data;
93
94 hid->claimed = 0;
95 uhid_queue_event(uhid, UHID_STOP);
David Herrmannd365c6c2012-06-10 15:16:18 +020096}
97
98static int uhid_hid_open(struct hid_device *hid)
99{
David Herrmanne7191472012-06-10 15:16:22 +0200100 struct uhid_device *uhid = hid->driver_data;
101
102 return uhid_queue_event(uhid, UHID_OPEN);
David Herrmannd365c6c2012-06-10 15:16:18 +0200103}
104
105static void uhid_hid_close(struct hid_device *hid)
106{
David Herrmanne7191472012-06-10 15:16:22 +0200107 struct uhid_device *uhid = hid->driver_data;
108
109 uhid_queue_event(uhid, UHID_CLOSE);
David Herrmannd365c6c2012-06-10 15:16:18 +0200110}
111
112static int uhid_hid_input(struct input_dev *input, unsigned int type,
113 unsigned int code, int value)
114{
115 return 0;
116}
117
118static int uhid_hid_parse(struct hid_device *hid)
119{
David Herrmann037c0612012-06-10 15:16:20 +0200120 struct uhid_device *uhid = hid->driver_data;
121
122 return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
David Herrmannd365c6c2012-06-10 15:16:18 +0200123}
124
125static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum,
126 __u8 *buf, size_t count, unsigned char rtype)
127{
128 return 0;
129}
130
131static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
132 unsigned char report_type)
133{
134 return 0;
135}
136
137static struct hid_ll_driver uhid_hid_driver = {
138 .start = uhid_hid_start,
139 .stop = uhid_hid_stop,
140 .open = uhid_hid_open,
141 .close = uhid_hid_close,
142 .hidinput_input_event = uhid_hid_input,
143 .parse = uhid_hid_parse,
144};
145
146static int uhid_dev_create(struct uhid_device *uhid,
147 const struct uhid_event *ev)
148{
149 struct hid_device *hid;
150 int ret;
151
152 if (uhid->running)
153 return -EALREADY;
154
155 uhid->rd_size = ev->u.create.rd_size;
156 if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
157 return -EINVAL;
158
159 uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
160 if (!uhid->rd_data)
161 return -ENOMEM;
162
163 if (copy_from_user(uhid->rd_data, ev->u.create.rd_data,
164 uhid->rd_size)) {
165 ret = -EFAULT;
166 goto err_free;
167 }
168
169 hid = hid_allocate_device();
170 if (IS_ERR(hid)) {
171 ret = PTR_ERR(hid);
172 goto err_free;
173 }
174
175 strncpy(hid->name, ev->u.create.name, 127);
176 hid->name[127] = 0;
177 strncpy(hid->phys, ev->u.create.phys, 63);
178 hid->phys[63] = 0;
179 strncpy(hid->uniq, ev->u.create.uniq, 63);
180 hid->uniq[63] = 0;
181
182 hid->ll_driver = &uhid_hid_driver;
183 hid->hid_get_raw_report = uhid_hid_get_raw;
184 hid->hid_output_raw_report = uhid_hid_output_raw;
185 hid->bus = ev->u.create.bus;
186 hid->vendor = ev->u.create.vendor;
187 hid->product = ev->u.create.product;
188 hid->version = ev->u.create.version;
189 hid->country = ev->u.create.country;
190 hid->driver_data = uhid;
191 hid->dev.parent = uhid_misc.this_device;
192
193 uhid->hid = hid;
194 uhid->running = true;
195
196 ret = hid_add_device(hid);
197 if (ret) {
198 hid_err(hid, "Cannot register HID device\n");
199 goto err_hid;
200 }
201
202 return 0;
203
204err_hid:
205 hid_destroy_device(hid);
206 uhid->hid = NULL;
207 uhid->running = false;
208err_free:
209 kfree(uhid->rd_data);
210 return ret;
211}
212
213static int uhid_dev_destroy(struct uhid_device *uhid)
214{
215 if (!uhid->running)
216 return -EINVAL;
217
218 uhid->running = false;
219
220 hid_destroy_device(uhid->hid);
221 kfree(uhid->rd_data);
222
223 return 0;
224}
225
David Herrmann5e87a362012-06-10 15:16:19 +0200226static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
227{
228 if (!uhid->running)
229 return -EINVAL;
230
231 hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data,
232 min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0);
233
234 return 0;
235}
236
David Herrmann1ccd7a22012-06-10 15:16:13 +0200237static int uhid_char_open(struct inode *inode, struct file *file)
238{
David Herrmannace3d862012-06-10 15:16:14 +0200239 struct uhid_device *uhid;
240
241 uhid = kzalloc(sizeof(*uhid), GFP_KERNEL);
242 if (!uhid)
243 return -ENOMEM;
244
David Herrmannd937ae52012-06-10 15:16:16 +0200245 mutex_init(&uhid->devlock);
David Herrmannace3d862012-06-10 15:16:14 +0200246 spin_lock_init(&uhid->qlock);
247 init_waitqueue_head(&uhid->waitq);
David Herrmannd365c6c2012-06-10 15:16:18 +0200248 uhid->running = false;
David Herrmannace3d862012-06-10 15:16:14 +0200249
250 file->private_data = uhid;
251 nonseekable_open(inode, file);
252
David Herrmann1ccd7a22012-06-10 15:16:13 +0200253 return 0;
254}
255
256static int uhid_char_release(struct inode *inode, struct file *file)
257{
David Herrmannace3d862012-06-10 15:16:14 +0200258 struct uhid_device *uhid = file->private_data;
259 unsigned int i;
260
David Herrmannd365c6c2012-06-10 15:16:18 +0200261 uhid_dev_destroy(uhid);
262
David Herrmannace3d862012-06-10 15:16:14 +0200263 for (i = 0; i < UHID_BUFSIZE; ++i)
264 kfree(uhid->outq[i]);
265
266 kfree(uhid);
267
David Herrmann1ccd7a22012-06-10 15:16:13 +0200268 return 0;
269}
270
271static ssize_t uhid_char_read(struct file *file, char __user *buffer,
272 size_t count, loff_t *ppos)
273{
David Herrmannd937ae52012-06-10 15:16:16 +0200274 struct uhid_device *uhid = file->private_data;
275 int ret;
276 unsigned long flags;
277 size_t len;
278
279 /* they need at least the "type" member of uhid_event */
280 if (count < sizeof(__u32))
281 return -EINVAL;
282
283try_again:
284 if (file->f_flags & O_NONBLOCK) {
285 if (uhid->head == uhid->tail)
286 return -EAGAIN;
287 } else {
288 ret = wait_event_interruptible(uhid->waitq,
289 uhid->head != uhid->tail);
290 if (ret)
291 return ret;
292 }
293
294 ret = mutex_lock_interruptible(&uhid->devlock);
295 if (ret)
296 return ret;
297
298 if (uhid->head == uhid->tail) {
299 mutex_unlock(&uhid->devlock);
300 goto try_again;
301 } else {
302 len = min(count, sizeof(**uhid->outq));
303 if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) {
304 ret = -EFAULT;
305 } else {
306 kfree(uhid->outq[uhid->tail]);
307 uhid->outq[uhid->tail] = NULL;
308
309 spin_lock_irqsave(&uhid->qlock, flags);
310 uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE;
311 spin_unlock_irqrestore(&uhid->qlock, flags);
312 }
313 }
314
315 mutex_unlock(&uhid->devlock);
316 return ret ? ret : len;
David Herrmann1ccd7a22012-06-10 15:16:13 +0200317}
318
319static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
320 size_t count, loff_t *ppos)
321{
David Herrmann6664ef72012-06-10 15:16:17 +0200322 struct uhid_device *uhid = file->private_data;
323 int ret;
324 size_t len;
325
326 /* we need at least the "type" member of uhid_event */
327 if (count < sizeof(__u32))
328 return -EINVAL;
329
330 ret = mutex_lock_interruptible(&uhid->devlock);
331 if (ret)
332 return ret;
333
334 memset(&uhid->input_buf, 0, sizeof(uhid->input_buf));
335 len = min(count, sizeof(uhid->input_buf));
336 if (copy_from_user(&uhid->input_buf, buffer, len)) {
337 ret = -EFAULT;
338 goto unlock;
339 }
340
341 switch (uhid->input_buf.type) {
David Herrmannd365c6c2012-06-10 15:16:18 +0200342 case UHID_CREATE:
343 ret = uhid_dev_create(uhid, &uhid->input_buf);
344 break;
345 case UHID_DESTROY:
346 ret = uhid_dev_destroy(uhid);
347 break;
David Herrmann5e87a362012-06-10 15:16:19 +0200348 case UHID_INPUT:
349 ret = uhid_dev_input(uhid, &uhid->input_buf);
350 break;
David Herrmann6664ef72012-06-10 15:16:17 +0200351 default:
352 ret = -EOPNOTSUPP;
353 }
354
355unlock:
356 mutex_unlock(&uhid->devlock);
357
358 /* return "count" not "len" to not confuse the caller */
359 return ret ? ret : count;
David Herrmann1ccd7a22012-06-10 15:16:13 +0200360}
361
362static unsigned int uhid_char_poll(struct file *file, poll_table *wait)
363{
David Herrmann1f9dec12012-06-10 15:16:15 +0200364 struct uhid_device *uhid = file->private_data;
365
366 poll_wait(file, &uhid->waitq, wait);
367
368 if (uhid->head != uhid->tail)
369 return POLLIN | POLLRDNORM;
370
David Herrmann1ccd7a22012-06-10 15:16:13 +0200371 return 0;
372}
373
374static const struct file_operations uhid_fops = {
375 .owner = THIS_MODULE,
376 .open = uhid_char_open,
377 .release = uhid_char_release,
378 .read = uhid_char_read,
379 .write = uhid_char_write,
380 .poll = uhid_char_poll,
381 .llseek = no_llseek,
382};
383
384static struct miscdevice uhid_misc = {
385 .fops = &uhid_fops,
386 .minor = MISC_DYNAMIC_MINOR,
387 .name = UHID_NAME,
388};
389
390static int __init uhid_init(void)
391{
392 return misc_register(&uhid_misc);
393}
394
395static void __exit uhid_exit(void)
396{
397 misc_deregister(&uhid_misc);
398}
399
400module_init(uhid_init);
401module_exit(uhid_exit);
402MODULE_LICENSE("GPL");
403MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
404MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");