diff options
Diffstat (limited to 'drivers/hid/hidraw.c')
-rw-r--r-- | drivers/hid/hidraw.c | 80 |
1 files changed, 40 insertions, 40 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 6f1feb2c2e97..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, | |||
113 | __u8 *buf; | 113 | __u8 *buf; |
114 | int ret = 0; | 114 | int ret = 0; |
115 | 115 | ||
116 | if (!hidraw_table[minor]) { | 116 | if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { |
117 | ret = -ENODEV; | 117 | ret = -ENODEV; |
118 | goto out; | 118 | goto out; |
119 | } | 119 | } |
@@ -253,6 +253,7 @@ static int hidraw_open(struct inode *inode, struct file *file) | |||
253 | unsigned int minor = iminor(inode); | 253 | unsigned int minor = iminor(inode); |
254 | struct hidraw *dev; | 254 | struct hidraw *dev; |
255 | struct hidraw_list *list; | 255 | struct hidraw_list *list; |
256 | unsigned long flags; | ||
256 | int err = 0; | 257 | int err = 0; |
257 | 258 | ||
258 | if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { | 259 | if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { |
@@ -261,16 +262,11 @@ 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 | } |
268 | 269 | ||
269 | list->hidraw = hidraw_table[minor]; | ||
270 | mutex_init(&list->read_mutex); | ||
271 | list_add_tail(&list->node, &hidraw_table[minor]->list); | ||
272 | file->private_data = list; | ||
273 | |||
274 | dev = hidraw_table[minor]; | 270 | dev = hidraw_table[minor]; |
275 | if (!dev->open++) { | 271 | if (!dev->open++) { |
276 | err = hid_hw_power(dev->hid, PM_HINT_FULLON); | 272 | err = hid_hw_power(dev->hid, PM_HINT_FULLON); |
@@ -283,9 +279,16 @@ static int hidraw_open(struct inode *inode, struct file *file) | |||
283 | if (err < 0) { | 279 | if (err < 0) { |
284 | hid_hw_power(dev->hid, PM_HINT_NORMAL); | 280 | hid_hw_power(dev->hid, PM_HINT_NORMAL); |
285 | dev->open--; | 281 | dev->open--; |
282 | goto out_unlock; | ||
286 | } | 283 | } |
287 | } | 284 | } |
288 | 285 | ||
286 | list->hidraw = hidraw_table[minor]; | ||
287 | mutex_init(&list->read_mutex); | ||
288 | spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); | ||
289 | list_add_tail(&list->node, &hidraw_table[minor]->list); | ||
290 | spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); | ||
291 | file->private_data = list; | ||
289 | out_unlock: | 292 | out_unlock: |
290 | mutex_unlock(&minors_lock); | 293 | mutex_unlock(&minors_lock); |
291 | out: | 294 | out: |
@@ -302,39 +305,41 @@ static int hidraw_fasync(int fd, struct file *file, int on) | |||
302 | return fasync_helper(fd, file, on, &list->fasync); | 305 | return fasync_helper(fd, file, on, &list->fasync); |
303 | } | 306 | } |
304 | 307 | ||
308 | static void drop_ref(struct hidraw *hidraw, int exists_bit) | ||
309 | { | ||
310 | if (exists_bit) { | ||
311 | hid_hw_close(hidraw->hid); | ||
312 | hidraw->exist = 0; | ||
313 | if (hidraw->open) | ||
314 | wake_up_interruptible(&hidraw->wait); | ||
315 | } else { | ||
316 | --hidraw->open; | ||
317 | } | ||
318 | |||
319 | if (!hidraw->open && !hidraw->exist) { | ||
320 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
321 | hidraw_table[hidraw->minor] = NULL; | ||
322 | kfree(hidraw); | ||
323 | } | ||
324 | } | ||
325 | |||
305 | static int hidraw_release(struct inode * inode, struct file * file) | 326 | static int hidraw_release(struct inode * inode, struct file * file) |
306 | { | 327 | { |
307 | unsigned int minor = iminor(inode); | 328 | unsigned int minor = iminor(inode); |
308 | struct hidraw *dev; | ||
309 | struct hidraw_list *list = file->private_data; | 329 | struct hidraw_list *list = file->private_data; |
310 | int ret; | 330 | unsigned long flags; |
311 | int i; | ||
312 | 331 | ||
313 | mutex_lock(&minors_lock); | 332 | mutex_lock(&minors_lock); |
314 | if (!hidraw_table[minor]) { | ||
315 | ret = -ENODEV; | ||
316 | goto unlock; | ||
317 | } | ||
318 | 333 | ||
334 | spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); | ||
319 | list_del(&list->node); | 335 | list_del(&list->node); |
320 | dev = hidraw_table[minor]; | 336 | spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); |
321 | if (!--dev->open) { | ||
322 | if (list->hidraw->exist) { | ||
323 | hid_hw_power(dev->hid, PM_HINT_NORMAL); | ||
324 | hid_hw_close(dev->hid); | ||
325 | } else { | ||
326 | kfree(list->hidraw); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) | ||
331 | kfree(list->buffer[i].value); | ||
332 | kfree(list); | 337 | kfree(list); |
333 | ret = 0; | ||
334 | unlock: | ||
335 | mutex_unlock(&minors_lock); | ||
336 | 338 | ||
337 | return ret; | 339 | drop_ref(hidraw_table[minor], 0); |
340 | |||
341 | mutex_unlock(&minors_lock); | ||
342 | return 0; | ||
338 | } | 343 | } |
339 | 344 | ||
340 | static long hidraw_ioctl(struct file *file, unsigned int cmd, | 345 | 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) | |||
457 | struct hidraw *dev = hid->hidraw; | 462 | struct hidraw *dev = hid->hidraw; |
458 | struct hidraw_list *list; | 463 | struct hidraw_list *list; |
459 | int ret = 0; | 464 | int ret = 0; |
465 | unsigned long flags; | ||
460 | 466 | ||
467 | spin_lock_irqsave(&dev->list_lock, flags); | ||
461 | list_for_each_entry(list, &dev->list, node) { | 468 | list_for_each_entry(list, &dev->list, node) { |
462 | int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); | 469 | int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); |
463 | 470 | ||
@@ -472,6 +479,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) | |||
472 | list->head = new_head; | 479 | list->head = new_head; |
473 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | 480 | kill_fasync(&list->fasync, SIGIO, POLL_IN); |
474 | } | 481 | } |
482 | spin_unlock_irqrestore(&dev->list_lock, flags); | ||
475 | 483 | ||
476 | wake_up_interruptible(&dev->wait); | 484 | wake_up_interruptible(&dev->wait); |
477 | return ret; | 485 | return ret; |
@@ -519,6 +527,7 @@ int hidraw_connect(struct hid_device *hid) | |||
519 | } | 527 | } |
520 | 528 | ||
521 | init_waitqueue_head(&dev->wait); | 529 | init_waitqueue_head(&dev->wait); |
530 | spin_lock_init(&dev->list_lock); | ||
522 | INIT_LIST_HEAD(&dev->list); | 531 | INIT_LIST_HEAD(&dev->list); |
523 | 532 | ||
524 | dev->hid = hid; | 533 | dev->hid = hid; |
@@ -539,18 +548,9 @@ void hidraw_disconnect(struct hid_device *hid) | |||
539 | struct hidraw *hidraw = hid->hidraw; | 548 | struct hidraw *hidraw = hid->hidraw; |
540 | 549 | ||
541 | mutex_lock(&minors_lock); | 550 | mutex_lock(&minors_lock); |
542 | hidraw->exist = 0; | ||
543 | 551 | ||
544 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | 552 | drop_ref(hidraw, 1); |
545 | 553 | ||
546 | hidraw_table[hidraw->minor] = NULL; | ||
547 | |||
548 | if (hidraw->open) { | ||
549 | hid_hw_close(hid); | ||
550 | wake_up_interruptible(&hidraw->wait); | ||
551 | } else { | ||
552 | kfree(hidraw); | ||
553 | } | ||
554 | mutex_unlock(&minors_lock); | 554 | mutex_unlock(&minors_lock); |
555 | } | 555 | } |
556 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | 556 | EXPORT_SYMBOL_GPL(hidraw_disconnect); |