diff options
author | David Herrmann <dh.herrmann@googlemail.com> | 2012-06-10 09:16:14 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-06-18 07:42:00 -0400 |
commit | ace3d8614ab0e6544f5f85921085b55b915fe9aa (patch) | |
tree | b666635c332af039a991d8391b4f24ac62a51182 | |
parent | 1ccd7a2a33f2b47e46c51f4501e9623a51d28090 (diff) |
HID: uhid: add internal message buffer
When receiving messages from the HID subsystem, we need to process them
and store them in an internal buffer so user-space can read() on the char
device to retrieve the messages.
This adds a static buffer for 32 messages to each uhid device. Each
message is dynamically allocated so the uhid_device structure does not get
too big.
uhid_queue() adds a message to the buffer. If the buffer is full, the
message is discarded. uhid_queue_event() is an helper for messages without
payload.
This also adds a public header: uhid.h. It contains the declarations for
the user-space API. It is built around "struct uhid_event" which contains
a type field which specifies the event type and each event can then add a
variable-length payload. For now, there is only a dummy event but later
patches will add new event types and payloads.
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/uhid.c | 65 | ||||
-rw-r--r-- | include/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/uhid.h | 33 |
3 files changed, 99 insertions, 0 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5b02d6cb0e60..05ef4b05a63e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c | |||
@@ -25,16 +25,81 @@ | |||
25 | #include <linux/wait.h> | 25 | #include <linux/wait.h> |
26 | 26 | ||
27 | #define UHID_NAME "uhid" | 27 | #define UHID_NAME "uhid" |
28 | #define UHID_BUFSIZE 32 | ||
29 | |||
30 | struct uhid_device { | ||
31 | struct hid_device *hid; | ||
32 | |||
33 | wait_queue_head_t waitq; | ||
34 | spinlock_t qlock; | ||
35 | __u8 head; | ||
36 | __u8 tail; | ||
37 | struct uhid_event *outq[UHID_BUFSIZE]; | ||
38 | }; | ||
28 | 39 | ||
29 | static struct miscdevice uhid_misc; | 40 | static struct miscdevice uhid_misc; |
30 | 41 | ||
42 | static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) | ||
43 | { | ||
44 | __u8 newhead; | ||
45 | |||
46 | newhead = (uhid->head + 1) % UHID_BUFSIZE; | ||
47 | |||
48 | if (newhead != uhid->tail) { | ||
49 | uhid->outq[uhid->head] = ev; | ||
50 | uhid->head = newhead; | ||
51 | wake_up_interruptible(&uhid->waitq); | ||
52 | } else { | ||
53 | hid_warn(uhid->hid, "Output queue is full\n"); | ||
54 | kfree(ev); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | static int uhid_queue_event(struct uhid_device *uhid, __u32 event) | ||
59 | { | ||
60 | unsigned long flags; | ||
61 | struct uhid_event *ev; | ||
62 | |||
63 | ev = kzalloc(sizeof(*ev), GFP_KERNEL); | ||
64 | if (!ev) | ||
65 | return -ENOMEM; | ||
66 | |||
67 | ev->type = event; | ||
68 | |||
69 | spin_lock_irqsave(&uhid->qlock, flags); | ||
70 | uhid_queue(uhid, ev); | ||
71 | spin_unlock_irqrestore(&uhid->qlock, flags); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
31 | static int uhid_char_open(struct inode *inode, struct file *file) | 76 | static int uhid_char_open(struct inode *inode, struct file *file) |
32 | { | 77 | { |
78 | struct uhid_device *uhid; | ||
79 | |||
80 | uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); | ||
81 | if (!uhid) | ||
82 | return -ENOMEM; | ||
83 | |||
84 | spin_lock_init(&uhid->qlock); | ||
85 | init_waitqueue_head(&uhid->waitq); | ||
86 | |||
87 | file->private_data = uhid; | ||
88 | nonseekable_open(inode, file); | ||
89 | |||
33 | return 0; | 90 | return 0; |
34 | } | 91 | } |
35 | 92 | ||
36 | static int uhid_char_release(struct inode *inode, struct file *file) | 93 | static int uhid_char_release(struct inode *inode, struct file *file) |
37 | { | 94 | { |
95 | struct uhid_device *uhid = file->private_data; | ||
96 | unsigned int i; | ||
97 | |||
98 | for (i = 0; i < UHID_BUFSIZE; ++i) | ||
99 | kfree(uhid->outq[i]); | ||
100 | |||
101 | kfree(uhid); | ||
102 | |||
38 | return 0; | 103 | return 0; |
39 | } | 104 | } |
40 | 105 | ||
diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 39737839ce29..8cdabecfbe27 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild | |||
@@ -373,6 +373,7 @@ header-y += tty.h | |||
373 | header-y += types.h | 373 | header-y += types.h |
374 | header-y += udf_fs_i.h | 374 | header-y += udf_fs_i.h |
375 | header-y += udp.h | 375 | header-y += udp.h |
376 | header-y += uhid.h | ||
376 | header-y += uinput.h | 377 | header-y += uinput.h |
377 | header-y += uio.h | 378 | header-y += uio.h |
378 | header-y += ultrasound.h | 379 | header-y += ultrasound.h |
diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 000000000000..16b786a2b18f --- /dev/null +++ b/include/linux/uhid.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #ifndef __UHID_H_ | ||
2 | #define __UHID_H_ | ||
3 | |||
4 | /* | ||
5 | * User-space I/O driver support for HID subsystem | ||
6 | * Copyright (c) 2012 David Herrmann | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the Free | ||
12 | * Software Foundation; either version 2 of the License, or (at your option) | ||
13 | * any later version. | ||
14 | */ | ||
15 | |||
16 | /* | ||
17 | * Public header for user-space communication. We try to keep every structure | ||
18 | * aligned but to be safe we also use __attribute__((__packed__)). Therefore, | ||
19 | * the communication should be ABI compatible even between architectures. | ||
20 | */ | ||
21 | |||
22 | #include <linux/input.h> | ||
23 | #include <linux/types.h> | ||
24 | |||
25 | enum uhid_event_type { | ||
26 | UHID_DUMMY, | ||
27 | }; | ||
28 | |||
29 | struct uhid_event { | ||
30 | __u32 type; | ||
31 | } __attribute__((__packed__)); | ||
32 | |||
33 | #endif /* __UHID_H_ */ | ||