aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
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
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')
-rw-r--r--drivers/hid/Kconfig19
-rw-r--r--drivers/hid/Makefile2
-rw-r--r--drivers/hid/hid-core.c16
-rw-r--r--drivers/hid/hidraw.c395
-rw-r--r--drivers/hid/usbhid/hid-core.c15
5 files changed, 445 insertions, 2 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 19667fcc722a..cacf89e65af4 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -46,6 +46,25 @@ config HID_DEBUG
46 46
47 If unsure, say N 47 If unsure, say N
48 48
49config HIDRAW
50 bool "/dev/hidraw raw HID device support"
51 depends on HID
52 ---help---
53 Say Y here if you want to support HID devices (from the USB
54 specification standpoint) that aren't strictly user interface
55 devices, like monitor controls and Uninterruptable Power Supplies.
56
57 This module supports these devices separately using a separate
58 event interface on /dev/hidraw.
59
60 There is also a /dev/hiddev configuration option in the USB HID
61 configuration menu. In comparison to hiddev, this device does not process
62 the hid events at all (no parsing, no lookups). This lets applications
63 to work on raw hid events when they want to, and avoid using transport-specific
64 userspace libhid/libusb libraries.
65
66 If unsure, say Y.
67
49source "drivers/hid/usbhid/Kconfig" 68source "drivers/hid/usbhid/Kconfig"
50 69
51endif # HID_SUPPORT 70endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 68d1376a53fb..1ac5103f7c93 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -4,7 +4,9 @@
4hid-objs := hid-core.o hid-input.o 4hid-objs := hid-core.o hid-input.o
5 5
6obj-$(CONFIG_HID) += hid.o 6obj-$(CONFIG_HID) += hid.o
7
7hid-$(CONFIG_HID_DEBUG) += hid-debug.o 8hid-$(CONFIG_HID_DEBUG) += hid-debug.o
9hid-$(CONFIG_HIDRAW) += hidraw.o
8 10
9obj-$(CONFIG_USB_HID) += usbhid/ 11obj-$(CONFIG_USB_HID) += usbhid/
10obj-$(CONFIG_USB_MOUSE) += usbhid/ 12obj-$(CONFIG_USB_MOUSE) += usbhid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 317cf8a7b63c..2884b036495a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -30,6 +30,7 @@
30#include <linux/hid.h> 30#include <linux/hid.h>
31#include <linux/hiddev.h> 31#include <linux/hiddev.h>
32#include <linux/hid-debug.h> 32#include <linux/hid-debug.h>
33#include <linux/hidraw.h>
33 34
34/* 35/*
35 * Version Information 36 * Version Information
@@ -979,6 +980,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
979 980
980 if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) 981 if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
981 hid->hiddev_report_event(hid, report); 982 hid->hiddev_report_event(hid, report);
983 if (hid->claimed & HID_CLAIMED_HIDRAW)
984 hidraw_report_event(hid, data, size);
982 985
983 for (n = 0; n < report->maxfield; n++) 986 for (n = 0; n < report->maxfield; n++)
984 hid_input_field(hid, report->field[n], data, interrupt); 987 hid_input_field(hid, report->field[n], data, interrupt);
@@ -990,5 +993,18 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
990} 993}
991EXPORT_SYMBOL_GPL(hid_input_report); 994EXPORT_SYMBOL_GPL(hid_input_report);
992 995
996static int __init hid_init(void)
997{
998 return hidraw_init();
999}
1000
1001static void __exit hid_exit(void)
1002{
1003 hidraw_exit();
1004}
1005
1006module_init(hid_init);
1007module_exit(hid_exit);
1008
993MODULE_LICENSE(DRIVER_LICENSE); 1009MODULE_LICENSE(DRIVER_LICENSE);
994 1010
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}
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 74b817d7968d..3a9563195850 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -32,6 +32,7 @@
32#include <linux/hid.h> 32#include <linux/hid.h>
33#include <linux/hiddev.h> 33#include <linux/hiddev.h>
34#include <linux/hid-debug.h> 34#include <linux/hid-debug.h>
35#include <linux/hidraw.h>
35#include "usbhid.h" 36#include "usbhid.h"
36 37
37/* 38/*
@@ -932,6 +933,8 @@ static void hid_disconnect(struct usb_interface *intf)
932 hidinput_disconnect(hid); 933 hidinput_disconnect(hid);
933 if (hid->claimed & HID_CLAIMED_HIDDEV) 934 if (hid->claimed & HID_CLAIMED_HIDDEV)
934 hiddev_disconnect(hid); 935 hiddev_disconnect(hid);
936 if (hid->claimed & HID_CLAIMED_HIDRAW)
937 hidraw_disconnect(hid);
935 938
936 usb_free_urb(usbhid->urbin); 939 usb_free_urb(usbhid->urbin);
937 usb_free_urb(usbhid->urbctrl); 940 usb_free_urb(usbhid->urbctrl);
@@ -964,11 +967,13 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
964 hid->claimed |= HID_CLAIMED_INPUT; 967 hid->claimed |= HID_CLAIMED_INPUT;
965 if (!hiddev_connect(hid)) 968 if (!hiddev_connect(hid))
966 hid->claimed |= HID_CLAIMED_HIDDEV; 969 hid->claimed |= HID_CLAIMED_HIDDEV;
970 if (!hidraw_connect(hid))
971 hid->claimed |= HID_CLAIMED_HIDRAW;
967 972
968 usb_set_intfdata(intf, hid); 973 usb_set_intfdata(intf, hid);
969 974
970 if (!hid->claimed) { 975 if (!hid->claimed) {
971 printk ("HID device not claimed by input or hiddev\n"); 976 printk ("HID device claimed by neither input, hiddev nor hidraw\n");
972 hid_disconnect(intf); 977 hid_disconnect(intf);
973 return -ENODEV; 978 return -ENODEV;
974 } 979 }
@@ -984,10 +989,16 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
984 989
985 if (hid->claimed & HID_CLAIMED_INPUT) 990 if (hid->claimed & HID_CLAIMED_INPUT)
986 printk("input"); 991 printk("input");
987 if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV)) 992 if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
993 hid->claimed & HID_CLAIMED_HIDRAW))
988 printk(","); 994 printk(",");
989 if (hid->claimed & HID_CLAIMED_HIDDEV) 995 if (hid->claimed & HID_CLAIMED_HIDDEV)
990 printk("hiddev%d", hid->minor); 996 printk("hiddev%d", hid->minor);
997 if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
998 (hid->claimed & HID_CLAIMED_HIDRAW))
999 printk(",");
1000 if (hid->claimed & HID_CLAIMED_HIDRAW)
1001 printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
991 1002
992 c = "Device"; 1003 c = "Device";
993 for (i = 0; i < hid->maxcollection; i++) { 1004 for (i = 0; i < hid->maxcollection; i++) {