aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2007-10-14 08:47:56 -0400
committerJiri Kosina <jkosina@suse.cz>2007-10-14 08:47:56 -0400
commitd057fd4cb892087955568a139d15eae4115a0174 (patch)
tree4fd631492ff8d2d78c394fd2b054175588a7d633 /drivers/hid
parentbb6c8d8fa9b5587eea18078ce0bcf6bb2905789f (diff)
parent709d27c04f4eccbc99d57a5569bce028915a4345 (diff)
Merge branch 'hidraw' into for-linus
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.c401
-rw-r--r--drivers/hid/usbhid/hid-core.c38
5 files changed, 474 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..8503197a8131
--- /dev/null
+++ b/drivers/hid/hidraw.c
@@ -0,0 +1,401 @@
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;
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 dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
294 if (!dev)
295 return -ENOMEM;
296
297 result = -EINVAL;
298
299 spin_lock(&minors_lock);
300
301 for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
302 if (hidraw_table[minor])
303 continue;
304 hidraw_table[minor] = dev;
305 result = 0;
306 break;
307 }
308
309 spin_unlock(&minors_lock);
310
311 if (result) {
312 kfree(dev);
313 goto out;
314 }
315
316 dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor),
317 "%s%d", "hidraw", minor);
318
319 if (IS_ERR(dev->dev)) {
320 spin_lock(&minors_lock);
321 hidraw_table[minor] = NULL;
322 spin_unlock(&minors_lock);
323 result = PTR_ERR(dev->dev);
324 kfree(dev);
325 goto out;
326 }
327
328 init_waitqueue_head(&dev->wait);
329 INIT_LIST_HEAD(&dev->list);
330
331 dev->hid = hid;
332 dev->minor = minor;
333
334 dev->exist = 1;
335 hid->hidraw = dev;
336
337out:
338 return result;
339
340}
341EXPORT_SYMBOL_GPL(hidraw_connect);
342
343void hidraw_disconnect(struct hid_device *hid)
344{
345 struct hidraw *hidraw = hid->hidraw;
346
347 hidraw->exist = 0;
348
349 spin_lock(&minors_lock);
350 hidraw_table[hidraw->minor] = NULL;
351 spin_unlock(&minors_lock);
352
353 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
354
355 if (hidraw->open) {
356 hid->hid_close(hid);
357 wake_up_interruptible(&hidraw->wait);
358 } else {
359 kfree(hidraw);
360 }
361}
362EXPORT_SYMBOL_GPL(hidraw_disconnect);
363
364int __init hidraw_init(void)
365{
366 int result;
367 dev_t dev_id;
368
369 result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
370 HIDRAW_MAX_DEVICES, "hidraw");
371
372 hidraw_major = MAJOR(dev_id);
373
374 if (result < 0) {
375 printk(KERN_WARNING "hidraw: can't get major number\n");
376 result = 0;
377 goto out;
378 }
379
380 hidraw_class = class_create(THIS_MODULE, "hidraw");
381 if (IS_ERR(hidraw_class)) {
382 result = PTR_ERR(hidraw_class);
383 unregister_chrdev(hidraw_major, "hidraw");
384 goto out;
385 }
386
387 cdev_init(&hidraw_cdev, &hidraw_ops);
388 cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
389out:
390 return result;
391}
392
393void __exit hidraw_exit(void)
394{
395 dev_t dev_id = MKDEV(hidraw_major, 0);
396
397 cdev_del(&hidraw_cdev);
398 class_destroy(hidraw_class);
399 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
400
401}
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index a34e0f098f63..b38e559b7a46 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/*
@@ -639,6 +640,28 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
639 return 0; 640 return 0;
640} 641}
641 642
643static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count)
644{
645 struct usbhid_device *usbhid = hid->driver_data;
646 struct usb_device *dev = hid_to_usb_dev(hid);
647 struct usb_interface *intf = usbhid->intf;
648 struct usb_host_interface *interface = intf->cur_altsetting;
649 int ret;
650
651 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
652 HID_REQ_SET_REPORT,
653 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
654 cpu_to_le16(((HID_OUTPUT_REPORT + 1) << 8) | *buf),
655 interface->desc.bInterfaceNumber, buf + 1, count - 1,
656 USB_CTRL_SET_TIMEOUT);
657
658 /* count also the report id */
659 if (ret > 0)
660 ret++;
661
662 return ret;
663}
664
642static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) 665static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
643{ 666{
644 struct usbhid_device *usbhid = hid->driver_data; 667 struct usbhid_device *usbhid = hid->driver_data;
@@ -882,6 +905,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
882 hid->hiddev_hid_event = hiddev_hid_event; 905 hid->hiddev_hid_event = hiddev_hid_event;
883 hid->hiddev_report_event = hiddev_report_event; 906 hid->hiddev_report_event = hiddev_report_event;
884#endif 907#endif
908 hid->hid_output_raw_report = usbhid_output_raw_report;
885 return hid; 909 return hid;
886 910
887fail: 911fail:
@@ -920,6 +944,8 @@ static void hid_disconnect(struct usb_interface *intf)
920 hidinput_disconnect(hid); 944 hidinput_disconnect(hid);
921 if (hid->claimed & HID_CLAIMED_HIDDEV) 945 if (hid->claimed & HID_CLAIMED_HIDDEV)
922 hiddev_disconnect(hid); 946 hiddev_disconnect(hid);
947 if (hid->claimed & HID_CLAIMED_HIDRAW)
948 hidraw_disconnect(hid);
923 949
924 usb_free_urb(usbhid->urbin); 950 usb_free_urb(usbhid->urbin);
925 usb_free_urb(usbhid->urbctrl); 951 usb_free_urb(usbhid->urbctrl);
@@ -952,11 +978,13 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
952 hid->claimed |= HID_CLAIMED_INPUT; 978 hid->claimed |= HID_CLAIMED_INPUT;
953 if (!hiddev_connect(hid)) 979 if (!hiddev_connect(hid))
954 hid->claimed |= HID_CLAIMED_HIDDEV; 980 hid->claimed |= HID_CLAIMED_HIDDEV;
981 if (!hidraw_connect(hid))
982 hid->claimed |= HID_CLAIMED_HIDRAW;
955 983
956 usb_set_intfdata(intf, hid); 984 usb_set_intfdata(intf, hid);
957 985
958 if (!hid->claimed) { 986 if (!hid->claimed) {
959 printk ("HID device not claimed by input or hiddev\n"); 987 printk ("HID device claimed by neither input, hiddev nor hidraw\n");
960 hid_disconnect(intf); 988 hid_disconnect(intf);
961 return -ENODEV; 989 return -ENODEV;
962 } 990 }
@@ -972,10 +1000,16 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
972 1000
973 if (hid->claimed & HID_CLAIMED_INPUT) 1001 if (hid->claimed & HID_CLAIMED_INPUT)
974 printk("input"); 1002 printk("input");
975 if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV)) 1003 if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
1004 hid->claimed & HID_CLAIMED_HIDRAW))
976 printk(","); 1005 printk(",");
977 if (hid->claimed & HID_CLAIMED_HIDDEV) 1006 if (hid->claimed & HID_CLAIMED_HIDDEV)
978 printk("hiddev%d", hid->minor); 1007 printk("hiddev%d", hid->minor);
1008 if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
1009 (hid->claimed & HID_CLAIMED_HIDRAW))
1010 printk(",");
1011 if (hid->claimed & HID_CLAIMED_HIDRAW)
1012 printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
979 1013
980 c = "Device"; 1014 c = "Device";
981 for (i = 0; i < hid->maxcollection; i++) { 1015 for (i = 0; i < hid->maxcollection; i++) {