Blame view

drivers/hid/hidraw.c 12.4 KB
86166b7bc   Jiri Kosina   HID: add hidraw i...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * HID raw devices, giving access to raw HID events.
   *
   * In comparison to hiddev, this device does not process the
   * hid events at all (no parsing, no lookups). This lets applications
   * to work on raw hid events as they want to, and avoids a need to
   * use a transport-specific userspace libhid/libusb libraries.
   *
   *  Copyright (c) 2007 Jiri Kosina
   */
  
  /*
   * This program is free software; you can redistribute it and/or modify it
   * under the terms and conditions of the GNU General Public License,
   * version 2, as published by the Free Software Foundation.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
   */
4291ee305   Joe Perches   HID: Add and use ...
21
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
86166b7bc   Jiri Kosina   HID: add hidraw i...
22
23
24
25
26
27
28
29
30
  #include <linux/fs.h>
  #include <linux/module.h>
  #include <linux/errno.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/cdev.h>
  #include <linux/poll.h>
  #include <linux/device.h>
  #include <linux/major.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
86166b7bc   Jiri Kosina   HID: add hidraw i...
32
33
  #include <linux/hid.h>
  #include <linux/mutex.h>
a99bbaf5e   Alexey Dobriyan   headers: remove s...
34
  #include <linux/sched.h>
86166b7bc   Jiri Kosina   HID: add hidraw i...
35
36
37
38
39
40
41
  
  #include <linux/hidraw.h>
  
  static int hidraw_major;
  static struct cdev hidraw_cdev;
  static struct class *hidraw_class;
  static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
7d672cd75   Oliver Neukum   HID: fix locking ...
42
  static DEFINE_MUTEX(minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
43
44
45
46
47
  
  static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
  {
  	struct hidraw_list *list = file->private_data;
  	int ret = 0, len;
86166b7bc   Jiri Kosina   HID: add hidraw i...
48
  	DECLARE_WAITQUEUE(wait, current);
b0e14951e   Jiri Kosina   HID: fix possible...
49
  	mutex_lock(&list->read_mutex);
86166b7bc   Jiri Kosina   HID: add hidraw i...
50

b0e14951e   Jiri Kosina   HID: fix possible...
51
  	while (ret == 0) {
86166b7bc   Jiri Kosina   HID: add hidraw i...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  		if (list->head == list->tail) {
  			add_wait_queue(&list->hidraw->wait, &wait);
  			set_current_state(TASK_INTERRUPTIBLE);
  
  			while (list->head == list->tail) {
  				if (file->f_flags & O_NONBLOCK) {
  					ret = -EAGAIN;
  					break;
  				}
  				if (signal_pending(current)) {
  					ret = -ERESTARTSYS;
  					break;
  				}
  				if (!list->hidraw->exist) {
  					ret = -EIO;
  					break;
  				}
  
  				/* allow O_NONBLOCK to work well from other threads */
  				mutex_unlock(&list->read_mutex);
  				schedule();
  				mutex_lock(&list->read_mutex);
  				set_current_state(TASK_INTERRUPTIBLE);
  			}
  
  			set_current_state(TASK_RUNNING);
  			remove_wait_queue(&list->hidraw->wait, &wait);
  		}
  
  		if (ret)
  			goto out;
86166b7bc   Jiri Kosina   HID: add hidraw i...
83
84
85
86
87
88
89
  		len = list->buffer[list->tail].len > count ?
  			count : list->buffer[list->tail].len;
  
  		if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
  			ret = -EFAULT;
  			goto out;
  		}
cf28a6736   Alan Ott   HID: hidraw: Repl...
90
  		ret = len;
86166b7bc   Jiri Kosina   HID: add hidraw i...
91
92
93
94
95
96
97
98
  
  		kfree(list->buffer[list->tail].value);
  		list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
  	}
  out:
  	mutex_unlock(&list->read_mutex);
  	return ret;
  }
d2a1cfebe   Jiri Kosina   HID: hidraw: fix ...
99
100
  /* The first byte is expected to be a report number.
   * This function is to be called with the minors_lock mutex held */
b4dbde9da   Alan Ott   HID: Add Support ...
101
  static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
86166b7bc   Jiri Kosina   HID: add hidraw i...
102
103
  {
  	unsigned int minor = iminor(file->f_path.dentry->d_inode);
2e57480b2   Jiri Kosina   HID: remove BKL f...
104
  	struct hid_device *dev;
86166b7bc   Jiri Kosina   HID: add hidraw i...
105
106
  	__u8 *buf;
  	int ret = 0;
e42dee9a9   Antonio Ospite   HID: hidraw, fix ...
107
108
109
110
  	if (!hidraw_table[minor]) {
  		ret = -ENODEV;
  		goto out;
  	}
2e57480b2   Jiri Kosina   HID: remove BKL f...
111
112
113
114
115
116
  	dev = hidraw_table[minor]->hid;
  
  	if (!dev->hid_output_raw_report) {
  		ret = -ENODEV;
  		goto out;
  	}
86166b7bc   Jiri Kosina   HID: add hidraw i...
117

2b107d629   Jiri Kosina   HID: fix incorren...
118
  	if (count > HID_MAX_BUFFER_SIZE) {
4291ee305   Joe Perches   HID: Add and use ...
119
120
121
  		hid_warn(dev, "pid %d passed too large report
  ",
  			 task_pid_nr(current));
2e57480b2   Jiri Kosina   HID: remove BKL f...
122
123
  		ret = -EINVAL;
  		goto out;
86166b7bc   Jiri Kosina   HID: add hidraw i...
124
125
126
  	}
  
  	if (count < 2) {
4291ee305   Joe Perches   HID: Add and use ...
127
128
129
  		hid_warn(dev, "pid %d passed too short report
  ",
  			 task_pid_nr(current));
2e57480b2   Jiri Kosina   HID: remove BKL f...
130
131
  		ret = -EINVAL;
  		goto out;
86166b7bc   Jiri Kosina   HID: add hidraw i...
132
133
134
  	}
  
  	buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
2e57480b2   Jiri Kosina   HID: remove BKL f...
135
136
137
138
  	if (!buf) {
  		ret = -ENOMEM;
  		goto out;
  	}
86166b7bc   Jiri Kosina   HID: add hidraw i...
139
140
141
  
  	if (copy_from_user(buf, buffer, count)) {
  		ret = -EFAULT;
2e57480b2   Jiri Kosina   HID: remove BKL f...
142
  		goto out_free;
86166b7bc   Jiri Kosina   HID: add hidraw i...
143
  	}
b4dbde9da   Alan Ott   HID: Add Support ...
144
  	ret = dev->hid_output_raw_report(dev, buf, count, report_type);
2e57480b2   Jiri Kosina   HID: remove BKL f...
145
  out_free:
86166b7bc   Jiri Kosina   HID: add hidraw i...
146
  	kfree(buf);
2e57480b2   Jiri Kosina   HID: remove BKL f...
147
  out:
b4dbde9da   Alan Ott   HID: Add Support ...
148
149
150
151
152
153
154
155
156
  	return ret;
  }
  
  /* the first byte is expected to be a report number */
  static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
  {
  	ssize_t ret;
  	mutex_lock(&minors_lock);
  	ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
2e57480b2   Jiri Kosina   HID: remove BKL f...
157
  	mutex_unlock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
158
159
  	return ret;
  }
b4dbde9da   Alan Ott   HID: Add Support ...
160
161
  
  /* This function performs a Get_Report transfer over the control endpoint
d2a1cfebe   Jiri Kosina   HID: hidraw: fix ...
162
163
164
165
166
   * per section 7.2.1 of the HID specification, version 1.1.  The first byte
   * of buffer is the report number to request, or 0x0 if the defice does not
   * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
   * or HID_INPUT_REPORT.  This function is to be called with the minors_lock
   *  mutex held. */
b4dbde9da   Alan Ott   HID: Add Support ...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
  {
  	unsigned int minor = iminor(file->f_path.dentry->d_inode);
  	struct hid_device *dev;
  	__u8 *buf;
  	int ret = 0, len;
  	unsigned char report_number;
  
  	dev = hidraw_table[minor]->hid;
  
  	if (!dev->hid_get_raw_report) {
  		ret = -ENODEV;
  		goto out;
  	}
  
  	if (count > HID_MAX_BUFFER_SIZE) {
  		printk(KERN_WARNING "hidraw: pid %d passed too large report
  ",
  				task_pid_nr(current));
  		ret = -EINVAL;
  		goto out;
  	}
  
  	if (count < 2) {
  		printk(KERN_WARNING "hidraw: pid %d passed too short report
  ",
  				task_pid_nr(current));
  		ret = -EINVAL;
  		goto out;
  	}
  
  	buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
  	if (!buf) {
  		ret = -ENOMEM;
  		goto out;
  	}
  
  	/* Read the first byte from the user. This is the report number,
d2a1cfebe   Jiri Kosina   HID: hidraw: fix ...
205
  	 * which is passed to dev->hid_get_raw_report(). */
b4dbde9da   Alan Ott   HID: Add Support ...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  	if (copy_from_user(&report_number, buffer, 1)) {
  		ret = -EFAULT;
  		goto out_free;
  	}
  
  	ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
  
  	if (ret < 0)
  		goto out_free;
  
  	len = (ret < count) ? ret : count;
  
  	if (copy_to_user(buffer, buf, len)) {
  		ret = -EFAULT;
  		goto out_free;
  	}
  
  	ret = len;
  
  out_free:
  	kfree(buf);
  out:
  	return ret;
  }
86166b7bc   Jiri Kosina   HID: add hidraw i...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  static unsigned int hidraw_poll(struct file *file, poll_table *wait)
  {
  	struct hidraw_list *list = file->private_data;
  
  	poll_wait(file, &list->hidraw->wait, wait);
  	if (list->head != list->tail)
  		return POLLIN | POLLRDNORM;
  	if (!list->hidraw->exist)
  		return POLLERR | POLLHUP;
  	return 0;
  }
  
  static int hidraw_open(struct inode *inode, struct file *file)
  {
  	unsigned int minor = iminor(inode);
  	struct hidraw *dev;
  	struct hidraw_list *list;
  	int err = 0;
  
  	if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
  		err = -ENOMEM;
  		goto out;
  	}
7d672cd75   Oliver Neukum   HID: fix locking ...
253
  	mutex_lock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
254
  	if (!hidraw_table[minor]) {
86166b7bc   Jiri Kosina   HID: add hidraw i...
255
256
257
258
259
260
261
262
263
264
  		err = -ENODEV;
  		goto out_unlock;
  	}
  
  	list->hidraw = hidraw_table[minor];
  	mutex_init(&list->read_mutex);
  	list_add_tail(&list->node, &hidraw_table[minor]->list);
  	file->private_data = list;
  
  	dev = hidraw_table[minor];
7d672cd75   Oliver Neukum   HID: fix locking ...
265
  	if (!dev->open++) {
5bea7660b   Dmitry Torokhov   HID: add hid_hw_o...
266
  		err = hid_hw_power(dev->hid, PM_HINT_FULLON);
f554ff803   Amit Nagal   HID: hidraw: open...
267
268
  		if (err < 0) {
  			dev->open--;
5bea7660b   Dmitry Torokhov   HID: add hid_hw_o...
269
  			goto out_unlock;
f554ff803   Amit Nagal   HID: hidraw: open...
270
  		}
5bea7660b   Dmitry Torokhov   HID: add hid_hw_o...
271
272
  
  		err = hid_hw_open(dev->hid);
0361a28d3   Oliver Neukum   HID: autosuspend ...
273
  		if (err < 0) {
5bea7660b   Dmitry Torokhov   HID: add hid_hw_o...
274
  			hid_hw_power(dev->hid, PM_HINT_NORMAL);
7d672cd75   Oliver Neukum   HID: fix locking ...
275
  			dev->open--;
0361a28d3   Oliver Neukum   HID: autosuspend ...
276
  		}
7d672cd75   Oliver Neukum   HID: fix locking ...
277
  	}
86166b7bc   Jiri Kosina   HID: add hidraw i...
278
279
  
  out_unlock:
7d672cd75   Oliver Neukum   HID: fix locking ...
280
  	mutex_unlock(&minors_lock);
7d672cd75   Oliver Neukum   HID: fix locking ...
281
  out:
1a8962317   Amit Nagal   HID: hidraw: free...
282
283
  	if (err < 0)
  		kfree(list);
86166b7bc   Jiri Kosina   HID: add hidraw i...
284
285
286
287
288
289
290
291
292
  	return err;
  
  }
  
  static int hidraw_release(struct inode * inode, struct file * file)
  {
  	unsigned int minor = iminor(inode);
  	struct hidraw *dev;
  	struct hidraw_list *list = file->private_data;
cb174681a   Jiri Slaby   HID: hidraw: fix ...
293
  	int ret;
86166b7bc   Jiri Kosina   HID: add hidraw i...
294

cb174681a   Jiri Slaby   HID: hidraw: fix ...
295
296
297
298
299
  	mutex_lock(&minors_lock);
  	if (!hidraw_table[minor]) {
  		ret = -ENODEV;
  		goto unlock;
  	}
86166b7bc   Jiri Kosina   HID: add hidraw i...
300
301
302
  
  	list_del(&list->node);
  	dev = hidraw_table[minor];
b8a832b1c   Oliver Neukum   HID: fix referenc...
303
  	if (!--dev->open) {
0361a28d3   Oliver Neukum   HID: autosuspend ...
304
  		if (list->hidraw->exist) {
5bea7660b   Dmitry Torokhov   HID: add hid_hw_o...
305
306
  			hid_hw_power(dev->hid, PM_HINT_NORMAL);
  			hid_hw_close(dev->hid);
0361a28d3   Oliver Neukum   HID: autosuspend ...
307
  		} else {
86166b7bc   Jiri Kosina   HID: add hidraw i...
308
  			kfree(list->hidraw);
0361a28d3   Oliver Neukum   HID: autosuspend ...
309
  		}
86166b7bc   Jiri Kosina   HID: add hidraw i...
310
  	}
4db1c62c9   Jiri Kosina   HID: fix memory l...
311
  	kfree(list);
cb174681a   Jiri Slaby   HID: hidraw: fix ...
312
313
314
  	ret = 0;
  unlock:
  	mutex_unlock(&minors_lock);
4db1c62c9   Jiri Kosina   HID: fix memory l...
315

cb174681a   Jiri Slaby   HID: hidraw: fix ...
316
  	return ret;
86166b7bc   Jiri Kosina   HID: add hidraw i...
317
  }
979c407e3   Alan Cox   HID: Push down BK...
318
319
  static long hidraw_ioctl(struct file *file, unsigned int cmd,
  							unsigned long arg)
86166b7bc   Jiri Kosina   HID: add hidraw i...
320
  {
979c407e3   Alan Cox   HID: Push down BK...
321
  	struct inode *inode = file->f_path.dentry->d_inode;
86166b7bc   Jiri Kosina   HID: add hidraw i...
322
  	unsigned int minor = iminor(inode);
979c407e3   Alan Cox   HID: Push down BK...
323
  	long ret = 0;
2e57480b2   Jiri Kosina   HID: remove BKL f...
324
  	struct hidraw *dev;
86166b7bc   Jiri Kosina   HID: add hidraw i...
325
  	void __user *user_arg = (void __user*) arg;
2e57480b2   Jiri Kosina   HID: remove BKL f...
326
327
  	mutex_lock(&minors_lock);
  	dev = hidraw_table[minor];
d20d5ffab   Antonio Ospite   HID: hidraw, fix ...
328
329
330
331
  	if (!dev) {
  		ret = -ENODEV;
  		goto out;
  	}
2e57480b2   Jiri Kosina   HID: remove BKL f...
332

86166b7bc   Jiri Kosina   HID: add hidraw i...
333
334
335
  	switch (cmd) {
  		case HIDIOCGRDESCSIZE:
  			if (put_user(dev->hid->rsize, (int __user *)arg))
979c407e3   Alan Cox   HID: Push down BK...
336
337
  				ret = -EFAULT;
  			break;
86166b7bc   Jiri Kosina   HID: add hidraw i...
338
339
340
341
342
343
  
  		case HIDIOCGRDESC:
  			{
  				__u32 len;
  
  				if (get_user(len, (int __user *)arg))
979c407e3   Alan Cox   HID: Push down BK...
344
345
346
347
348
349
350
351
352
353
  					ret = -EFAULT;
  				else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
  					ret = -EINVAL;
  				else if (copy_to_user(user_arg + offsetof(
  					struct hidraw_report_descriptor,
  					value[0]),
  					dev->hid->rdesc,
  					min(dev->hid->rsize, len)))
  					ret = -EFAULT;
  				break;
86166b7bc   Jiri Kosina   HID: add hidraw i...
354
355
356
357
358
359
360
361
362
  			}
  		case HIDIOCGRAWINFO:
  			{
  				struct hidraw_devinfo dinfo;
  
  				dinfo.bustype = dev->hid->bus;
  				dinfo.vendor = dev->hid->vendor;
  				dinfo.product = dev->hid->product;
  				if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
979c407e3   Alan Cox   HID: Push down BK...
363
364
  					ret = -EFAULT;
  				break;
86166b7bc   Jiri Kosina   HID: add hidraw i...
365
366
  			}
  		default:
9188e79ec   Jiri Kosina   HID: add phys and...
367
368
  			{
  				struct hid_device *hid = dev->hid;
b4dbde9da   Alan Ott   HID: Add Support ...
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  				if (_IOC_TYPE(cmd) != 'H') {
  					ret = -EINVAL;
  					break;
  				}
  
  				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {
  					int len = _IOC_SIZE(cmd);
  					ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
  					break;
  				}
  				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {
  					int len = _IOC_SIZE(cmd);
  					ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
  					break;
  				}
  
  				/* Begin Read-only ioctls. */
  				if (_IOC_DIR(cmd) != _IOC_READ) {
dfd395aff   Dan Carpenter   HID: unlock prope...
387
388
389
  					ret = -EINVAL;
  					break;
  				}
9188e79ec   Jiri Kosina   HID: add phys and...
390
391
  
  				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {
dd2ed487f   Daniel Mack   HID: 'name' and '...
392
  					int len = strlen(hid->name) + 1;
9188e79ec   Jiri Kosina   HID: add phys and...
393
394
  					if (len > _IOC_SIZE(cmd))
  						len = _IOC_SIZE(cmd);
dfd395aff   Dan Carpenter   HID: unlock prope...
395
  					ret = copy_to_user(user_arg, hid->name, len) ?
9188e79ec   Jiri Kosina   HID: add phys and...
396
  						-EFAULT : len;
dfd395aff   Dan Carpenter   HID: unlock prope...
397
  					break;
9188e79ec   Jiri Kosina   HID: add phys and...
398
399
400
  				}
  
  				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {
dd2ed487f   Daniel Mack   HID: 'name' and '...
401
  					int len = strlen(hid->phys) + 1;
9188e79ec   Jiri Kosina   HID: add phys and...
402
403
  					if (len > _IOC_SIZE(cmd))
  						len = _IOC_SIZE(cmd);
dfd395aff   Dan Carpenter   HID: unlock prope...
404
  					ret = copy_to_user(user_arg, hid->phys, len) ?
9188e79ec   Jiri Kosina   HID: add phys and...
405
  						-EFAULT : len;
dfd395aff   Dan Carpenter   HID: unlock prope...
406
  					break;
9188e79ec   Jiri Kosina   HID: add phys and...
407
  				}
b4dbde9da   Alan Ott   HID: Add Support ...
408
  			}
9188e79ec   Jiri Kosina   HID: add phys and...
409

dfd395aff   Dan Carpenter   HID: unlock prope...
410
  		ret = -ENOTTY;
86166b7bc   Jiri Kosina   HID: add hidraw i...
411
  	}
d20d5ffab   Antonio Ospite   HID: hidraw, fix ...
412
  out:
2e57480b2   Jiri Kosina   HID: remove BKL f...
413
  	mutex_unlock(&minors_lock);
979c407e3   Alan Cox   HID: Push down BK...
414
  	return ret;
86166b7bc   Jiri Kosina   HID: add hidraw i...
415
416
417
418
419
420
421
422
423
  }
  
  static const struct file_operations hidraw_ops = {
  	.owner =        THIS_MODULE,
  	.read =         hidraw_read,
  	.write =        hidraw_write,
  	.poll =         hidraw_poll,
  	.open =         hidraw_open,
  	.release =      hidraw_release,
979c407e3   Alan Cox   HID: Push down BK...
424
  	.unlocked_ioctl = hidraw_ioctl,
ae5e49c79   Alan Ott   HID: hidraw: add ...
425
426
427
  #ifdef CONFIG_COMPAT
  	.compat_ioctl   = hidraw_ioctl,
  #endif
6038f373a   Arnd Bergmann   llseek: automatic...
428
  	.llseek =	noop_llseek,
86166b7bc   Jiri Kosina   HID: add hidraw i...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  };
  
  void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
  {
  	struct hidraw *dev = hid->hidraw;
  	struct hidraw_list *list;
  
  	list_for_each_entry(list, &dev->list, node) {
  		list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
  		list->buffer[list->head].len = len;
  		list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
  		kill_fasync(&list->fasync, SIGIO, POLL_IN);
  	}
  
  	wake_up_interruptible(&dev->wait);
  }
  EXPORT_SYMBOL_GPL(hidraw_report_event);
  
  int hidraw_connect(struct hid_device *hid)
  {
709d27c04   Mariusz Kozlowski   HID: hidraw_conne...
449
  	int minor, result;
86166b7bc   Jiri Kosina   HID: add hidraw i...
450
  	struct hidraw *dev;
bbe281fad   Jiri Kosina   HID: hidraw -- fi...
451
  	/* we accept any HID device, no matter the applications */
86166b7bc   Jiri Kosina   HID: add hidraw i...
452

709d27c04   Mariusz Kozlowski   HID: hidraw_conne...
453
454
455
456
457
  	dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
  	if (!dev)
  		return -ENOMEM;
  
  	result = -EINVAL;
86166b7bc   Jiri Kosina   HID: add hidraw i...
458

7d672cd75   Oliver Neukum   HID: fix locking ...
459
  	mutex_lock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
460
461
462
463
464
465
466
467
  
  	for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
  		if (hidraw_table[minor])
  			continue;
  		hidraw_table[minor] = dev;
  		result = 0;
  		break;
  	}
709d27c04   Mariusz Kozlowski   HID: hidraw_conne...
468
  	if (result) {
7d672cd75   Oliver Neukum   HID: fix locking ...
469
  		mutex_unlock(&minors_lock);
709d27c04   Mariusz Kozlowski   HID: hidraw_conne...
470
  		kfree(dev);
86166b7bc   Jiri Kosina   HID: add hidraw i...
471
  		goto out;
709d27c04   Mariusz Kozlowski   HID: hidraw_conne...
472
  	}
86166b7bc   Jiri Kosina   HID: add hidraw i...
473

aae6c286d   Jiri Kosina   HID: set proper d...
474
  	dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
a9b12619f   Greg Kroah-Hartman   device create: mi...
475
  				 NULL, "%s%d", "hidraw", minor);
86166b7bc   Jiri Kosina   HID: add hidraw i...
476
477
  
  	if (IS_ERR(dev->dev)) {
86166b7bc   Jiri Kosina   HID: add hidraw i...
478
  		hidraw_table[minor] = NULL;
7d672cd75   Oliver Neukum   HID: fix locking ...
479
  		mutex_unlock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
480
  		result = PTR_ERR(dev->dev);
709d27c04   Mariusz Kozlowski   HID: hidraw_conne...
481
  		kfree(dev);
86166b7bc   Jiri Kosina   HID: add hidraw i...
482
483
  		goto out;
  	}
7d672cd75   Oliver Neukum   HID: fix locking ...
484
  	mutex_unlock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  	init_waitqueue_head(&dev->wait);
  	INIT_LIST_HEAD(&dev->list);
  
  	dev->hid = hid;
  	dev->minor = minor;
  
  	dev->exist = 1;
  	hid->hidraw = dev;
  
  out:
  	return result;
  
  }
  EXPORT_SYMBOL_GPL(hidraw_connect);
  
  void hidraw_disconnect(struct hid_device *hid)
  {
  	struct hidraw *hidraw = hid->hidraw;
65b01bd56   James Hogan   HID: hidraw: prot...
503
  	mutex_lock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
504
  	hidraw->exist = 0;
3a22ebe9c   Stefan Achatz   HID: hidraw: fix ...
505
  	device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
86166b7bc   Jiri Kosina   HID: add hidraw i...
506
  	hidraw_table[hidraw->minor] = NULL;
86166b7bc   Jiri Kosina   HID: add hidraw i...
507

86166b7bc   Jiri Kosina   HID: add hidraw i...
508
  	if (hidraw->open) {
5bea7660b   Dmitry Torokhov   HID: add hid_hw_o...
509
  		hid_hw_close(hid);
86166b7bc   Jiri Kosina   HID: add hidraw i...
510
511
512
513
  		wake_up_interruptible(&hidraw->wait);
  	} else {
  		kfree(hidraw);
  	}
65b01bd56   James Hogan   HID: hidraw: prot...
514
  	mutex_unlock(&minors_lock);
86166b7bc   Jiri Kosina   HID: add hidraw i...
515
516
517
518
519
520
521
522
523
524
525
526
527
528
  }
  EXPORT_SYMBOL_GPL(hidraw_disconnect);
  
  int __init hidraw_init(void)
  {
  	int result;
  	dev_t dev_id;
  
  	result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
  			HIDRAW_MAX_DEVICES, "hidraw");
  
  	hidraw_major = MAJOR(dev_id);
  
  	if (result < 0) {
4291ee305   Joe Perches   HID: Add and use ...
529
530
  		pr_warn("can't get major number
  ");
86166b7bc   Jiri Kosina   HID: add hidraw i...
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
  		result = 0;
  		goto out;
  	}
  
  	hidraw_class = class_create(THIS_MODULE, "hidraw");
  	if (IS_ERR(hidraw_class)) {
  		result = PTR_ERR(hidraw_class);
  		unregister_chrdev(hidraw_major, "hidraw");
  		goto out;
  	}
  
          cdev_init(&hidraw_cdev, &hidraw_ops);
          cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
  out:
  	return result;
  }
140ae3eb6   Jiri Slaby   HID: fix hidraw_e...
547
  void hidraw_exit(void)
86166b7bc   Jiri Kosina   HID: add hidraw i...
548
549
550
551
552
553
554
555
  {
  	dev_t dev_id = MKDEV(hidraw_major, 0);
  
  	cdev_del(&hidraw_cdev);
  	class_destroy(hidraw_class);
  	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
  
  }