aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorRatan Nalumasu <ratan@google.com>2012-09-22 14:46:30 -0400
committerJiri Kosina <jkosina@suse.cz>2012-10-01 04:27:36 -0400
commit4fe9f8e203fdad1524c04beb390f3c6099781ed9 (patch)
treeb3f6e92fb068c718fd2f6a9b345b03f3dda970ed /drivers/hid
parentcead24c1181ad199354bd08013d0f9d08b66691b (diff)
HID: hidraw: don't deallocate memory when it is in use
When a device is unplugged, wait for all processes that have opened the device to close before deallocating the device. Signed-off-by: Ratan Nalumasu <ratan@google.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hidraw.c69
1 files changed, 26 insertions, 43 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 3b6f7bf5a77e..c46c5f1037f4 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -42,6 +42,7 @@ 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);
45 46
46static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 47static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
47{ 48{
@@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
113 __u8 *buf; 114 __u8 *buf;
114 int ret = 0; 115 int ret = 0;
115 116
116 if (!hidraw_table[minor]) { 117 if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
117 ret = -ENODEV; 118 ret = -ENODEV;
118 goto out; 119 goto out;
119 } 120 }
@@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
261 } 262 }
262 263
263 mutex_lock(&minors_lock); 264 mutex_lock(&minors_lock);
264 if (!hidraw_table[minor]) { 265 if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
265 err = -ENODEV; 266 err = -ENODEV;
266 goto out_unlock; 267 goto out_unlock;
267 } 268 }
@@ -298,36 +299,12 @@ out:
298static int hidraw_release(struct inode * inode, struct file * file) 299static int hidraw_release(struct inode * inode, struct file * file)
299{ 300{
300 unsigned int minor = iminor(inode); 301 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 }
311 303
304 drop_ref(hidraw_table[minor], 0);
312 list_del(&list->node); 305 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);
325 kfree(list); 306 kfree(list);
326 ret = 0; 307 return 0;
327unlock:
328 mutex_unlock(&minors_lock);
329
330 return ret;
331} 308}
332 309
333static long hidraw_ioctl(struct file *file, unsigned int cmd, 310static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
529void hidraw_disconnect(struct hid_device *hid) 506void hidraw_disconnect(struct hid_device *hid)
530{ 507{
531 struct hidraw *hidraw = hid->hidraw; 508 struct hidraw *hidraw = hid->hidraw;
532 509 drop_ref(hidraw, 1);
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);
547} 510}
548EXPORT_SYMBOL_GPL(hidraw_disconnect); 511EXPORT_SYMBOL_GPL(hidraw_disconnect);
549 512
@@ -585,3 +548,23 @@ void hidraw_exit(void)
585 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); 548 unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
586 549
587} 550}
551
552static void drop_ref(struct hidraw *hidraw, int exists_bit)
553{
554 mutex_lock(&minors_lock);
555 if (exists_bit) {
556 hid_hw_close(hidraw->hid);
557 hidraw->exist = 0;
558 if (hidraw->open)
559 wake_up_interruptible(&hidraw->wait);
560 } else {
561 --hidraw->open;
562 }
563
564 if (!hidraw->open && !hidraw->exist) {
565 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
566 hidraw_table[hidraw->minor] = NULL;
567 kfree(hidraw);
568 }
569 mutex_unlock(&minors_lock);
570}