diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2012-11-01 06:33:26 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2012-11-01 06:33:26 -0400 |
| commit | df0cfd6990347c20ae031f3f34137cba274f1972 (patch) | |
| tree | 8d5229ab017c67d708b3fd5698bf71317dcc83f0 | |
| parent | 8d80da90f53d37cf6caefc61353e1cc3a145b9e0 (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>
| -rw-r--r-- | drivers/hid/hidraw.c | 69 |
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; | |||
| 42 | static struct class *hidraw_class; | 42 | static struct class *hidraw_class; |
| 43 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; | 43 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; |
| 44 | static DEFINE_MUTEX(minors_lock); | 44 | static DEFINE_MUTEX(minors_lock); |
| 45 | static void drop_ref(struct hidraw *hid, int exists_bit); | ||
| 46 | 45 | ||
| 47 | static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | 46 | static 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: | |||
| 299 | static int hidraw_release(struct inode * inode, struct file * file) | 298 | static 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; |
| 327 | unlock: | ||
| 328 | mutex_unlock(&minors_lock); | ||
| 329 | |||
| 330 | return ret; | ||
| 308 | } | 331 | } |
| 309 | 332 | ||
| 310 | static long hidraw_ioctl(struct file *file, unsigned int cmd, | 333 | static long hidraw_ioctl(struct file *file, unsigned int cmd, |
| @@ -506,7 +529,21 @@ EXPORT_SYMBOL_GPL(hidraw_connect); | |||
| 506 | void hidraw_disconnect(struct hid_device *hid) | 529 | void 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 | } |
| 511 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | 548 | EXPORT_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 | |||
| 559 | static 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 | } | ||
