diff options
-rw-r--r-- | drivers/hid/Kconfig | 19 | ||||
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 16 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 395 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 15 | ||||
-rw-r--r-- | include/linux/hid.h | 5 | ||||
-rw-r--r-- | include/linux/hidraw.h | 86 |
7 files changed, 536 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 | ||
49 | config 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 | |||
49 | source "drivers/hid/usbhid/Kconfig" | 68 | source "drivers/hid/usbhid/Kconfig" |
50 | 69 | ||
51 | endif # HID_SUPPORT | 70 | endif # 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 @@ | |||
4 | hid-objs := hid-core.o hid-input.o | 4 | hid-objs := hid-core.o hid-input.o |
5 | 5 | ||
6 | obj-$(CONFIG_HID) += hid.o | 6 | obj-$(CONFIG_HID) += hid.o |
7 | |||
7 | hid-$(CONFIG_HID_DEBUG) += hid-debug.o | 8 | hid-$(CONFIG_HID_DEBUG) += hid-debug.o |
9 | hid-$(CONFIG_HIDRAW) += hidraw.o | ||
8 | 10 | ||
9 | obj-$(CONFIG_USB_HID) += usbhid/ | 11 | obj-$(CONFIG_USB_HID) += usbhid/ |
10 | obj-$(CONFIG_USB_MOUSE) += usbhid/ | 12 | obj-$(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 | } |
991 | EXPORT_SYMBOL_GPL(hid_input_report); | 994 | EXPORT_SYMBOL_GPL(hid_input_report); |
992 | 995 | ||
996 | static int __init hid_init(void) | ||
997 | { | ||
998 | return hidraw_init(); | ||
999 | } | ||
1000 | |||
1001 | static void __exit hid_exit(void) | ||
1002 | { | ||
1003 | hidraw_exit(); | ||
1004 | } | ||
1005 | |||
1006 | module_init(hid_init); | ||
1007 | module_exit(hid_exit); | ||
1008 | |||
993 | MODULE_LICENSE(DRIVER_LICENSE); | 1009 | MODULE_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 | |||
36 | static int hidraw_major; | ||
37 | static struct cdev hidraw_cdev; | ||
38 | static struct class *hidraw_class; | ||
39 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; | ||
40 | static DEFINE_SPINLOCK(minors_lock); | ||
41 | |||
42 | static 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 | } | ||
98 | out: | ||
99 | mutex_unlock(&list->read_mutex); | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | /* the first byte is expected to be a report number */ | ||
104 | static 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); | ||
136 | out: | ||
137 | kfree(buf); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | static 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 | |||
153 | static 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 | |||
183 | out_unlock: | ||
184 | spin_unlock(&minors_lock); | ||
185 | out: | ||
186 | return err; | ||
187 | |||
188 | } | ||
189 | |||
190 | static 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 | |||
214 | static 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 | |||
257 | static 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 | |||
267 | void 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 | } | ||
281 | EXPORT_SYMBOL_GPL(hidraw_report_event); | ||
282 | |||
283 | int 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 | |||
331 | out: | ||
332 | return result; | ||
333 | |||
334 | } | ||
335 | EXPORT_SYMBOL_GPL(hidraw_connect); | ||
336 | |||
337 | void 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 | } | ||
356 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | ||
357 | |||
358 | int __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); | ||
383 | out: | ||
384 | return result; | ||
385 | } | ||
386 | |||
387 | void __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++) { |
diff --git a/include/linux/hid.h b/include/linux/hid.h index 898103b401f1..0ac2b5260b26 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
@@ -403,6 +403,7 @@ struct hid_control_fifo { | |||
403 | 403 | ||
404 | #define HID_CLAIMED_INPUT 1 | 404 | #define HID_CLAIMED_INPUT 1 |
405 | #define HID_CLAIMED_HIDDEV 2 | 405 | #define HID_CLAIMED_HIDDEV 2 |
406 | #define HID_CLAIMED_HIDRAW 4 | ||
406 | 407 | ||
407 | #define HID_CTRL_RUNNING 1 | 408 | #define HID_CTRL_RUNNING 1 |
408 | #define HID_OUT_RUNNING 2 | 409 | #define HID_OUT_RUNNING 2 |
@@ -438,6 +439,7 @@ struct hid_device { /* device report descriptor */ | |||
438 | 439 | ||
439 | struct list_head inputs; /* The list of inputs */ | 440 | struct list_head inputs; /* The list of inputs */ |
440 | void *hiddev; /* The hiddev structure */ | 441 | void *hiddev; /* The hiddev structure */ |
442 | void *hidraw; | ||
441 | int minor; /* Hiddev minor number */ | 443 | int minor; /* Hiddev minor number */ |
442 | 444 | ||
443 | wait_queue_head_t wait; /* For sleeping */ | 445 | wait_queue_head_t wait; /* For sleeping */ |
@@ -458,6 +460,9 @@ struct hid_device { /* device report descriptor */ | |||
458 | void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, | 460 | void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, |
459 | struct hid_usage *, __s32); | 461 | struct hid_usage *, __s32); |
460 | void (*hiddev_report_event) (struct hid_device *, struct hid_report *); | 462 | void (*hiddev_report_event) (struct hid_device *, struct hid_report *); |
463 | |||
464 | /* handler for raw output data, used by hidraw */ | ||
465 | int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); | ||
461 | #ifdef CONFIG_USB_HIDINPUT_POWERBOOK | 466 | #ifdef CONFIG_USB_HIDINPUT_POWERBOOK |
462 | unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; | 467 | unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; |
463 | unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; | 468 | unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; |
diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h new file mode 100644 index 000000000000..6676cd5e9954 --- /dev/null +++ b/include/linux/hidraw.h | |||
@@ -0,0 +1,86 @@ | |||
1 | #ifndef _HIDRAW_H | ||
2 | #define _HIDRAW_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (c) 2007 Jiri Kosina | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms and conditions of the GNU General Public License, | ||
11 | * version 2, as published by the Free Software Foundation. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | */ | ||
17 | |||
18 | struct hidraw_report_descriptor { | ||
19 | __u32 size; | ||
20 | __u8 *value; | ||
21 | }; | ||
22 | |||
23 | struct hidraw_devinfo { | ||
24 | __u32 bustype; | ||
25 | __s16 vendor; | ||
26 | __s16 product; | ||
27 | }; | ||
28 | |||
29 | /* ioctl interface */ | ||
30 | #define HIDIOCGRDESCSIZE _IOR('H', 0x01, int) | ||
31 | #define HIDIOCGRDESC _IOR('H', 0x02, struct hidraw_report_descriptor) | ||
32 | #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) | ||
33 | |||
34 | #define HIDRAW_FIRST_MINOR 0 | ||
35 | #define HIDRAW_MAX_DEVICES 64 | ||
36 | /* number of reports to buffer */ | ||
37 | #define HIDRAW_BUFFER_SIZE 64 | ||
38 | |||
39 | |||
40 | /* kernel-only API declarations */ | ||
41 | #ifdef __KERNEL__ | ||
42 | |||
43 | #include <linux/hid.h> | ||
44 | |||
45 | struct hidraw { | ||
46 | unsigned int minor; | ||
47 | int exist; | ||
48 | int open; | ||
49 | wait_queue_head_t wait; | ||
50 | struct hid_device *hid; | ||
51 | struct device *dev; | ||
52 | struct list_head list; | ||
53 | }; | ||
54 | |||
55 | struct hidraw_report { | ||
56 | __u8 *value; | ||
57 | int len; | ||
58 | }; | ||
59 | |||
60 | struct hidraw_list { | ||
61 | struct hidraw_report buffer[HIDRAW_BUFFER_SIZE]; | ||
62 | int head; | ||
63 | int tail; | ||
64 | struct fasync_struct *fasync; | ||
65 | struct hidraw *hidraw; | ||
66 | struct list_head node; | ||
67 | struct mutex read_mutex; | ||
68 | }; | ||
69 | |||
70 | #ifdef CONFIG_HIDRAW | ||
71 | int hidraw_init(void); | ||
72 | void hidraw_exit(void); | ||
73 | void hidraw_report_event(struct hid_device *, u8 *, int); | ||
74 | int hidraw_connect(struct hid_device *); | ||
75 | void hidraw_disconnect(struct hid_device *); | ||
76 | #else | ||
77 | static inline int hidraw_init(void) { return 0; } | ||
78 | static inline void hidraw_exit(void) { } | ||
79 | static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { } | ||
80 | static inline int hidraw_connect(struct hid_device *hid) { return -1; } | ||
81 | static inline void hidraw_disconnect(struct hid_device *hid) { } | ||
82 | #endif | ||
83 | |||
84 | #endif | ||
85 | |||
86 | #endif | ||