blob: 54409cba018cd81b3bbefd1f990cc77fbbea8cee [file] [log] [blame]
Jiri Kosina86166b72007-05-14 09:57:40 +02001/*
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
Joe Perches4291ee32010-12-09 19:29:03 -080022#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
Jiri Kosina86166b72007-05-14 09:57:40 +020024#include <linux/fs.h>
25#include <linux/module.h>
26#include <linux/errno.h>
27#include <linux/kernel.h>
28#include <linux/init.h>
29#include <linux/cdev.h>
30#include <linux/poll.h>
31#include <linux/device.h>
32#include <linux/major.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090033#include <linux/slab.h>
Jiri Kosina86166b72007-05-14 09:57:40 +020034#include <linux/hid.h>
35#include <linux/mutex.h>
Alexey Dobriyana99bbaf2009-10-04 16:11:37 +040036#include <linux/sched.h>
Jiri Kosina86166b72007-05-14 09:57:40 +020037
38#include <linux/hidraw.h>
39
40static int hidraw_major;
41static struct cdev hidraw_cdev;
42static struct class *hidraw_class;
43static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
Oliver Neukum7d672cd2008-10-31 00:07:23 +010044static DEFINE_MUTEX(minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +020045
46static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
47{
48 struct hidraw_list *list = file->private_data;
49 int ret = 0, len;
Jiri Kosina86166b72007-05-14 09:57:40 +020050 DECLARE_WAITQUEUE(wait, current);
51
Jiri Kosinab0e14952009-10-12 11:25:56 +020052 mutex_lock(&list->read_mutex);
53
Jiri Kosina86166b72007-05-14 09:57:40 +020054 while (ret == 0) {
Jiri Kosina86166b72007-05-14 09:57:40 +020055 if (list->head == list->tail) {
56 add_wait_queue(&list->hidraw->wait, &wait);
57 set_current_state(TASK_INTERRUPTIBLE);
58
59 while (list->head == list->tail) {
60 if (file->f_flags & O_NONBLOCK) {
61 ret = -EAGAIN;
62 break;
63 }
64 if (signal_pending(current)) {
65 ret = -ERESTARTSYS;
66 break;
67 }
68 if (!list->hidraw->exist) {
69 ret = -EIO;
70 break;
71 }
72
73 /* allow O_NONBLOCK to work well from other threads */
74 mutex_unlock(&list->read_mutex);
75 schedule();
76 mutex_lock(&list->read_mutex);
77 set_current_state(TASK_INTERRUPTIBLE);
78 }
79
80 set_current_state(TASK_RUNNING);
81 remove_wait_queue(&list->hidraw->wait, &wait);
82 }
83
84 if (ret)
85 goto out;
86
Jiri Kosina86166b72007-05-14 09:57:40 +020087 len = list->buffer[list->tail].len > count ?
88 count : list->buffer[list->tail].len;
89
90 if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
91 ret = -EFAULT;
92 goto out;
93 }
Alan Ottcf28a672011-01-26 22:25:18 -050094 ret = len;
Jiri Kosina86166b72007-05-14 09:57:40 +020095
96 kfree(list->buffer[list->tail].value);
97 list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
98 }
99out:
100 mutex_unlock(&list->read_mutex);
101 return ret;
102}
103
104/* the first byte is expected to be a report number */
Alan Ottb4dbde92011-01-18 03:04:39 -0500105/* This function is to be called with the minors_lock mutex held */
106static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
Jiri Kosina86166b72007-05-14 09:57:40 +0200107{
108 unsigned int minor = iminor(file->f_path.dentry->d_inode);
Jiri Kosina2e574802010-03-25 14:15:11 +0100109 struct hid_device *dev;
Jiri Kosina86166b72007-05-14 09:57:40 +0200110 __u8 *buf;
111 int ret = 0;
112
Antonio Ospitee42dee92010-10-05 17:20:17 +0200113 if (!hidraw_table[minor]) {
114 ret = -ENODEV;
115 goto out;
116 }
117
Jiri Kosina2e574802010-03-25 14:15:11 +0100118 dev = hidraw_table[minor]->hid;
119
120 if (!dev->hid_output_raw_report) {
121 ret = -ENODEV;
122 goto out;
123 }
Jiri Kosina86166b72007-05-14 09:57:40 +0200124
Jiri Kosina2b107d62008-09-17 19:41:58 +0200125 if (count > HID_MAX_BUFFER_SIZE) {
Joe Perches4291ee32010-12-09 19:29:03 -0800126 hid_warn(dev, "pid %d passed too large report\n",
127 task_pid_nr(current));
Jiri Kosina2e574802010-03-25 14:15:11 +0100128 ret = -EINVAL;
129 goto out;
Jiri Kosina86166b72007-05-14 09:57:40 +0200130 }
131
132 if (count < 2) {
Joe Perches4291ee32010-12-09 19:29:03 -0800133 hid_warn(dev, "pid %d passed too short report\n",
134 task_pid_nr(current));
Jiri Kosina2e574802010-03-25 14:15:11 +0100135 ret = -EINVAL;
Jiri Kosina86166b72007-05-14 09:57:40 +0200136 goto out;
137 }
138
Jiri Kosina2e574802010-03-25 14:15:11 +0100139 buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
140 if (!buf) {
141 ret = -ENOMEM;
142 goto out;
143 }
144
145 if (copy_from_user(buf, buffer, count)) {
146 ret = -EFAULT;
147 goto out_free;
148 }
149
Alan Ottb4dbde92011-01-18 03:04:39 -0500150 ret = dev->hid_output_raw_report(dev, buf, count, report_type);
Jiri Kosina2e574802010-03-25 14:15:11 +0100151out_free:
Jiri Kosina86166b72007-05-14 09:57:40 +0200152 kfree(buf);
Jiri Kosina2e574802010-03-25 14:15:11 +0100153out:
Alan Ottb4dbde92011-01-18 03:04:39 -0500154 return ret;
155}
156
157/* the first byte is expected to be a report number */
158static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
159{
160 ssize_t ret;
161 mutex_lock(&minors_lock);
162 ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
Jiri Kosina2e574802010-03-25 14:15:11 +0100163 mutex_unlock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200164 return ret;
165}
166
Alan Ottb4dbde92011-01-18 03:04:39 -0500167
168/* This function performs a Get_Report transfer over the control endpoint
169 per section 7.2.1 of the HID specification, version 1.1. The first byte
170 of buffer is the report number to request, or 0x0 if the defice does not
171 use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
172 or HID_INPUT_REPORT. This function is to be called with the minors_lock
173 mutex held. */
174static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
175{
176 unsigned int minor = iminor(file->f_path.dentry->d_inode);
177 struct hid_device *dev;
178 __u8 *buf;
179 int ret = 0, len;
180 unsigned char report_number;
181
182 dev = hidraw_table[minor]->hid;
183
184 if (!dev->hid_get_raw_report) {
185 ret = -ENODEV;
186 goto out;
187 }
188
189 if (count > HID_MAX_BUFFER_SIZE) {
190 printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
191 task_pid_nr(current));
192 ret = -EINVAL;
193 goto out;
194 }
195
196 if (count < 2) {
197 printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
198 task_pid_nr(current));
199 ret = -EINVAL;
200 goto out;
201 }
202
203 buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
204 if (!buf) {
205 ret = -ENOMEM;
206 goto out;
207 }
208
209 /* Read the first byte from the user. This is the report number,
210 which is passed to dev->hid_get_raw_report(). */
211 if (copy_from_user(&report_number, buffer, 1)) {
212 ret = -EFAULT;
213 goto out_free;
214 }
215
216 ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
217
218 if (ret < 0)
219 goto out_free;
220
221 len = (ret < count) ? ret : count;
222
223 if (copy_to_user(buffer, buf, len)) {
224 ret = -EFAULT;
225 goto out_free;
226 }
227
228 ret = len;
229
230out_free:
231 kfree(buf);
232out:
233 return ret;
234}
235
Jiri Kosina86166b72007-05-14 09:57:40 +0200236static unsigned int hidraw_poll(struct file *file, poll_table *wait)
237{
238 struct hidraw_list *list = file->private_data;
239
240 poll_wait(file, &list->hidraw->wait, wait);
241 if (list->head != list->tail)
242 return POLLIN | POLLRDNORM;
243 if (!list->hidraw->exist)
244 return POLLERR | POLLHUP;
245 return 0;
246}
247
248static int hidraw_open(struct inode *inode, struct file *file)
249{
250 unsigned int minor = iminor(inode);
251 struct hidraw *dev;
252 struct hidraw_list *list;
253 int err = 0;
254
255 if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
256 err = -ENOMEM;
257 goto out;
258 }
259
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100260 mutex_lock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200261 if (!hidraw_table[minor]) {
Jiri Kosina86166b72007-05-14 09:57:40 +0200262 kfree(list);
263 err = -ENODEV;
264 goto out_unlock;
265 }
266
267 list->hidraw = hidraw_table[minor];
268 mutex_init(&list->read_mutex);
269 list_add_tail(&list->node, &hidraw_table[minor]->list);
270 file->private_data = list;
271
272 dev = hidraw_table[minor];
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100273 if (!dev->open++) {
Dmitry Torokhov5bea7662010-12-07 23:02:48 -0800274 err = hid_hw_power(dev->hid, PM_HINT_FULLON);
275 if (err < 0)
276 goto out_unlock;
277
278 err = hid_hw_open(dev->hid);
Oliver Neukum0361a282008-12-17 15:38:03 +0100279 if (err < 0) {
Dmitry Torokhov5bea7662010-12-07 23:02:48 -0800280 hid_hw_power(dev->hid, PM_HINT_NORMAL);
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100281 dev->open--;
Oliver Neukum0361a282008-12-17 15:38:03 +0100282 }
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100283 }
Jiri Kosina86166b72007-05-14 09:57:40 +0200284
285out_unlock:
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100286 mutex_unlock(&minors_lock);
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100287out:
Jiri Kosina86166b72007-05-14 09:57:40 +0200288 return err;
289
290}
291
292static int hidraw_release(struct inode * inode, struct file * file)
293{
294 unsigned int minor = iminor(inode);
295 struct hidraw *dev;
296 struct hidraw_list *list = file->private_data;
Jiri Slabycb174682010-10-19 11:29:55 +0200297 int ret;
Jiri Kosina86166b72007-05-14 09:57:40 +0200298
Jiri Slabycb174682010-10-19 11:29:55 +0200299 mutex_lock(&minors_lock);
300 if (!hidraw_table[minor]) {
301 ret = -ENODEV;
302 goto unlock;
303 }
Jiri Kosina86166b72007-05-14 09:57:40 +0200304
305 list_del(&list->node);
306 dev = hidraw_table[minor];
Oliver Neukumb8a832b2008-12-15 13:12:08 +0100307 if (!--dev->open) {
Oliver Neukum0361a282008-12-17 15:38:03 +0100308 if (list->hidraw->exist) {
Dmitry Torokhov5bea7662010-12-07 23:02:48 -0800309 hid_hw_power(dev->hid, PM_HINT_NORMAL);
310 hid_hw_close(dev->hid);
Oliver Neukum0361a282008-12-17 15:38:03 +0100311 } else {
Jiri Kosina86166b72007-05-14 09:57:40 +0200312 kfree(list->hidraw);
Oliver Neukum0361a282008-12-17 15:38:03 +0100313 }
Jiri Kosina86166b72007-05-14 09:57:40 +0200314 }
Jiri Kosina4db1c622008-06-24 14:45:27 +0200315 kfree(list);
Jiri Slabycb174682010-10-19 11:29:55 +0200316 ret = 0;
317unlock:
318 mutex_unlock(&minors_lock);
Jiri Kosina4db1c622008-06-24 14:45:27 +0200319
Jiri Slabycb174682010-10-19 11:29:55 +0200320 return ret;
Jiri Kosina86166b72007-05-14 09:57:40 +0200321}
322
Alan Cox979c4072008-05-26 11:25:26 +0200323static long hidraw_ioctl(struct file *file, unsigned int cmd,
324 unsigned long arg)
Jiri Kosina86166b72007-05-14 09:57:40 +0200325{
Alan Cox979c4072008-05-26 11:25:26 +0200326 struct inode *inode = file->f_path.dentry->d_inode;
Jiri Kosina86166b72007-05-14 09:57:40 +0200327 unsigned int minor = iminor(inode);
Alan Cox979c4072008-05-26 11:25:26 +0200328 long ret = 0;
Jiri Kosina2e574802010-03-25 14:15:11 +0100329 struct hidraw *dev;
Jiri Kosina86166b72007-05-14 09:57:40 +0200330 void __user *user_arg = (void __user*) arg;
331
Jiri Kosina2e574802010-03-25 14:15:11 +0100332 mutex_lock(&minors_lock);
333 dev = hidraw_table[minor];
Antonio Ospited20d5ff2010-10-05 17:20:16 +0200334 if (!dev) {
335 ret = -ENODEV;
336 goto out;
337 }
Jiri Kosina2e574802010-03-25 14:15:11 +0100338
Jiri Kosina86166b72007-05-14 09:57:40 +0200339 switch (cmd) {
340 case HIDIOCGRDESCSIZE:
341 if (put_user(dev->hid->rsize, (int __user *)arg))
Alan Cox979c4072008-05-26 11:25:26 +0200342 ret = -EFAULT;
343 break;
Jiri Kosina86166b72007-05-14 09:57:40 +0200344
345 case HIDIOCGRDESC:
346 {
347 __u32 len;
348
349 if (get_user(len, (int __user *)arg))
Alan Cox979c4072008-05-26 11:25:26 +0200350 ret = -EFAULT;
351 else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
352 ret = -EINVAL;
353 else if (copy_to_user(user_arg + offsetof(
354 struct hidraw_report_descriptor,
355 value[0]),
356 dev->hid->rdesc,
357 min(dev->hid->rsize, len)))
358 ret = -EFAULT;
359 break;
Jiri Kosina86166b72007-05-14 09:57:40 +0200360 }
361 case HIDIOCGRAWINFO:
362 {
363 struct hidraw_devinfo dinfo;
364
365 dinfo.bustype = dev->hid->bus;
366 dinfo.vendor = dev->hid->vendor;
367 dinfo.product = dev->hid->product;
368 if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
Alan Cox979c4072008-05-26 11:25:26 +0200369 ret = -EFAULT;
370 break;
Jiri Kosina86166b72007-05-14 09:57:40 +0200371 }
372 default:
Jiri Kosina9188e792008-11-12 16:14:08 +0100373 {
374 struct hid_device *hid = dev->hid;
Alan Ottb4dbde92011-01-18 03:04:39 -0500375 if (_IOC_TYPE(cmd) != 'H') {
376 ret = -EINVAL;
377 break;
378 }
379
380 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {
381 int len = _IOC_SIZE(cmd);
382 ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
383 break;
384 }
385 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {
386 int len = _IOC_SIZE(cmd);
387 ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
388 break;
389 }
390
391 /* Begin Read-only ioctls. */
392 if (_IOC_DIR(cmd) != _IOC_READ) {
Dan Carpenterdfd395af2009-02-03 16:35:17 +0300393 ret = -EINVAL;
394 break;
395 }
Jiri Kosina9188e792008-11-12 16:14:08 +0100396
397 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {
398 int len;
Dan Carpenter38089c62009-04-07 16:35:56 +0200399 if (!hid->name) {
400 ret = 0;
401 break;
402 }
Jiri Kosina9188e792008-11-12 16:14:08 +0100403 len = strlen(hid->name) + 1;
404 if (len > _IOC_SIZE(cmd))
405 len = _IOC_SIZE(cmd);
Dan Carpenterdfd395af2009-02-03 16:35:17 +0300406 ret = copy_to_user(user_arg, hid->name, len) ?
Jiri Kosina9188e792008-11-12 16:14:08 +0100407 -EFAULT : len;
Dan Carpenterdfd395af2009-02-03 16:35:17 +0300408 break;
Jiri Kosina9188e792008-11-12 16:14:08 +0100409 }
410
411 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {
412 int len;
Dan Carpenter38089c62009-04-07 16:35:56 +0200413 if (!hid->phys) {
414 ret = 0;
415 break;
416 }
Jiri Kosina9188e792008-11-12 16:14:08 +0100417 len = strlen(hid->phys) + 1;
418 if (len > _IOC_SIZE(cmd))
419 len = _IOC_SIZE(cmd);
Dan Carpenterdfd395af2009-02-03 16:35:17 +0300420 ret = copy_to_user(user_arg, hid->phys, len) ?
Jiri Kosina9188e792008-11-12 16:14:08 +0100421 -EFAULT : len;
Dan Carpenterdfd395af2009-02-03 16:35:17 +0300422 break;
Jiri Kosina9188e792008-11-12 16:14:08 +0100423 }
Alan Ottb4dbde92011-01-18 03:04:39 -0500424 }
Jiri Kosina9188e792008-11-12 16:14:08 +0100425
Dan Carpenterdfd395af2009-02-03 16:35:17 +0300426 ret = -ENOTTY;
Jiri Kosina86166b72007-05-14 09:57:40 +0200427 }
Antonio Ospited20d5ff2010-10-05 17:20:16 +0200428out:
Jiri Kosina2e574802010-03-25 14:15:11 +0100429 mutex_unlock(&minors_lock);
Alan Cox979c4072008-05-26 11:25:26 +0200430 return ret;
Jiri Kosina86166b72007-05-14 09:57:40 +0200431}
432
433static const struct file_operations hidraw_ops = {
434 .owner = THIS_MODULE,
435 .read = hidraw_read,
436 .write = hidraw_write,
437 .poll = hidraw_poll,
438 .open = hidraw_open,
439 .release = hidraw_release,
Alan Cox979c4072008-05-26 11:25:26 +0200440 .unlocked_ioctl = hidraw_ioctl,
Alan Ottae5e49c2011-01-04 00:37:22 -0500441#ifdef CONFIG_COMPAT
442 .compat_ioctl = hidraw_ioctl,
443#endif
Arnd Bergmann6038f372010-08-15 18:52:59 +0200444 .llseek = noop_llseek,
Jiri Kosina86166b72007-05-14 09:57:40 +0200445};
446
447void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
448{
449 struct hidraw *dev = hid->hidraw;
450 struct hidraw_list *list;
451
452 list_for_each_entry(list, &dev->list, node) {
453 list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
454 list->buffer[list->head].len = len;
455 list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
456 kill_fasync(&list->fasync, SIGIO, POLL_IN);
457 }
458
459 wake_up_interruptible(&dev->wait);
460}
461EXPORT_SYMBOL_GPL(hidraw_report_event);
462
463int hidraw_connect(struct hid_device *hid)
464{
Mariusz Kozlowski709d27c2007-09-27 11:24:55 +0200465 int minor, result;
Jiri Kosina86166b72007-05-14 09:57:40 +0200466 struct hidraw *dev;
467
Jiri Kosinabbe281f2009-06-04 15:44:25 +0200468 /* we accept any HID device, no matter the applications */
Jiri Kosina86166b72007-05-14 09:57:40 +0200469
Mariusz Kozlowski709d27c2007-09-27 11:24:55 +0200470 dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
471 if (!dev)
472 return -ENOMEM;
473
474 result = -EINVAL;
Jiri Kosina86166b72007-05-14 09:57:40 +0200475
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100476 mutex_lock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200477
478 for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
479 if (hidraw_table[minor])
480 continue;
481 hidraw_table[minor] = dev;
482 result = 0;
483 break;
484 }
485
Mariusz Kozlowski709d27c2007-09-27 11:24:55 +0200486 if (result) {
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100487 mutex_unlock(&minors_lock);
Mariusz Kozlowski709d27c2007-09-27 11:24:55 +0200488 kfree(dev);
Jiri Kosina86166b72007-05-14 09:57:40 +0200489 goto out;
Mariusz Kozlowski709d27c2007-09-27 11:24:55 +0200490 }
Jiri Kosina86166b72007-05-14 09:57:40 +0200491
Jiri Kosinaaae6c282008-12-04 16:16:46 +0100492 dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
Greg Kroah-Hartmana9b12612008-07-21 20:03:34 -0700493 NULL, "%s%d", "hidraw", minor);
Jiri Kosina86166b72007-05-14 09:57:40 +0200494
495 if (IS_ERR(dev->dev)) {
Jiri Kosina86166b72007-05-14 09:57:40 +0200496 hidraw_table[minor] = NULL;
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100497 mutex_unlock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200498 result = PTR_ERR(dev->dev);
Mariusz Kozlowski709d27c2007-09-27 11:24:55 +0200499 kfree(dev);
Jiri Kosina86166b72007-05-14 09:57:40 +0200500 goto out;
501 }
502
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100503 mutex_unlock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200504 init_waitqueue_head(&dev->wait);
505 INIT_LIST_HEAD(&dev->list);
506
507 dev->hid = hid;
508 dev->minor = minor;
509
510 dev->exist = 1;
511 hid->hidraw = dev;
512
513out:
514 return result;
515
516}
517EXPORT_SYMBOL_GPL(hidraw_connect);
518
519void hidraw_disconnect(struct hid_device *hid)
520{
521 struct hidraw *hidraw = hid->hidraw;
522
523 hidraw->exist = 0;
524
Stefan Achatz3a22ebe2011-01-29 02:17:30 +0100525 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
526
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100527 mutex_lock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200528 hidraw_table[hidraw->minor] = NULL;
Oliver Neukum7d672cd2008-10-31 00:07:23 +0100529 mutex_unlock(&minors_lock);
Jiri Kosina86166b72007-05-14 09:57:40 +0200530
Jiri Kosina86166b72007-05-14 09:57:40 +0200531 if (hidraw->open) {
Dmitry Torokhov5bea7662010-12-07 23:02:48 -0800532 hid_hw_close(hid);
Jiri Kosina86166b72007-05-14 09:57:40 +0200533 wake_up_interruptible(&hidraw->wait);
534 } else {
535 kfree(hidraw);
536 }
537}
538EXPORT_SYMBOL_GPL(hidraw_disconnect);
539
540int __init hidraw_init(void)
541{
542 int result;
543 dev_t dev_id;
544
545 result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
546 HIDRAW_MAX_DEVICES, "hidraw");
547
548 hidraw_major = MAJOR(dev_id);
549
550 if (result < 0) {
Joe Perches4291ee32010-12-09 19:29:03 -0800551 pr_warn("can't get major number\n");
Jiri Kosina86166b72007-05-14 09:57:40 +0200552 result = 0;
553 goto out;
554 }
555
556 hidraw_class = class_create(THIS_MODULE, "hidraw");
557 if (IS_ERR(hidraw_class)) {
558 result = PTR_ERR(hidraw_class);
559 unregister_chrdev(hidraw_major, "hidraw");
560 goto out;
561 }
562
563 cdev_init(&hidraw_cdev, &hidraw_ops);
564 cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
565out:
566 return result;
567}
568
Jiri Slaby140ae3e2008-10-17 18:04:48 +0200569void hidraw_exit(void)
Jiri Kosina86166b72007-05-14 09:57:40 +0200570{
571 dev_t dev_id = MKDEV(hidraw_major, 0);
572
573 cdev_del(&hidraw_cdev);
574 class_destroy(hidraw_class);
575 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
576
577}