diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/misc/uinput.c | 74 |
1 files changed, 44 insertions, 30 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index e74ed9cc6371..1719554fe194 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
@@ -439,6 +439,9 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t | |||
439 | struct uinput_device *udev = file->private_data; | 439 | struct uinput_device *udev = file->private_data; |
440 | int retval; | 440 | int retval; |
441 | 441 | ||
442 | if (count == 0) | ||
443 | return 0; | ||
444 | |||
442 | retval = mutex_lock_interruptible(&udev->mutex); | 445 | retval = mutex_lock_interruptible(&udev->mutex); |
443 | if (retval) | 446 | if (retval) |
444 | return retval; | 447 | return retval; |
@@ -470,48 +473,59 @@ static bool uinput_fetch_next_event(struct uinput_device *udev, | |||
470 | return have_event; | 473 | return have_event; |
471 | } | 474 | } |
472 | 475 | ||
473 | static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | 476 | static ssize_t uinput_events_to_user(struct uinput_device *udev, |
477 | char __user *buffer, size_t count) | ||
474 | { | 478 | { |
475 | struct uinput_device *udev = file->private_data; | ||
476 | struct input_event event; | 479 | struct input_event event; |
477 | int retval = 0; | 480 | size_t read = 0; |
481 | int error = 0; | ||
478 | 482 | ||
479 | if (count != 0 && count < input_event_size()) | 483 | while (read + input_event_size() <= count && |
480 | return -EINVAL; | 484 | uinput_fetch_next_event(udev, &event)) { |
481 | 485 | ||
482 | if (udev->state != UIST_CREATED) | 486 | if (input_event_to_user(buffer + read, &event)) { |
483 | return -ENODEV; | 487 | error = -EFAULT; |
488 | break; | ||
489 | } | ||
484 | 490 | ||
485 | if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) | 491 | read += input_event_size(); |
486 | return -EAGAIN; | 492 | } |
487 | 493 | ||
488 | retval = wait_event_interruptible(udev->waitq, | 494 | return read ?: error; |
489 | udev->head != udev->tail || udev->state != UIST_CREATED); | 495 | } |
490 | if (retval) | ||
491 | return retval; | ||
492 | 496 | ||
493 | retval = mutex_lock_interruptible(&udev->mutex); | 497 | static ssize_t uinput_read(struct file *file, char __user *buffer, |
494 | if (retval) | 498 | size_t count, loff_t *ppos) |
495 | return retval; | 499 | { |
500 | struct uinput_device *udev = file->private_data; | ||
501 | ssize_t retval; | ||
496 | 502 | ||
497 | if (udev->state != UIST_CREATED) { | 503 | if (count != 0 && count < input_event_size()) |
498 | retval = -ENODEV; | 504 | return -EINVAL; |
499 | goto out; | ||
500 | } | ||
501 | 505 | ||
502 | while (retval + input_event_size() <= count && | 506 | do { |
503 | uinput_fetch_next_event(udev, &event)) { | 507 | retval = mutex_lock_interruptible(&udev->mutex); |
508 | if (retval) | ||
509 | return retval; | ||
504 | 510 | ||
505 | if (input_event_to_user(buffer + retval, &event)) { | 511 | if (udev->state != UIST_CREATED) |
506 | retval = -EFAULT; | 512 | retval = -ENODEV; |
507 | goto out; | 513 | else if (udev->head == udev->tail && |
508 | } | 514 | (file->f_flags & O_NONBLOCK)) |
515 | retval = -EAGAIN; | ||
516 | else | ||
517 | retval = uinput_events_to_user(udev, buffer, count); | ||
509 | 518 | ||
510 | retval += input_event_size(); | 519 | mutex_unlock(&udev->mutex); |
511 | } | ||
512 | 520 | ||
513 | out: | 521 | if (retval || count == 0) |
514 | mutex_unlock(&udev->mutex); | 522 | break; |
523 | |||
524 | if (!(file->f_flags & O_NONBLOCK)) | ||
525 | retval = wait_event_interruptible(udev->waitq, | ||
526 | udev->head != udev->tail || | ||
527 | udev->state != UIST_CREATED); | ||
528 | } while (retval == 0); | ||
515 | 529 | ||
516 | return retval; | 530 | return retval; |
517 | } | 531 | } |