summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2012-05-02 03:13:37 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2012-05-02 03:23:58 -0400
commit2872a9b521ac936c7a8525a8c2bdfb9b4ccf5cfc (patch)
tree613f1456fe437419bbc014f84d7d3c2e73568743 /drivers/input
parentdba4258068f822b7dafc78c28fe9c99c551eca7e (diff)
Input: evdev - properly handle read/write with count 0
According to the standard count 0 is special - no IO should happen but we can check error conditions (device gone away, etc), and return 0 if there are no errors. We used to return -EINVAL instead and we also could return 0 if an event was "stolen" by another thread. Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/evdev.c61
1 files changed, 38 insertions, 23 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 9226b4d9118f..6c58bfff01a3 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -345,7 +345,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
345 struct input_event event; 345 struct input_event event;
346 int retval = 0; 346 int retval = 0;
347 347
348 if (count < input_event_size()) 348 if (count != 0 && count < input_event_size())
349 return -EINVAL; 349 return -EINVAL;
350 350
351 retval = mutex_lock_interruptible(&evdev->mutex); 351 retval = mutex_lock_interruptible(&evdev->mutex);
@@ -357,7 +357,8 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
357 goto out; 357 goto out;
358 } 358 }
359 359
360 do { 360 while (retval + input_event_size() <= count) {
361
361 if (input_event_from_user(buffer + retval, &event)) { 362 if (input_event_from_user(buffer + retval, &event)) {
362 retval = -EFAULT; 363 retval = -EFAULT;
363 goto out; 364 goto out;
@@ -366,7 +367,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
366 367
367 input_inject_event(&evdev->handle, 368 input_inject_event(&evdev->handle,
368 event.type, event.code, event.value); 369 event.type, event.code, event.value);
369 } while (retval + input_event_size() <= count); 370 }
370 371
371 out: 372 out:
372 mutex_unlock(&evdev->mutex); 373 mutex_unlock(&evdev->mutex);
@@ -397,35 +398,49 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
397 struct evdev_client *client = file->private_data; 398 struct evdev_client *client = file->private_data;
398 struct evdev *evdev = client->evdev; 399 struct evdev *evdev = client->evdev;
399 struct input_event event; 400 struct input_event event;
400 int retval = 0; 401 size_t read = 0;
402 int error;
401 403
402 if (count < input_event_size()) 404 if (count != 0 && count < input_event_size())
403 return -EINVAL; 405 return -EINVAL;
404 406
405 if (!(file->f_flags & O_NONBLOCK)) { 407 for (;;) {
406 retval = wait_event_interruptible(evdev->wait, 408 if (!evdev->exist)
407 client->packet_head != client->tail || 409 return -ENODEV;
408 !evdev->exist);
409 if (retval)
410 return retval;
411 }
412 410
413 if (!evdev->exist) 411 if (client->packet_head == client->tail &&
414 return -ENODEV; 412 (file->f_flags & O_NONBLOCK))
413 return -EAGAIN;
414
415 /*
416 * count == 0 is special - no IO is done but we check
417 * for error conditions (see above).
418 */
419 if (count == 0)
420 break;
415 421
416 while (retval + input_event_size() <= count && 422 while (read + input_event_size() <= count &&
417 evdev_fetch_next_event(client, &event)) { 423 evdev_fetch_next_event(client, &event)) {
418 424
419 if (input_event_to_user(buffer + retval, &event)) 425 if (input_event_to_user(buffer + read, &event))
420 return -EFAULT; 426 return -EFAULT;
421 427
422 retval += input_event_size(); 428 read += input_event_size();
423 } 429 }
424 430
425 if (retval == 0 && (file->f_flags & O_NONBLOCK)) 431 if (read)
426 return -EAGAIN; 432 break;
427 433
428 return retval; 434 if (!(file->f_flags & O_NONBLOCK)) {
435 error = wait_event_interruptible(evdev->wait,
436 client->packet_head != client->tail ||
437 !evdev->exist);
438 if (error)
439 return error;
440 }
441 }
442
443 return read;
429} 444}
430 445
431/* No kernel lock - fine */ 446/* No kernel lock - fine */