summaryrefslogtreecommitdiffstats
path: root/drivers/hid/hidraw.c
diff options
context:
space:
mode:
authorRatan Nalumasu <ratan@google.com>2012-09-22 20:46:30 +0200
committerJiri Kosina <jkosina@suse.cz>2012-10-01 10:27:36 +0200
commit4fe9f8e203fdad1524c04beb390f3c6099781ed9 (patch)
treeb3f6e92fb068c718fd2f6a9b345b03f3dda970ed /drivers/hid/hidraw.c
parentMerge branch 'upstream-fixes' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff)
downloadlinux-4fe9f8e203fdad1524c04beb390f3c6099781ed9.tar.xz
linux-4fe9f8e203fdad1524c04beb390f3c6099781ed9.zip
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 '')
-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;
static struct class *hidraw_class;
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DEFINE_MUTEX(minors_lock);
+static void drop_ref(struct hidraw *hid, int exists_bit);
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
@@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
}
@@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
}
@@ -298,36 +299,12 @@ out:
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;
- int ret;
- int i;
-
- mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
- ret = -ENODEV;
- goto unlock;
- }
+ drop_ref(hidraw_table[minor], 0);
list_del(&list->node);
- dev = hidraw_table[minor];
- if (!--dev->open) {
- if (list->hidraw->exist) {
- hid_hw_power(dev->hid, PM_HINT_NORMAL);
- hid_hw_close(dev->hid);
- } else {
- kfree(list->hidraw);
- }
- }
-
- for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
- kfree(list->buffer[i].value);
kfree(list);
- ret = 0;
-unlock:
- mutex_unlock(&minors_lock);
-
- return ret;
+ return 0;
}
static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
void hidraw_disconnect(struct hid_device *hid)
{
struct hidraw *hidraw = hid->hidraw;
-
- mutex_lock(&minors_lock);
- hidraw->exist = 0;
-
- device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
-
- hidraw_table[hidraw->minor] = NULL;
-
- if (hidraw->open) {
- hid_hw_close(hid);
- wake_up_interruptible(&hidraw->wait);
- } else {
- kfree(hidraw);
- }
- mutex_unlock(&minors_lock);
+ drop_ref(hidraw, 1);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
@@ -585,3 +548,23 @@ void hidraw_exit(void)
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
}
+
+static void drop_ref(struct hidraw *hidraw, int exists_bit)
+{
+ mutex_lock(&minors_lock);
+ if (exists_bit) {
+ hid_hw_close(hidraw->hid);
+ hidraw->exist = 0;
+ if (hidraw->open)
+ wake_up_interruptible(&hidraw->wait);
+ } else {
+ --hidraw->open;
+ }
+
+ if (!hidraw->open && !hidraw->exist) {
+ device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+ hidraw_table[hidraw->minor] = NULL;
+ kfree(hidraw);
+ }
+ mutex_unlock(&minors_lock);
+}