aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2012-11-01 06:33:26 -0400
committerJiri Kosina <jkosina@suse.cz>2012-11-01 06:33:26 -0400
commitdf0cfd6990347c20ae031f3f34137cba274f1972 (patch)
tree8d5229ab017c67d708b3fd5698bf71317dcc83f0 /drivers
parent8d80da90f53d37cf6caefc61353e1cc3a145b9e0 (diff)
HID: hidraw: put old deallocation mechanism in place
This basically reverts commit 4fe9f8e203fda. It causes multiple problems, namely: - after rmmod/modprobe cycle of bus driver, the input is not claimed any more. This is likely because of misplaced hid_hw_close() - it causes memory corruption on hidraw_list As original patch author is not responding to requests to fix his patch, and the original deallocation mechanism is not exposing any problems, I am reverting back to it. Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/hidraw.c69
1 files changed, 43 insertions, 26 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 17d15bb610d1..7c47fc3f7b2b 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -42,7 +42,6 @@ static struct cdev hidraw_cdev;
42static struct class *hidraw_class; 42static struct class *hidraw_class;
43static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; 43static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
44static DEFINE_MUTEX(minors_lock); 44static DEFINE_MUTEX(minors_lock);
45static void drop_ref(struct hidraw *hid, int exists_bit);
46 45
47static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 46static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
48{ 47{
@@ -114,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
114 __u8 *buf; 113 __u8 *buf;
115 int ret = 0; 114 int ret = 0;
116 115
117 if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { 116 if (!hidraw_table[minor]) {
118 ret = -ENODEV; 117 ret = -ENODEV;
119 goto out; 118 goto out;
120 } 119 }
@@ -262,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
262 } 261 }
263 262
264 mutex_lock(&minors_lock); 263 mutex_lock(&minors_lock);
265 if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { 264 if (!hidraw_table[minor]) {
266 err = -ENODEV; 265 err = -ENODEV;
267 goto out_unlock; 266 goto out_unlock;
268 } 267 }
@@ -299,12 +298,36 @@ out:
299static int hidraw_release(struct inode * inode, struct file * file) 298static int hidraw_release(struct inode * inode, struct file * file)
300{ 299{
301 unsigned int minor = iminor(inode); 300 unsigned int minor = iminor(inode);
301 struct hidraw *dev;
302 struct hidraw_list *list = file->private_data; 302 struct hidraw_list *list = file->private_data;
303 int ret;
304 int i;
305
306 mutex_lock(&minors_lock);
307 if (!hidraw_table[minor]) {
308 ret = -ENODEV;
309 goto unlock;
310 }
303 311
304 drop_ref(hidraw_table[minor], 0);
305 list_del(&list->node); 312 list_del(&list->node);
313 dev = hidraw_table[minor];
314 if (!--dev->open) {
315 if (list->hidraw->exist) {
316 hid_hw_power(dev->hid, PM_HINT_NORMAL);
317 hid_hw_close(dev->hid);
318 } else {
319 kfree(list->hidraw);
320 }
321 }
322
323 for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
324 kfree(list->buffer[i].value);
306 kfree(list); 325 kfree(list);
307 return 0; 326 ret = 0;
327unlock:
328 mutex_unlock(&minors_lock);
329
330 return ret;
308} 331}
309 332
310static long hidraw_ioctl(struct file *file, unsigned int cmd, 333static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -506,7 +529,21 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
506void hidraw_disconnect(struct hid_device *hid) 529void hidraw_disconnect(struct hid_device *hid)
507{ 530{
508 struct hidraw *hidraw = hid->hidraw; 531 struct hidraw *hidraw = hid->hidraw;
509 drop_ref(hidraw, 1); 532
533 mutex_lock(&minors_lock);
534 hidraw->exist = 0;
535
536 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
537
538 hidraw_table[hidraw->minor] = NULL;
539
540 if (hidraw->open) {
541 hid_hw_close(hid);
542 wake_up_interruptible(&hidraw->wait);
543 } else {
544 kfree(hidraw);
545 }
546 mutex_unlock(&minors_lock);
510} 547}
511EXPORT_SYMBOL_GPL(hidraw_disconnect); 548EXPORT_SYMBOL_GPL(hidraw_disconnect);
512 549
@@ -555,23 +592,3 @@ void hidraw_exit(void)
555 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); 592 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
556 593
557} 594}
558
559static void drop_ref(struct hidraw *hidraw, int exists_bit)
560{
561 mutex_lock(&minors_lock);
562 if (exists_bit) {
563 hid_hw_close(hidraw->hid);
564 hidraw->exist = 0;
565 if (hidraw->open)
566 wake_up_interruptible(&hidraw->wait);
567 } else {
568 --hidraw->open;
569 }
570
571 if (!hidraw->open && !hidraw->exist) {
572 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
573 hidraw_table[hidraw->minor] = NULL;
574 kfree(hidraw);
575 }
576 mutex_unlock(&minors_lock);
577}