diff options
Diffstat (limited to 'drivers/hid/hidraw.c')
| -rw-r--r-- | drivers/hid/hidraw.c | 82 | 
1 files changed, 41 insertions, 41 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index a7451632ceb4..8918dd12bb69 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -113,7 +113,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;  	} @@ -253,6 +253,7 @@ static int hidraw_open(struct inode *inode, struct file *file)  	unsigned int minor = iminor(inode);  	struct hidraw *dev;  	struct hidraw_list *list; +	unsigned long flags;  	int err = 0;  	if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { @@ -261,16 +262,11 @@ 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;  	} -	list->hidraw = hidraw_table[minor]; -	mutex_init(&list->read_mutex); -	list_add_tail(&list->node, &hidraw_table[minor]->list); -	file->private_data = list; -  	dev = hidraw_table[minor];  	if (!dev->open++) {  		err = hid_hw_power(dev->hid, PM_HINT_FULLON); @@ -283,9 +279,16 @@ static int hidraw_open(struct inode *inode, struct file *file)  		if (err < 0) {  			hid_hw_power(dev->hid, PM_HINT_NORMAL);  			dev->open--; +			goto out_unlock;  		}  	} +	list->hidraw = hidraw_table[minor]; +	mutex_init(&list->read_mutex); +	spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); +	list_add_tail(&list->node, &hidraw_table[minor]->list); +	spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); +	file->private_data = list;  out_unlock:  	mutex_unlock(&minors_lock);  out: @@ -302,39 +305,41 @@ static int hidraw_fasync(int fd, struct file *file, int on)  	return fasync_helper(fd, file, on, &list->fasync);  } +static void drop_ref(struct hidraw *hidraw, int exists_bit) +{ +	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); +	} +} +  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; +	unsigned long flags;  	mutex_lock(&minors_lock); -	if (!hidraw_table[minor]) { -		ret = -ENODEV; -		goto unlock; -	} +	spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);  	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); +	spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);  	kfree(list); -	ret = 0; -unlock: -	mutex_unlock(&minors_lock); -	return ret; +	drop_ref(hidraw_table[minor], 0); + +	mutex_unlock(&minors_lock); +	return 0;  }  static long hidraw_ioctl(struct file *file, unsigned int cmd, @@ -457,7 +462,9 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)  	struct hidraw *dev = hid->hidraw;  	struct hidraw_list *list;  	int ret = 0; +	unsigned long flags; +	spin_lock_irqsave(&dev->list_lock, flags);  	list_for_each_entry(list, &dev->list, node) {  		int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -472,6 +479,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)  		list->head = new_head;  		kill_fasync(&list->fasync, SIGIO, POLL_IN);  	} +	spin_unlock_irqrestore(&dev->list_lock, flags);  	wake_up_interruptible(&dev->wait);  	return ret; @@ -518,8 +526,8 @@ int hidraw_connect(struct hid_device *hid)  		goto out;  	} -	mutex_unlock(&minors_lock);  	init_waitqueue_head(&dev->wait); +	spin_lock_init(&dev->list_lock);  	INIT_LIST_HEAD(&dev->list);  	dev->hid = hid; @@ -528,6 +536,7 @@ int hidraw_connect(struct hid_device *hid)  	dev->exist = 1;  	hid->hidraw = dev; +	mutex_unlock(&minors_lock);  out:  	return result; @@ -539,18 +548,9 @@ 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)); +	drop_ref(hidraw, 1); -	hidraw_table[hidraw->minor] = NULL; - -	if (hidraw->open) { -		hid_hw_close(hid); -		wake_up_interruptible(&hidraw->wait); -	} else { -		kfree(hidraw); -	}  	mutex_unlock(&minors_lock);  }  EXPORT_SYMBOL_GPL(hidraw_disconnect);  | 
