diff options
author | Jiri Kosina <jkosina@suse.cz> | 2012-07-24 07:40:06 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-07-24 07:40:06 -0400 |
commit | c062c4d1de57789bf15f7641a24c429eeb8a1c6a (patch) | |
tree | dfd376034ea8458689c937dafa5e318892af82af /drivers/hid | |
parent | 929bd380b7aec9f9553b6509af86cdba25e334b8 (diff) | |
parent | adefb69b1b94df29ea2df05cd838c0e032b2c473 (diff) |
Merge branch 'uhid' into for-linus
Conflicts:
drivers/hid/Kconfig
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 21 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/uhid.c | 572 |
3 files changed, 594 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 00445bc3019c..fbf49503508d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -53,6 +53,27 @@ config HIDRAW | |||
53 | 53 | ||
54 | If unsure, say Y. | 54 | If unsure, say Y. |
55 | 55 | ||
56 | config UHID | ||
57 | tristate "User-space I/O driver support for HID subsystem" | ||
58 | depends on HID | ||
59 | default n | ||
60 | ---help--- | ||
61 | Say Y here if you want to provide HID I/O Drivers from user-space. | ||
62 | This allows to write I/O drivers in user-space and feed the data from | ||
63 | the device into the kernel. The kernel parses the HID reports, loads the | ||
64 | corresponding HID Device Driver or provides input devices on top of your | ||
65 | user-space device. | ||
66 | |||
67 | This driver cannot be used to parse HID-reports in user-space and write | ||
68 | special HID-drivers. You should use hidraw for that. | ||
69 | Instead, this driver allows to write the transport-layer driver in | ||
70 | user-space like USB-HID and Bluetooth-HID do in kernel-space. | ||
71 | |||
72 | If unsure, say N. | ||
73 | |||
74 | To compile this driver as a module, choose M here: the | ||
75 | module will be called uhid. | ||
76 | |||
56 | config HID_GENERIC | 77 | config HID_GENERIC |
57 | tristate "Generic HID driver" | 78 | tristate "Generic HID driver" |
58 | depends on HID | 79 | depends on HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 02fa93896951..f975485f88b2 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS | |||
8 | endif | 8 | endif |
9 | 9 | ||
10 | obj-$(CONFIG_HID) += hid.o | 10 | obj-$(CONFIG_HID) += hid.o |
11 | obj-$(CONFIG_UHID) += uhid.o | ||
11 | 12 | ||
12 | obj-$(CONFIG_HID_GENERIC) += hid-generic.o | 13 | obj-$(CONFIG_HID_GENERIC) += hid-generic.o |
13 | 14 | ||
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c new file mode 100644 index 000000000000..714cd8cc9579 --- /dev/null +++ b/drivers/hid/uhid.c | |||
@@ -0,0 +1,572 @@ | |||
1 | /* | ||
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" | ||
28 | #define UHID_BUFSIZE 32 | ||
29 | |||
30 | struct uhid_device { | ||
31 | struct mutex devlock; | ||
32 | bool running; | ||
33 | |||
34 | __u8 *rd_data; | ||
35 | uint rd_size; | ||
36 | |||
37 | struct hid_device *hid; | ||
38 | struct uhid_event input_buf; | ||
39 | |||
40 | wait_queue_head_t waitq; | ||
41 | spinlock_t qlock; | ||
42 | __u8 head; | ||
43 | __u8 tail; | ||
44 | struct uhid_event *outq[UHID_BUFSIZE]; | ||
45 | |||
46 | struct mutex report_lock; | ||
47 | wait_queue_head_t report_wait; | ||
48 | atomic_t report_done; | ||
49 | atomic_t report_id; | ||
50 | struct uhid_event report_buf; | ||
51 | }; | ||
52 | |||
53 | static struct miscdevice uhid_misc; | ||
54 | |||
55 | static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) | ||
56 | { | ||
57 | __u8 newhead; | ||
58 | |||
59 | newhead = (uhid->head + 1) % UHID_BUFSIZE; | ||
60 | |||
61 | if (newhead != uhid->tail) { | ||
62 | uhid->outq[uhid->head] = ev; | ||
63 | uhid->head = newhead; | ||
64 | wake_up_interruptible(&uhid->waitq); | ||
65 | } else { | ||
66 | hid_warn(uhid->hid, "Output queue is full\n"); | ||
67 | kfree(ev); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static int uhid_queue_event(struct uhid_device *uhid, __u32 event) | ||
72 | { | ||
73 | unsigned long flags; | ||
74 | struct uhid_event *ev; | ||
75 | |||
76 | ev = kzalloc(sizeof(*ev), GFP_KERNEL); | ||
77 | if (!ev) | ||
78 | return -ENOMEM; | ||
79 | |||
80 | ev->type = event; | ||
81 | |||
82 | spin_lock_irqsave(&uhid->qlock, flags); | ||
83 | uhid_queue(uhid, ev); | ||
84 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int uhid_hid_start(struct hid_device *hid) | ||
90 | { | ||
91 | struct uhid_device *uhid = hid->driver_data; | ||
92 | |||
93 | return uhid_queue_event(uhid, UHID_START); | ||
94 | } | ||
95 | |||
96 | static void uhid_hid_stop(struct hid_device *hid) | ||
97 | { | ||
98 | struct uhid_device *uhid = hid->driver_data; | ||
99 | |||
100 | hid->claimed = 0; | ||
101 | uhid_queue_event(uhid, UHID_STOP); | ||
102 | } | ||
103 | |||
104 | static int uhid_hid_open(struct hid_device *hid) | ||
105 | { | ||
106 | struct uhid_device *uhid = hid->driver_data; | ||
107 | |||
108 | return uhid_queue_event(uhid, UHID_OPEN); | ||
109 | } | ||
110 | |||
111 | static void uhid_hid_close(struct hid_device *hid) | ||
112 | { | ||
113 | struct uhid_device *uhid = hid->driver_data; | ||
114 | |||
115 | uhid_queue_event(uhid, UHID_CLOSE); | ||
116 | } | ||
117 | |||
118 | static int uhid_hid_input(struct input_dev *input, unsigned int type, | ||
119 | unsigned int code, int value) | ||
120 | { | ||
121 | struct hid_device *hid = input_get_drvdata(input); | ||
122 | struct uhid_device *uhid = hid->driver_data; | ||
123 | unsigned long flags; | ||
124 | struct uhid_event *ev; | ||
125 | |||
126 | ev = kzalloc(sizeof(*ev), GFP_ATOMIC); | ||
127 | if (!ev) | ||
128 | return -ENOMEM; | ||
129 | |||
130 | ev->type = UHID_OUTPUT_EV; | ||
131 | ev->u.output_ev.type = type; | ||
132 | ev->u.output_ev.code = code; | ||
133 | ev->u.output_ev.value = value; | ||
134 | |||
135 | spin_lock_irqsave(&uhid->qlock, flags); | ||
136 | uhid_queue(uhid, ev); | ||
137 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int uhid_hid_parse(struct hid_device *hid) | ||
143 | { | ||
144 | struct uhid_device *uhid = hid->driver_data; | ||
145 | |||
146 | return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); | ||
147 | } | ||
148 | |||
149 | static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, | ||
150 | __u8 *buf, size_t count, unsigned char rtype) | ||
151 | { | ||
152 | struct uhid_device *uhid = hid->driver_data; | ||
153 | __u8 report_type; | ||
154 | struct uhid_event *ev; | ||
155 | unsigned long flags; | ||
156 | int ret; | ||
157 | size_t uninitialized_var(len); | ||
158 | struct uhid_feature_answer_req *req; | ||
159 | |||
160 | if (!uhid->running) | ||
161 | return -EIO; | ||
162 | |||
163 | switch (rtype) { | ||
164 | case HID_FEATURE_REPORT: | ||
165 | report_type = UHID_FEATURE_REPORT; | ||
166 | break; | ||
167 | case HID_OUTPUT_REPORT: | ||
168 | report_type = UHID_OUTPUT_REPORT; | ||
169 | break; | ||
170 | case HID_INPUT_REPORT: | ||
171 | report_type = UHID_INPUT_REPORT; | ||
172 | break; | ||
173 | default: | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | ret = mutex_lock_interruptible(&uhid->report_lock); | ||
178 | if (ret) | ||
179 | return ret; | ||
180 | |||
181 | ev = kzalloc(sizeof(*ev), GFP_KERNEL); | ||
182 | if (!ev) { | ||
183 | ret = -ENOMEM; | ||
184 | goto unlock; | ||
185 | } | ||
186 | |||
187 | spin_lock_irqsave(&uhid->qlock, flags); | ||
188 | ev->type = UHID_FEATURE; | ||
189 | ev->u.feature.id = atomic_inc_return(&uhid->report_id); | ||
190 | ev->u.feature.rnum = rnum; | ||
191 | ev->u.feature.rtype = report_type; | ||
192 | |||
193 | atomic_set(&uhid->report_done, 0); | ||
194 | uhid_queue(uhid, ev); | ||
195 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
196 | |||
197 | ret = wait_event_interruptible_timeout(uhid->report_wait, | ||
198 | atomic_read(&uhid->report_done), 5 * HZ); | ||
199 | |||
200 | /* | ||
201 | * Make sure "uhid->running" is cleared on shutdown before | ||
202 | * "uhid->report_done" is set. | ||
203 | */ | ||
204 | smp_rmb(); | ||
205 | if (!ret || !uhid->running) { | ||
206 | ret = -EIO; | ||
207 | } else if (ret < 0) { | ||
208 | ret = -ERESTARTSYS; | ||
209 | } else { | ||
210 | spin_lock_irqsave(&uhid->qlock, flags); | ||
211 | req = &uhid->report_buf.u.feature_answer; | ||
212 | |||
213 | if (req->err) { | ||
214 | ret = -EIO; | ||
215 | } else { | ||
216 | ret = 0; | ||
217 | len = min(count, | ||
218 | min_t(size_t, req->size, UHID_DATA_MAX)); | ||
219 | memcpy(buf, req->data, len); | ||
220 | } | ||
221 | |||
222 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
223 | } | ||
224 | |||
225 | atomic_set(&uhid->report_done, 1); | ||
226 | |||
227 | unlock: | ||
228 | mutex_unlock(&uhid->report_lock); | ||
229 | return ret ? ret : len; | ||
230 | } | ||
231 | |||
232 | static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, | ||
233 | unsigned char report_type) | ||
234 | { | ||
235 | struct uhid_device *uhid = hid->driver_data; | ||
236 | __u8 rtype; | ||
237 | unsigned long flags; | ||
238 | struct uhid_event *ev; | ||
239 | |||
240 | switch (report_type) { | ||
241 | case HID_FEATURE_REPORT: | ||
242 | rtype = UHID_FEATURE_REPORT; | ||
243 | break; | ||
244 | case HID_OUTPUT_REPORT: | ||
245 | rtype = UHID_OUTPUT_REPORT; | ||
246 | break; | ||
247 | default: | ||
248 | return -EINVAL; | ||
249 | } | ||
250 | |||
251 | if (count < 1 || count > UHID_DATA_MAX) | ||
252 | return -EINVAL; | ||
253 | |||
254 | ev = kzalloc(sizeof(*ev), GFP_KERNEL); | ||
255 | if (!ev) | ||
256 | return -ENOMEM; | ||
257 | |||
258 | ev->type = UHID_OUTPUT; | ||
259 | ev->u.output.size = count; | ||
260 | ev->u.output.rtype = rtype; | ||
261 | memcpy(ev->u.output.data, buf, count); | ||
262 | |||
263 | spin_lock_irqsave(&uhid->qlock, flags); | ||
264 | uhid_queue(uhid, ev); | ||
265 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
266 | |||
267 | return count; | ||
268 | } | ||
269 | |||
270 | static struct hid_ll_driver uhid_hid_driver = { | ||
271 | .start = uhid_hid_start, | ||
272 | .stop = uhid_hid_stop, | ||
273 | .open = uhid_hid_open, | ||
274 | .close = uhid_hid_close, | ||
275 | .hidinput_input_event = uhid_hid_input, | ||
276 | .parse = uhid_hid_parse, | ||
277 | }; | ||
278 | |||
279 | static int uhid_dev_create(struct uhid_device *uhid, | ||
280 | const struct uhid_event *ev) | ||
281 | { | ||
282 | struct hid_device *hid; | ||
283 | int ret; | ||
284 | |||
285 | if (uhid->running) | ||
286 | return -EALREADY; | ||
287 | |||
288 | uhid->rd_size = ev->u.create.rd_size; | ||
289 | if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) | ||
290 | return -EINVAL; | ||
291 | |||
292 | uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); | ||
293 | if (!uhid->rd_data) | ||
294 | return -ENOMEM; | ||
295 | |||
296 | if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, | ||
297 | uhid->rd_size)) { | ||
298 | ret = -EFAULT; | ||
299 | goto err_free; | ||
300 | } | ||
301 | |||
302 | hid = hid_allocate_device(); | ||
303 | if (IS_ERR(hid)) { | ||
304 | ret = PTR_ERR(hid); | ||
305 | goto err_free; | ||
306 | } | ||
307 | |||
308 | strncpy(hid->name, ev->u.create.name, 127); | ||
309 | hid->name[127] = 0; | ||
310 | strncpy(hid->phys, ev->u.create.phys, 63); | ||
311 | hid->phys[63] = 0; | ||
312 | strncpy(hid->uniq, ev->u.create.uniq, 63); | ||
313 | hid->uniq[63] = 0; | ||
314 | |||
315 | hid->ll_driver = &uhid_hid_driver; | ||
316 | hid->hid_get_raw_report = uhid_hid_get_raw; | ||
317 | hid->hid_output_raw_report = uhid_hid_output_raw; | ||
318 | hid->bus = ev->u.create.bus; | ||
319 | hid->vendor = ev->u.create.vendor; | ||
320 | hid->product = ev->u.create.product; | ||
321 | hid->version = ev->u.create.version; | ||
322 | hid->country = ev->u.create.country; | ||
323 | hid->driver_data = uhid; | ||
324 | hid->dev.parent = uhid_misc.this_device; | ||
325 | |||
326 | uhid->hid = hid; | ||
327 | uhid->running = true; | ||
328 | |||
329 | ret = hid_add_device(hid); | ||
330 | if (ret) { | ||
331 | hid_err(hid, "Cannot register HID device\n"); | ||
332 | goto err_hid; | ||
333 | } | ||
334 | |||
335 | return 0; | ||
336 | |||
337 | err_hid: | ||
338 | hid_destroy_device(hid); | ||
339 | uhid->hid = NULL; | ||
340 | uhid->running = false; | ||
341 | err_free: | ||
342 | kfree(uhid->rd_data); | ||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | static int uhid_dev_destroy(struct uhid_device *uhid) | ||
347 | { | ||
348 | if (!uhid->running) | ||
349 | return -EINVAL; | ||
350 | |||
351 | /* clear "running" before setting "report_done" */ | ||
352 | uhid->running = false; | ||
353 | smp_wmb(); | ||
354 | atomic_set(&uhid->report_done, 1); | ||
355 | wake_up_interruptible(&uhid->report_wait); | ||
356 | |||
357 | hid_destroy_device(uhid->hid); | ||
358 | kfree(uhid->rd_data); | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) | ||
364 | { | ||
365 | if (!uhid->running) | ||
366 | return -EINVAL; | ||
367 | |||
368 | hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, | ||
369 | min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static int uhid_dev_feature_answer(struct uhid_device *uhid, | ||
375 | struct uhid_event *ev) | ||
376 | { | ||
377 | unsigned long flags; | ||
378 | |||
379 | if (!uhid->running) | ||
380 | return -EINVAL; | ||
381 | |||
382 | spin_lock_irqsave(&uhid->qlock, flags); | ||
383 | |||
384 | /* id for old report; drop it silently */ | ||
385 | if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) | ||
386 | goto unlock; | ||
387 | if (atomic_read(&uhid->report_done)) | ||
388 | goto unlock; | ||
389 | |||
390 | memcpy(&uhid->report_buf, ev, sizeof(*ev)); | ||
391 | atomic_set(&uhid->report_done, 1); | ||
392 | wake_up_interruptible(&uhid->report_wait); | ||
393 | |||
394 | unlock: | ||
395 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static int uhid_char_open(struct inode *inode, struct file *file) | ||
400 | { | ||
401 | struct uhid_device *uhid; | ||
402 | |||
403 | uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); | ||
404 | if (!uhid) | ||
405 | return -ENOMEM; | ||
406 | |||
407 | mutex_init(&uhid->devlock); | ||
408 | mutex_init(&uhid->report_lock); | ||
409 | spin_lock_init(&uhid->qlock); | ||
410 | init_waitqueue_head(&uhid->waitq); | ||
411 | init_waitqueue_head(&uhid->report_wait); | ||
412 | uhid->running = false; | ||
413 | atomic_set(&uhid->report_done, 1); | ||
414 | |||
415 | file->private_data = uhid; | ||
416 | nonseekable_open(inode, file); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int uhid_char_release(struct inode *inode, struct file *file) | ||
422 | { | ||
423 | struct uhid_device *uhid = file->private_data; | ||
424 | unsigned int i; | ||
425 | |||
426 | uhid_dev_destroy(uhid); | ||
427 | |||
428 | for (i = 0; i < UHID_BUFSIZE; ++i) | ||
429 | kfree(uhid->outq[i]); | ||
430 | |||
431 | kfree(uhid); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static ssize_t uhid_char_read(struct file *file, char __user *buffer, | ||
437 | size_t count, loff_t *ppos) | ||
438 | { | ||
439 | struct uhid_device *uhid = file->private_data; | ||
440 | int ret; | ||
441 | unsigned long flags; | ||
442 | size_t len; | ||
443 | |||
444 | /* they need at least the "type" member of uhid_event */ | ||
445 | if (count < sizeof(__u32)) | ||
446 | return -EINVAL; | ||
447 | |||
448 | try_again: | ||
449 | if (file->f_flags & O_NONBLOCK) { | ||
450 | if (uhid->head == uhid->tail) | ||
451 | return -EAGAIN; | ||
452 | } else { | ||
453 | ret = wait_event_interruptible(uhid->waitq, | ||
454 | uhid->head != uhid->tail); | ||
455 | if (ret) | ||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | ret = mutex_lock_interruptible(&uhid->devlock); | ||
460 | if (ret) | ||
461 | return ret; | ||
462 | |||
463 | if (uhid->head == uhid->tail) { | ||
464 | mutex_unlock(&uhid->devlock); | ||
465 | goto try_again; | ||
466 | } else { | ||
467 | len = min(count, sizeof(**uhid->outq)); | ||
468 | if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { | ||
469 | ret = -EFAULT; | ||
470 | } else { | ||
471 | kfree(uhid->outq[uhid->tail]); | ||
472 | uhid->outq[uhid->tail] = NULL; | ||
473 | |||
474 | spin_lock_irqsave(&uhid->qlock, flags); | ||
475 | uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; | ||
476 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | mutex_unlock(&uhid->devlock); | ||
481 | return ret ? ret : len; | ||
482 | } | ||
483 | |||
484 | static ssize_t uhid_char_write(struct file *file, const char __user *buffer, | ||
485 | size_t count, loff_t *ppos) | ||
486 | { | ||
487 | struct uhid_device *uhid = file->private_data; | ||
488 | int ret; | ||
489 | size_t len; | ||
490 | |||
491 | /* we need at least the "type" member of uhid_event */ | ||
492 | if (count < sizeof(__u32)) | ||
493 | return -EINVAL; | ||
494 | |||
495 | ret = mutex_lock_interruptible(&uhid->devlock); | ||
496 | if (ret) | ||
497 | return ret; | ||
498 | |||
499 | memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); | ||
500 | len = min(count, sizeof(uhid->input_buf)); | ||
501 | if (copy_from_user(&uhid->input_buf, buffer, len)) { | ||
502 | ret = -EFAULT; | ||
503 | goto unlock; | ||
504 | } | ||
505 | |||
506 | switch (uhid->input_buf.type) { | ||
507 | case UHID_CREATE: | ||
508 | ret = uhid_dev_create(uhid, &uhid->input_buf); | ||
509 | break; | ||
510 | case UHID_DESTROY: | ||
511 | ret = uhid_dev_destroy(uhid); | ||
512 | break; | ||
513 | case UHID_INPUT: | ||
514 | ret = uhid_dev_input(uhid, &uhid->input_buf); | ||
515 | break; | ||
516 | case UHID_FEATURE_ANSWER: | ||
517 | ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); | ||
518 | break; | ||
519 | default: | ||
520 | ret = -EOPNOTSUPP; | ||
521 | } | ||
522 | |||
523 | unlock: | ||
524 | mutex_unlock(&uhid->devlock); | ||
525 | |||
526 | /* return "count" not "len" to not confuse the caller */ | ||
527 | return ret ? ret : count; | ||
528 | } | ||
529 | |||
530 | static unsigned int uhid_char_poll(struct file *file, poll_table *wait) | ||
531 | { | ||
532 | struct uhid_device *uhid = file->private_data; | ||
533 | |||
534 | poll_wait(file, &uhid->waitq, wait); | ||
535 | |||
536 | if (uhid->head != uhid->tail) | ||
537 | return POLLIN | POLLRDNORM; | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static const struct file_operations uhid_fops = { | ||
543 | .owner = THIS_MODULE, | ||
544 | .open = uhid_char_open, | ||
545 | .release = uhid_char_release, | ||
546 | .read = uhid_char_read, | ||
547 | .write = uhid_char_write, | ||
548 | .poll = uhid_char_poll, | ||
549 | .llseek = no_llseek, | ||
550 | }; | ||
551 | |||
552 | static struct miscdevice uhid_misc = { | ||
553 | .fops = &uhid_fops, | ||
554 | .minor = MISC_DYNAMIC_MINOR, | ||
555 | .name = UHID_NAME, | ||
556 | }; | ||
557 | |||
558 | static int __init uhid_init(void) | ||
559 | { | ||
560 | return misc_register(&uhid_misc); | ||
561 | } | ||
562 | |||
563 | static void __exit uhid_exit(void) | ||
564 | { | ||
565 | misc_deregister(&uhid_misc); | ||
566 | } | ||
567 | |||
568 | module_init(uhid_init); | ||
569 | module_exit(uhid_exit); | ||
570 | MODULE_LICENSE("GPL"); | ||
571 | MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); | ||
572 | MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); | ||