HID: uhid: add internal message buffer
[linux-2.6.git] / drivers / hid / uhid.c
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 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 };
39
40 static struct miscdevice uhid_misc;
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
76 static int uhid_char_open(struct inode *inode, struct file *file)
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
90         return 0;
91 }
92
93 static int uhid_char_release(struct inode *inode, struct file *file)
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
103         return 0;
104 }
105
106 static ssize_t uhid_char_read(struct file *file, char __user *buffer,
107                                 size_t count, loff_t *ppos)
108 {
109         return 0;
110 }
111
112 static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
113                                 size_t count, loff_t *ppos)
114 {
115         return 0;
116 }
117
118 static unsigned int uhid_char_poll(struct file *file, poll_table *wait)
119 {
120         return 0;
121 }
122
123 static const struct file_operations uhid_fops = {
124         .owner          = THIS_MODULE,
125         .open           = uhid_char_open,
126         .release        = uhid_char_release,
127         .read           = uhid_char_read,
128         .write          = uhid_char_write,
129         .poll           = uhid_char_poll,
130         .llseek         = no_llseek,
131 };
132
133 static struct miscdevice uhid_misc = {
134         .fops           = &uhid_fops,
135         .minor          = MISC_DYNAMIC_MINOR,
136         .name           = UHID_NAME,
137 };
138
139 static int __init uhid_init(void)
140 {
141         return misc_register(&uhid_misc);
142 }
143
144 static void __exit uhid_exit(void)
145 {
146         misc_deregister(&uhid_misc);
147 }
148
149 module_init(uhid_init);
150 module_exit(uhid_exit);
151 MODULE_LICENSE("GPL");
152 MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
153 MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");