diff options
Diffstat (limited to 'drivers/hid/usbhid/hiddev.c')
| -rw-r--r-- | drivers/hid/usbhid/hiddev.c | 135 |
1 files changed, 99 insertions, 36 deletions
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 83e851a5ed30..6a98f9f572b0 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c | |||
| @@ -49,6 +49,7 @@ | |||
| 49 | struct hiddev { | 49 | struct hiddev { |
| 50 | int exist; | 50 | int exist; |
| 51 | int open; | 51 | int open; |
| 52 | struct mutex existancelock; | ||
| 52 | wait_queue_head_t wait; | 53 | wait_queue_head_t wait; |
| 53 | struct hid_device *hid; | 54 | struct hid_device *hid; |
| 54 | struct list_head list; | 55 | struct list_head list; |
| @@ -63,6 +64,7 @@ struct hiddev_list { | |||
| 63 | struct fasync_struct *fasync; | 64 | struct fasync_struct *fasync; |
| 64 | struct hiddev *hiddev; | 65 | struct hiddev *hiddev; |
| 65 | struct list_head node; | 66 | struct list_head node; |
| 67 | struct mutex thread_lock; | ||
| 66 | }; | 68 | }; |
| 67 | 69 | ||
| 68 | static struct hiddev *hiddev_table[HIDDEV_MINORS]; | 70 | static struct hiddev *hiddev_table[HIDDEV_MINORS]; |
| @@ -264,29 +266,48 @@ static int hiddev_release(struct inode * inode, struct file * file) | |||
| 264 | static int hiddev_open(struct inode *inode, struct file *file) | 266 | static int hiddev_open(struct inode *inode, struct file *file) |
| 265 | { | 267 | { |
| 266 | struct hiddev_list *list; | 268 | struct hiddev_list *list; |
| 267 | unsigned long flags; | 269 | int res; |
| 268 | 270 | ||
| 269 | int i = iminor(inode) - HIDDEV_MINOR_BASE; | 271 | int i = iminor(inode) - HIDDEV_MINOR_BASE; |
| 270 | 272 | ||
| 271 | if (i >= HIDDEV_MINORS || !hiddev_table[i]) | 273 | if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i]) |
| 272 | return -ENODEV; | 274 | return -ENODEV; |
| 273 | 275 | ||
| 274 | if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) | 276 | if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) |
| 275 | return -ENOMEM; | 277 | return -ENOMEM; |
| 278 | mutex_init(&list->thread_lock); | ||
| 276 | 279 | ||
| 277 | list->hiddev = hiddev_table[i]; | 280 | list->hiddev = hiddev_table[i]; |
| 278 | 281 | ||
| 279 | spin_lock_irqsave(&list->hiddev->list_lock, flags); | ||
| 280 | list_add_tail(&list->node, &hiddev_table[i]->list); | ||
| 281 | spin_unlock_irqrestore(&list->hiddev->list_lock, flags); | ||
| 282 | 282 | ||
| 283 | file->private_data = list; | 283 | file->private_data = list; |
| 284 | 284 | ||
| 285 | if (!list->hiddev->open++) | 285 | /* |
| 286 | if (list->hiddev->exist) | 286 | * no need for locking because the USB major number |
| 287 | usbhid_open(hiddev_table[i]->hid); | 287 | * is shared which usbcore guards against disconnect |
| 288 | */ | ||
| 289 | if (list->hiddev->exist) { | ||
| 290 | if (!list->hiddev->open++) { | ||
| 291 | res = usbhid_open(hiddev_table[i]->hid); | ||
| 292 | if (res < 0) { | ||
| 293 | res = -EIO; | ||
| 294 | goto bail; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | } else { | ||
| 298 | res = -ENODEV; | ||
| 299 | goto bail; | ||
| 300 | } | ||
| 301 | |||
| 302 | spin_lock_irq(&list->hiddev->list_lock); | ||
| 303 | list_add_tail(&list->node, &hiddev_table[i]->list); | ||
| 304 | spin_unlock_irq(&list->hiddev->list_lock); | ||
| 288 | 305 | ||
| 289 | return 0; | 306 | return 0; |
| 307 | bail: | ||
| 308 | file->private_data = NULL; | ||
| 309 | kfree(list->hiddev); | ||
| 310 | return res; | ||
| 290 | } | 311 | } |
| 291 | 312 | ||
| 292 | /* | 313 | /* |
| @@ -305,7 +326,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun | |||
| 305 | DECLARE_WAITQUEUE(wait, current); | 326 | DECLARE_WAITQUEUE(wait, current); |
| 306 | struct hiddev_list *list = file->private_data; | 327 | struct hiddev_list *list = file->private_data; |
| 307 | int event_size; | 328 | int event_size; |
| 308 | int retval = 0; | 329 | int retval; |
| 309 | 330 | ||
| 310 | event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? | 331 | event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? |
| 311 | sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); | 332 | sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); |
| @@ -313,10 +334,14 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun | |||
| 313 | if (count < event_size) | 334 | if (count < event_size) |
| 314 | return 0; | 335 | return 0; |
| 315 | 336 | ||
| 337 | /* lock against other threads */ | ||
| 338 | retval = mutex_lock_interruptible(&list->thread_lock); | ||
| 339 | if (retval) | ||
| 340 | return -ERESTARTSYS; | ||
| 341 | |||
| 316 | while (retval == 0) { | 342 | while (retval == 0) { |
| 317 | if (list->head == list->tail) { | 343 | if (list->head == list->tail) { |
| 318 | add_wait_queue(&list->hiddev->wait, &wait); | 344 | prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE); |
| 319 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 320 | 345 | ||
| 321 | while (list->head == list->tail) { | 346 | while (list->head == list->tail) { |
| 322 | if (file->f_flags & O_NONBLOCK) { | 347 | if (file->f_flags & O_NONBLOCK) { |
| @@ -332,35 +357,45 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun | |||
| 332 | break; | 357 | break; |
| 333 | } | 358 | } |
| 334 | 359 | ||
| 360 | /* let O_NONBLOCK tasks run */ | ||
| 361 | mutex_unlock(&list->thread_lock); | ||
| 335 | schedule(); | 362 | schedule(); |
| 363 | if (mutex_lock_interruptible(&list->thread_lock)) | ||
| 364 | return -EINTR; | ||
| 336 | set_current_state(TASK_INTERRUPTIBLE); | 365 | set_current_state(TASK_INTERRUPTIBLE); |
| 337 | } | 366 | } |
| 367 | finish_wait(&list->hiddev->wait, &wait); | ||
| 338 | 368 | ||
| 339 | set_current_state(TASK_RUNNING); | ||
| 340 | remove_wait_queue(&list->hiddev->wait, &wait); | ||
| 341 | } | 369 | } |
| 342 | 370 | ||
| 343 | if (retval) | 371 | if (retval) { |
| 372 | mutex_unlock(&list->thread_lock); | ||
| 344 | return retval; | 373 | return retval; |
| 374 | } | ||
| 345 | 375 | ||
| 346 | 376 | ||
| 347 | while (list->head != list->tail && | 377 | while (list->head != list->tail && |
| 348 | retval + event_size <= count) { | 378 | retval + event_size <= count) { |
| 349 | if ((list->flags & HIDDEV_FLAG_UREF) == 0) { | 379 | if ((list->flags & HIDDEV_FLAG_UREF) == 0) { |
| 350 | if (list->buffer[list->tail].field_index != | 380 | if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) { |
| 351 | HID_FIELD_INDEX_NONE) { | ||
| 352 | struct hiddev_event event; | 381 | struct hiddev_event event; |
| 382 | |||
| 353 | event.hid = list->buffer[list->tail].usage_code; | 383 | event.hid = list->buffer[list->tail].usage_code; |
| 354 | event.value = list->buffer[list->tail].value; | 384 | event.value = list->buffer[list->tail].value; |
| 355 | if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) | 385 | if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) { |
| 386 | mutex_unlock(&list->thread_lock); | ||
| 356 | return -EFAULT; | 387 | return -EFAULT; |
| 388 | } | ||
| 357 | retval += sizeof(struct hiddev_event); | 389 | retval += sizeof(struct hiddev_event); |
| 358 | } | 390 | } |
| 359 | } else { | 391 | } else { |
| 360 | if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || | 392 | if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || |
| 361 | (list->flags & HIDDEV_FLAG_REPORT) != 0) { | 393 | (list->flags & HIDDEV_FLAG_REPORT) != 0) { |
| 362 | if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) | 394 | |
| 395 | if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) { | ||
| 396 | mutex_unlock(&list->thread_lock); | ||
| 363 | return -EFAULT; | 397 | return -EFAULT; |
| 398 | } | ||
| 364 | retval += sizeof(struct hiddev_usage_ref); | 399 | retval += sizeof(struct hiddev_usage_ref); |
| 365 | } | 400 | } |
| 366 | } | 401 | } |
| @@ -368,6 +403,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun | |||
| 368 | } | 403 | } |
| 369 | 404 | ||
| 370 | } | 405 | } |
| 406 | mutex_unlock(&list->thread_lock); | ||
| 371 | 407 | ||
| 372 | return retval; | 408 | return retval; |
| 373 | } | 409 | } |
| @@ -555,7 +591,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 555 | struct hid_field *field; | 591 | struct hid_field *field; |
| 556 | struct usbhid_device *usbhid = hid->driver_data; | 592 | struct usbhid_device *usbhid = hid->driver_data; |
| 557 | void __user *user_arg = (void __user *)arg; | 593 | void __user *user_arg = (void __user *)arg; |
| 558 | int i; | 594 | int i, r; |
| 559 | 595 | ||
| 560 | /* Called without BKL by compat methods so no BKL taken */ | 596 | /* Called without BKL by compat methods so no BKL taken */ |
| 561 | 597 | ||
| @@ -619,10 +655,22 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 619 | } | 655 | } |
| 620 | 656 | ||
| 621 | case HIDIOCGSTRING: | 657 | case HIDIOCGSTRING: |
| 622 | return hiddev_ioctl_string(hiddev, cmd, user_arg); | 658 | mutex_lock(&hiddev->existancelock); |
| 659 | if (!hiddev->exist) | ||
| 660 | r = hiddev_ioctl_string(hiddev, cmd, user_arg); | ||
| 661 | else | ||
| 662 | r = -ENODEV; | ||
| 663 | mutex_unlock(&hiddev->existancelock); | ||
| 664 | return r; | ||
| 623 | 665 | ||
| 624 | case HIDIOCINITREPORT: | 666 | case HIDIOCINITREPORT: |
| 667 | mutex_lock(&hiddev->existancelock); | ||
| 668 | if (!hiddev->exist) { | ||
| 669 | mutex_unlock(&hiddev->existancelock); | ||
| 670 | return -ENODEV; | ||
| 671 | } | ||
| 625 | usbhid_init_reports(hid); | 672 | usbhid_init_reports(hid); |
| 673 | mutex_unlock(&hiddev->existancelock); | ||
| 626 | 674 | ||
| 627 | return 0; | 675 | return 0; |
| 628 | 676 | ||
| @@ -636,8 +684,12 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 636 | if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) | 684 | if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) |
| 637 | return -EINVAL; | 685 | return -EINVAL; |
| 638 | 686 | ||
| 639 | usbhid_submit_report(hid, report, USB_DIR_IN); | 687 | mutex_lock(&hiddev->existancelock); |
| 640 | usbhid_wait_io(hid); | 688 | if (hiddev->exist) { |
| 689 | usbhid_submit_report(hid, report, USB_DIR_IN); | ||
| 690 | usbhid_wait_io(hid); | ||
| 691 | } | ||
| 692 | mutex_unlock(&hiddev->existancelock); | ||
| 641 | 693 | ||
| 642 | return 0; | 694 | return 0; |
| 643 | 695 | ||
| @@ -651,8 +703,12 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 651 | if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) | 703 | if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) |
| 652 | return -EINVAL; | 704 | return -EINVAL; |
| 653 | 705 | ||
| 654 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 706 | mutex_lock(&hiddev->existancelock); |
| 655 | usbhid_wait_io(hid); | 707 | if (hiddev->exist) { |
| 708 | usbhid_submit_report(hid, report, USB_DIR_OUT); | ||
| 709 | usbhid_wait_io(hid); | ||
| 710 | } | ||
| 711 | mutex_unlock(&hiddev->existancelock); | ||
| 656 | 712 | ||
| 657 | return 0; | 713 | return 0; |
| 658 | 714 | ||
| @@ -710,7 +766,13 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 710 | case HIDIOCGUSAGES: | 766 | case HIDIOCGUSAGES: |
| 711 | case HIDIOCSUSAGES: | 767 | case HIDIOCSUSAGES: |
| 712 | case HIDIOCGCOLLECTIONINDEX: | 768 | case HIDIOCGCOLLECTIONINDEX: |
| 713 | return hiddev_ioctl_usage(hiddev, cmd, user_arg); | 769 | mutex_lock(&hiddev->existancelock); |
| 770 | if (hiddev->exist) | ||
| 771 | r = hiddev_ioctl_usage(hiddev, cmd, user_arg); | ||
| 772 | else | ||
| 773 | r = -ENODEV; | ||
| 774 | mutex_unlock(&hiddev->existancelock); | ||
| 775 | return r; | ||
| 714 | 776 | ||
| 715 | case HIDIOCGCOLLECTIONINFO: | 777 | case HIDIOCGCOLLECTIONINFO: |
| 716 | if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) | 778 | if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) |
| @@ -808,23 +870,22 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) | |||
| 808 | if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) | 870 | if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) |
| 809 | return -1; | 871 | return -1; |
| 810 | 872 | ||
| 811 | retval = usb_register_dev(usbhid->intf, &hiddev_class); | ||
| 812 | if (retval) { | ||
| 813 | err_hid("Not able to get a minor for this device."); | ||
| 814 | kfree(hiddev); | ||
| 815 | return -1; | ||
| 816 | } | ||
| 817 | |||
| 818 | init_waitqueue_head(&hiddev->wait); | 873 | init_waitqueue_head(&hiddev->wait); |
| 819 | INIT_LIST_HEAD(&hiddev->list); | 874 | INIT_LIST_HEAD(&hiddev->list); |
| 820 | spin_lock_init(&hiddev->list_lock); | 875 | spin_lock_init(&hiddev->list_lock); |
| 876 | mutex_init(&hiddev->existancelock); | ||
| 821 | hiddev->hid = hid; | 877 | hiddev->hid = hid; |
| 822 | hiddev->exist = 1; | 878 | hiddev->exist = 1; |
| 823 | 879 | ||
| 824 | hid->minor = usbhid->intf->minor; | 880 | retval = usb_register_dev(usbhid->intf, &hiddev_class); |
| 825 | hid->hiddev = hiddev; | 881 | if (retval) { |
| 826 | 882 | err_hid("Not able to get a minor for this device."); | |
| 827 | hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; | 883 | kfree(hiddev); |
| 884 | return -1; | ||
| 885 | } else { | ||
| 886 | hid->minor = usbhid->intf->minor; | ||
| 887 | hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; | ||
| 888 | } | ||
| 828 | 889 | ||
| 829 | return 0; | 890 | return 0; |
| 830 | } | 891 | } |
| @@ -839,7 +900,9 @@ void hiddev_disconnect(struct hid_device *hid) | |||
| 839 | struct hiddev *hiddev = hid->hiddev; | 900 | struct hiddev *hiddev = hid->hiddev; |
| 840 | struct usbhid_device *usbhid = hid->driver_data; | 901 | struct usbhid_device *usbhid = hid->driver_data; |
| 841 | 902 | ||
| 903 | mutex_lock(&hiddev->existancelock); | ||
| 842 | hiddev->exist = 0; | 904 | hiddev->exist = 0; |
| 905 | mutex_unlock(&hiddev->existancelock); | ||
| 843 | 906 | ||
| 844 | hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; | 907 | hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; |
| 845 | usb_deregister_dev(usbhid->intf, &hiddev_class); | 908 | usb_deregister_dev(usbhid->intf, &hiddev_class); |
