aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hidraw.c
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2007-05-14 03:57:40 -0400
committerJiri Kosina <jkosina@suse.cz>2007-10-14 08:47:26 -0400
commit86166b7bcda0bcb53525114fa1c87ac432be478e (patch)
tree1f6afc4c1c1d7a6dd88236f3c11fde61c1885b14 /drivers/hid/hidraw.c
parentefc493f9d5463d933a64a2758fbe6d9bb8300cbb (diff)
HID: add hidraw interface
hidraw is an interface that is going to obsolete hiddev one day. Many userland applications are using libusb instead of using kernel-provided hiddev interface. This is caused by various reasons - the HID parser in kernel doesn't handle all the HID hardware on the planet properly, some devices might require its own specific quirks/drivers, etc. hiddev interface tries to do its best to parse all the received reports properly, and presents only parsed usages into userspace. This is however often not enough, and that's the reason why many userland applications just don't use hiddev at all, and rather use libusb to read raw USB events and process them on their own. Another drawback of hiddev is that it is USB-specific. hidraw interface provides userspace readers with really raw HID reports, no matter what the low-level transport layer is (USB/BT), and gives the userland applications all the freedom to process the HID reports in a way they wish to. Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hidraw.c')
-rw-r--r--drivers/hid/hidraw.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
new file mode 100644
index 000000000000..20ebba1032b8
--- /dev/null
+++ b/drivers/hid/hidraw.c
@@ -0,0 +1,395 @@
1/*
2 * HID raw devices, giving access to raw HID events.
3 *
4 * In comparison to hiddev, this device does not process the
5 * hid events at all (no parsing, no lookups). This lets applications
6 * to work on raw hid events as they want to, and avoids a need to
7 * use a transport-specific userspace libhid/libusb libraries.
8 *
9 * Copyright (c) 2007 Jiri Kosina
10 */
11
12/*
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms and conditions of the GNU General Public License,
15 * version 2, as published by the Free Software Foundation.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include <linux/fs.h>
23#include <linux/module.h>
24#include <linux/errno.h>
25#include <linux/kernel.h>
26#include <linux/init.h>
27#include <linux/cdev.h>
28#include <linux/poll.h>
29#include <linux/device.h>
30#include <linux/major.h>
31#include <linux/hid.h>
32#include <linux/mutex.h>
33
34#include <linux/hidraw.h>
35
36static int hidraw_major;
37static struct cdev hidraw_cdev;
38static struct class *hidraw_class;
39static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
40static DEFINE_SPINLOCK(minors_lock);
41
42static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
43{
44 struct hidraw_list *list = file->private_data;
45 int ret = 0, len;
46 char *report;
47 DECLARE_WAITQUEUE(wait, current);
48
49 while (ret == 0) {
50
51 mutex_lock(&list->read_mutex);
52
53 if (list->head == list->tail) {
54 add_wait_queue(&list->hidraw->wait, &wait);
55 set_current_state(TASK_INTERRUPTIBLE);
56
57 while (list->head == list->tail) {
58 if (file->f_flags & O_NONBLOCK) {
59 ret = -EAGAIN;
60 break;
61 }
62 if (signal_pending(current)) {
63 ret = -ERESTARTSYS;
64 break;
65 }
66 if (!list->hidraw->exist) {
67 ret = -EIO;
68 break;
69 }
70
71 /* allow O_NONBLOCK to work well from other threads */
72 mutex_unlock(&list->read_mutex);
73 schedule();
74 mutex_lock(&list->read_mutex);
75 set_current_state(TASK_INTERRUPTIBLE);
76 }
77
78 set_current_state(TASK_RUNNING);
79 remove_wait_queue(&list->hidraw->wait, &wait);
80 }
81
82 if (ret)
83 goto out;
84
85 report = list->buffer[list->tail].value;
86 len = list->buffer[list->tail].len > count ?
87 count : list->buffer[list->tail].len;
88
89 if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
90 ret = -EFAULT;
91 goto out;
92 }
93 ret += len;
94
95 kfree(list->buffer[list->tail].value);
96 list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
97 }
98out:
99 mutex_unlock(&list->read_mutex);
100 return ret;
101}
102
103/* the first byte is expected to be a report number */
104static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
105{
106 unsigned int minor = iminor(file->f_path.dentry->d_inode);
107 struct hid_device *dev = hidraw_table[minor]->hid;
108 __u8 *buf;
109 int ret = 0;
110
111 if (!dev->hid_output_raw_report)
112 return -ENODEV;
113
114 if (count > HID_MIN_BUFFER_SIZE) {
115 printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
116 current->pid);
117 return -EINVAL;
118 }
119
120 if (count < 2) {
121 printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
122 current->pid);
123 return -EINVAL;
124 }
125
126 buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
127 if (!buf)
128 return -ENOMEM;
129
130 if (copy_from_user(buf, buffer, count)) {
131 ret = -EFAULT;
132 goto out;
133 }
134
135 ret = dev->hid_output_raw_report(dev, buf, count);
136out:
137 kfree(buf);
138 return ret;
139}
140
141static unsigned int hidraw_poll(struct file *file, poll_table *wait)
142{
143 struct hidraw_list *list = file->private_data;
144
145 poll_wait(file, &list->hidraw->wait, wait);
146 if (list->head != list->tail)
147 return POLLIN | POLLRDNORM;
148 if (!list->hidraw->exist)
149 return POLLERR | POLLHUP;
150 return 0;
151}
152
153static int hidraw_open(struct inode *inode, struct file *file)
154{
155 unsigned int minor = iminor(inode);
156 struct hidraw *dev;
157 struct hidraw_list *list;
158 int err = 0;
159
160 if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
161 err = -ENOMEM;
162 goto out;
163 }
164
165 spin_lock(&minors_lock);
166 if (!hidraw_table[minor]) {
167 printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
168 minor);
169 kfree(list);
170 err = -ENODEV;
171 goto out_unlock;
172 }
173
174 list->hidraw = hidraw_table[minor];
175 mutex_init(&list->read_mutex);
176 list_add_tail(&list->node, &hidraw_table[minor]->list);
177 file->private_data = list;
178
179 dev = hidraw_table[minor];
180 if (!dev->open++)
181 dev->hid->hid_open(dev->hid);
182
183out_unlock:
184 spin_unlock(&minors_lock);
185out:
186 return err;
187
188}
189
190static int hidraw_release(struct inode * inode, struct file * file)
191{
192 unsigned int minor = iminor(inode);
193 struct hidraw *dev;
194 struct hidraw_list *list = file->private_data;
195
196 if (!hidraw_table[minor]) {
197 printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
198 minor);
199 return -ENODEV;
200 }
201
202 list_del(&list->node);
203 dev = hidraw_table[minor];
204 if (!dev->open--) {
205 if (list->hidraw->exist)
206 dev->hid->hid_close(dev->hid);
207 else
208 kfree(list->hidraw);
209 }
210
211 return 0;
212}
213
214static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
215{
216 unsigned int minor = iminor(inode);
217 struct hidraw *dev = hidraw_table[minor];
218 void __user *user_arg = (void __user*) arg;
219
220 switch (cmd) {
221 case HIDIOCGRDESCSIZE:
222 if (put_user(dev->hid->rsize, (int __user *)arg))
223 return -EFAULT;
224 return 0;
225
226 case HIDIOCGRDESC:
227 {
228 __u32 len;
229
230 if (get_user(len, (int __user *)arg))
231 return -EFAULT;
232 if (copy_to_user(*((__u8 **)(user_arg +
233 sizeof(__u32))),
234 dev->hid->rdesc, len))
235 return -EFAULT;
236 return 0;
237 }
238 case HIDIOCGRAWINFO:
239 {
240 struct hidraw_devinfo dinfo;
241
242 dinfo.bustype = dev->hid->bus;
243 dinfo.vendor = dev->hid->vendor;
244 dinfo.product = dev->hid->product;
245 if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
246 return -EFAULT;
247
248 return 0;
249 }
250 default:
251 printk(KERN_EMERG "hidraw: unsupported ioctl() %x\n",
252 cmd);
253 }
254 return -EINVAL;
255}
256
257static const struct file_operations hidraw_ops = {
258 .owner = THIS_MODULE,
259 .read = hidraw_read,
260 .write = hidraw_write,
261 .poll = hidraw_poll,
262 .open = hidraw_open,
263 .release = hidraw_release,
264 .ioctl = hidraw_ioctl,
265};
266
267void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
268{
269 struct hidraw *dev = hid->hidraw;
270 struct hidraw_list *list;
271
272 list_for_each_entry(list, &dev->list, node) {
273 list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
274 list->buffer[list->head].len = len;
275 list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
276 kill_fasync(&list->fasync, SIGIO, POLL_IN);
277 }
278
279 wake_up_interruptible(&dev->wait);
280}
281EXPORT_SYMBOL_GPL(hidraw_report_event);
282
283int hidraw_connect(struct hid_device *hid)
284{
285 int minor, result = -EINVAL;
286 struct hidraw *dev;
287
288 /* TODO currently we accept any HID device. This should later
289 * probably be fixed to accept only those devices which provide
290 * non-input applications
291 */
292
293 if (!(dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL)))
294 return -1;
295
296 spin_lock(&minors_lock);
297
298 for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
299 if (hidraw_table[minor])
300 continue;
301 hidraw_table[minor] = dev;
302 result = 0;
303 break;
304 }
305
306 spin_unlock(&minors_lock);
307
308 if (result)
309 goto out;
310
311 dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor),
312 "%s%d", "hidraw", minor);
313
314 if (IS_ERR(dev->dev)) {
315 spin_lock(&minors_lock);
316 hidraw_table[minor] = NULL;
317 spin_unlock(&minors_lock);
318 result = PTR_ERR(dev->dev);
319 goto out;
320 }
321
322 init_waitqueue_head(&dev->wait);
323 INIT_LIST_HEAD(&dev->list);
324
325 dev->hid = hid;
326 dev->minor = minor;
327
328 dev->exist = 1;
329 hid->hidraw = dev;
330
331out:
332 return result;
333
334}
335EXPORT_SYMBOL_GPL(hidraw_connect);
336
337void hidraw_disconnect(struct hid_device *hid)
338{
339 struct hidraw *hidraw = hid->hidraw;
340
341 hidraw->exist = 0;
342
343 spin_lock(&minors_lock);
344 hidraw_table[hidraw->minor] = NULL;
345 spin_unlock(&minors_lock);
346
347 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
348
349 if (hidraw->open) {
350 hid->hid_close(hid);
351 wake_up_interruptible(&hidraw->wait);
352 } else {
353 kfree(hidraw);
354 }
355}
356EXPORT_SYMBOL_GPL(hidraw_disconnect);
357
358int __init hidraw_init(void)
359{
360 int result;
361 dev_t dev_id;
362
363 result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
364 HIDRAW_MAX_DEVICES, "hidraw");
365
366 hidraw_major = MAJOR(dev_id);
367
368 if (result < 0) {
369 printk(KERN_WARNING "hidraw: can't get major number\n");
370 result = 0;
371 goto out;
372 }
373
374 hidraw_class = class_create(THIS_MODULE, "hidraw");
375 if (IS_ERR(hidraw_class)) {
376 result = PTR_ERR(hidraw_class);
377 unregister_chrdev(hidraw_major, "hidraw");
378 goto out;
379 }
380
381 cdev_init(&hidraw_cdev, &hidraw_ops);
382 cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
383out:
384 return result;
385}
386
387void __exit hidraw_exit(void)
388{
389 dev_t dev_id = MKDEV(hidraw_major, 0);
390
391 cdev_del(&hidraw_cdev);
392 class_destroy(hidraw_class);
393 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
394
395}