diff options
Diffstat (limited to 'drivers/hid/hidraw.c')
| -rw-r--r-- | drivers/hid/hidraw.c | 84 | 
1 files changed, 37 insertions, 47 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 3b6f7bf5a77..17d15bb610d 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); @@ -559,21 +522,28 @@ int __init hidraw_init(void)  	if (result < 0) {  		pr_warn("can't get major number\n"); -		result = 0;  		goto out;  	}  	hidraw_class = class_create(THIS_MODULE, "hidraw");  	if (IS_ERR(hidraw_class)) {  		result = PTR_ERR(hidraw_class); -		unregister_chrdev(hidraw_major, "hidraw"); -		goto out; +		goto error_cdev;  	}          cdev_init(&hidraw_cdev, &hidraw_ops); -        cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); +	result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); +	if (result < 0) +		goto error_class; +  out:  	return result; + +error_class: +	class_destroy(hidraw_class); +error_cdev: +	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); +	goto out;  }  void hidraw_exit(void) @@ -585,3 +555,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); +}  |