Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fe9f8e2 authored by Ratan Nalumasu's avatar Ratan Nalumasu Committed by Jiri Kosina
Browse files

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: default avatarRatan Nalumasu <ratan@google.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent cead24c1
Loading
Loading
Loading
Loading
+26 −43
Original line number Diff line number Diff line
@@ -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 @@ static int hidraw_open(struct inode *inode, struct file *file)
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);
}