diff options
author | Philip Langdale <philipl@overt.org> | 2008-10-16 22:31:42 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-10-27 22:03:42 -0400 |
commit | 2d56f3a32c0e62f99c043d2579840f9731fe5855 (patch) | |
tree | 3bf1539bbed43e5309dcfd634f202bb9ad0f11b2 /drivers/input/misc | |
parent | 49fdf6785fd660e18a1eb4588928f47e9fa29a9a (diff) |
Input: refactor evdev 32bit compat to be shareable with uinput
Currently, evdev has working 32bit compatibility and uinput does not. uinput
needs the input_event code that evdev uses, so let's refactor it so it can
be shared.
[dtor@mail.ru: add fix for force feedback compat issues]
Signed-off-by: Philip Langdale <philipl@overt.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/uinput.c | 172 |
1 files changed, 145 insertions, 27 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 223d56d5555b..46b7caeb2817 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/fs.h> | 37 | #include <linux/fs.h> |
38 | #include <linux/miscdevice.h> | 38 | #include <linux/miscdevice.h> |
39 | #include <linux/uinput.h> | 39 | #include <linux/uinput.h> |
40 | #include "../input-compat.h" | ||
40 | 41 | ||
41 | static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | 42 | static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
42 | { | 43 | { |
@@ -78,6 +79,7 @@ static struct uinput_request* uinput_request_find(struct uinput_device *udev, in | |||
78 | /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ | 79 | /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ |
79 | if (id >= UINPUT_NUM_REQUESTS || id < 0) | 80 | if (id >= UINPUT_NUM_REQUESTS || id < 0) |
80 | return NULL; | 81 | return NULL; |
82 | |||
81 | return udev->requests[id]; | 83 | return udev->requests[id]; |
82 | } | 84 | } |
83 | 85 | ||
@@ -127,6 +129,17 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff | |||
127 | struct uinput_request request; | 129 | struct uinput_request request; |
128 | int retval; | 130 | int retval; |
129 | 131 | ||
132 | /* | ||
133 | * uinput driver does not currently support periodic effects with | ||
134 | * custom waveform since it does not have a way to pass buffer of | ||
135 | * samples (custom_data) to userspace. If ever there is a device | ||
136 | * supporting custom waveforms we would need to define an additional | ||
137 | * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out. | ||
138 | */ | ||
139 | if (effect->type == FF_PERIODIC && | ||
140 | effect->u.periodic.waveform == FF_CUSTOM) | ||
141 | return -EINVAL; | ||
142 | |||
130 | request.id = -1; | 143 | request.id = -1; |
131 | init_completion(&request.done); | 144 | init_completion(&request.done); |
132 | request.code = UI_FF_UPLOAD; | 145 | request.code = UI_FF_UPLOAD; |
@@ -353,15 +366,15 @@ static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char | |||
353 | { | 366 | { |
354 | struct input_event ev; | 367 | struct input_event ev; |
355 | 368 | ||
356 | if (count != sizeof(struct input_event)) | 369 | if (count < input_event_size()) |
357 | return -EINVAL; | 370 | return -EINVAL; |
358 | 371 | ||
359 | if (copy_from_user(&ev, buffer, sizeof(struct input_event))) | 372 | if (input_event_from_user(buffer, &ev)) |
360 | return -EFAULT; | 373 | return -EFAULT; |
361 | 374 | ||
362 | input_event(udev->dev, ev.type, ev.code, ev.value); | 375 | input_event(udev->dev, ev.type, ev.code, ev.value); |
363 | 376 | ||
364 | return sizeof(struct input_event); | 377 | return input_event_size(); |
365 | } | 378 | } |
366 | 379 | ||
367 | static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) | 380 | static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) |
@@ -407,13 +420,13 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, | |||
407 | goto out; | 420 | goto out; |
408 | } | 421 | } |
409 | 422 | ||
410 | while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) { | 423 | while (udev->head != udev->tail && retval + input_event_size() <= count) { |
411 | if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) { | 424 | if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) { |
412 | retval = -EFAULT; | 425 | retval = -EFAULT; |
413 | goto out; | 426 | goto out; |
414 | } | 427 | } |
415 | udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; | 428 | udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; |
416 | retval += sizeof(struct input_event); | 429 | retval += input_event_size(); |
417 | } | 430 | } |
418 | 431 | ||
419 | out: | 432 | out: |
@@ -444,6 +457,93 @@ static int uinput_release(struct inode *inode, struct file *file) | |||
444 | return 0; | 457 | return 0; |
445 | } | 458 | } |
446 | 459 | ||
460 | #ifdef CONFIG_COMPAT | ||
461 | struct uinput_ff_upload_compat { | ||
462 | int request_id; | ||
463 | int retval; | ||
464 | struct ff_effect_compat effect; | ||
465 | struct ff_effect_compat old; | ||
466 | }; | ||
467 | |||
468 | static int uinput_ff_upload_to_user(char __user *buffer, | ||
469 | const struct uinput_ff_upload *ff_up) | ||
470 | { | ||
471 | if (INPUT_COMPAT_TEST) { | ||
472 | struct uinput_ff_upload_compat ff_up_compat; | ||
473 | |||
474 | ff_up_compat.request_id = ff_up->request_id; | ||
475 | ff_up_compat.retval = ff_up->retval; | ||
476 | /* | ||
477 | * It so happens that the pointer that gives us the trouble | ||
478 | * is the last field in the structure. Since we don't support | ||
479 | * custom waveforms in uinput anyway we can just copy the whole | ||
480 | * thing (to the compat size) and ignore the pointer. | ||
481 | */ | ||
482 | memcpy(&ff_up_compat.effect, &ff_up->effect, | ||
483 | sizeof(struct ff_effect_compat)); | ||
484 | memcpy(&ff_up_compat.old, &ff_up->old, | ||
485 | sizeof(struct ff_effect_compat)); | ||
486 | |||
487 | if (copy_to_user(buffer, &ff_up_compat, | ||
488 | sizeof(struct uinput_ff_upload_compat))) | ||
489 | return -EFAULT; | ||
490 | } else { | ||
491 | if (copy_to_user(buffer, ff_up, | ||
492 | sizeof(struct uinput_ff_upload))) | ||
493 | return -EFAULT; | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int uinput_ff_upload_from_user(const char __user *buffer, | ||
500 | struct uinput_ff_upload *ff_up) | ||
501 | { | ||
502 | if (INPUT_COMPAT_TEST) { | ||
503 | struct uinput_ff_upload_compat ff_up_compat; | ||
504 | |||
505 | if (copy_from_user(&ff_up_compat, buffer, | ||
506 | sizeof(struct uinput_ff_upload_compat))) | ||
507 | return -EFAULT; | ||
508 | |||
509 | ff_up->request_id = ff_up_compat.request_id; | ||
510 | ff_up->retval = ff_up_compat.retval; | ||
511 | memcpy(&ff_up->effect, &ff_up_compat.effect, | ||
512 | sizeof(struct ff_effect_compat)); | ||
513 | memcpy(&ff_up->old, &ff_up_compat.old, | ||
514 | sizeof(struct ff_effect_compat)); | ||
515 | |||
516 | } else { | ||
517 | if (copy_from_user(ff_up, buffer, | ||
518 | sizeof(struct uinput_ff_upload))) | ||
519 | return -EFAULT; | ||
520 | } | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | #else | ||
526 | |||
527 | static int uinput_ff_upload_to_user(char __user *buffer, | ||
528 | const struct uinput_ff_upload *ff_up) | ||
529 | { | ||
530 | if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload))) | ||
531 | return -EFAULT; | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int uinput_ff_upload_from_user(const char __user *buffer, | ||
537 | struct uinput_ff_upload *ff_up) | ||
538 | { | ||
539 | if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload))) | ||
540 | return -EFAULT; | ||
541 | |||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | #endif | ||
546 | |||
447 | #define uinput_set_bit(_arg, _bit, _max) \ | 547 | #define uinput_set_bit(_arg, _bit, _max) \ |
448 | ({ \ | 548 | ({ \ |
449 | int __ret = 0; \ | 549 | int __ret = 0; \ |
@@ -455,19 +555,17 @@ static int uinput_release(struct inode *inode, struct file *file) | |||
455 | __ret; \ | 555 | __ret; \ |
456 | }) | 556 | }) |
457 | 557 | ||
458 | static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 558 | static long uinput_ioctl_handler(struct file *file, unsigned int cmd, |
559 | unsigned long arg, void __user *p) | ||
459 | { | 560 | { |
460 | int retval; | 561 | int retval; |
461 | struct uinput_device *udev; | 562 | struct uinput_device *udev = file->private_data; |
462 | void __user *p = (void __user *)arg; | ||
463 | struct uinput_ff_upload ff_up; | 563 | struct uinput_ff_upload ff_up; |
464 | struct uinput_ff_erase ff_erase; | 564 | struct uinput_ff_erase ff_erase; |
465 | struct uinput_request *req; | 565 | struct uinput_request *req; |
466 | int length; | 566 | int length; |
467 | char *phys; | 567 | char *phys; |
468 | 568 | ||
469 | udev = file->private_data; | ||
470 | |||
471 | retval = mutex_lock_interruptible(&udev->mutex); | 569 | retval = mutex_lock_interruptible(&udev->mutex); |
472 | if (retval) | 570 | if (retval) |
473 | return retval; | 571 | return retval; |
@@ -549,26 +647,24 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
549 | break; | 647 | break; |
550 | 648 | ||
551 | case UI_BEGIN_FF_UPLOAD: | 649 | case UI_BEGIN_FF_UPLOAD: |
552 | if (copy_from_user(&ff_up, p, sizeof(ff_up))) { | 650 | retval = uinput_ff_upload_from_user(p, &ff_up); |
553 | retval = -EFAULT; | 651 | if (retval) |
554 | break; | 652 | break; |
555 | } | 653 | |
556 | req = uinput_request_find(udev, ff_up.request_id); | 654 | req = uinput_request_find(udev, ff_up.request_id); |
557 | if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { | 655 | if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { |
558 | retval = -EINVAL; | 656 | retval = -EINVAL; |
559 | break; | 657 | break; |
560 | } | 658 | } |
659 | |||
561 | ff_up.retval = 0; | 660 | ff_up.retval = 0; |
562 | memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect)); | 661 | ff_up.effect = *req->u.upload.effect; |
563 | if (req->u.upload.old) | 662 | if (req->u.upload.old) |
564 | memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect)); | 663 | ff_up.old = *req->u.upload.old; |
565 | else | 664 | else |
566 | memset(&ff_up.old, 0, sizeof(struct ff_effect)); | 665 | memset(&ff_up.old, 0, sizeof(struct ff_effect)); |
567 | 666 | ||
568 | if (copy_to_user(p, &ff_up, sizeof(ff_up))) { | 667 | retval = uinput_ff_upload_to_user(p, &ff_up); |
569 | retval = -EFAULT; | ||
570 | break; | ||
571 | } | ||
572 | break; | 668 | break; |
573 | 669 | ||
574 | case UI_BEGIN_FF_ERASE: | 670 | case UI_BEGIN_FF_ERASE: |
@@ -576,29 +672,34 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
576 | retval = -EFAULT; | 672 | retval = -EFAULT; |
577 | break; | 673 | break; |
578 | } | 674 | } |
675 | |||
579 | req = uinput_request_find(udev, ff_erase.request_id); | 676 | req = uinput_request_find(udev, ff_erase.request_id); |
580 | if (!(req && req->code == UI_FF_ERASE)) { | 677 | if (!req || req->code != UI_FF_ERASE) { |
581 | retval = -EINVAL; | 678 | retval = -EINVAL; |
582 | break; | 679 | break; |
583 | } | 680 | } |
681 | |||
584 | ff_erase.retval = 0; | 682 | ff_erase.retval = 0; |
585 | ff_erase.effect_id = req->u.effect_id; | 683 | ff_erase.effect_id = req->u.effect_id; |
586 | if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { | 684 | if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { |
587 | retval = -EFAULT; | 685 | retval = -EFAULT; |
588 | break; | 686 | break; |
589 | } | 687 | } |
688 | |||
590 | break; | 689 | break; |
591 | 690 | ||
592 | case UI_END_FF_UPLOAD: | 691 | case UI_END_FF_UPLOAD: |
593 | if (copy_from_user(&ff_up, p, sizeof(ff_up))) { | 692 | retval = uinput_ff_upload_from_user(p, &ff_up); |
594 | retval = -EFAULT; | 693 | if (retval) |
595 | break; | 694 | break; |
596 | } | 695 | |
597 | req = uinput_request_find(udev, ff_up.request_id); | 696 | req = uinput_request_find(udev, ff_up.request_id); |
598 | if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { | 697 | if (!req || req->code != UI_FF_UPLOAD || |
698 | !req->u.upload.effect) { | ||
599 | retval = -EINVAL; | 699 | retval = -EINVAL; |
600 | break; | 700 | break; |
601 | } | 701 | } |
702 | |||
602 | req->retval = ff_up.retval; | 703 | req->retval = ff_up.retval; |
603 | uinput_request_done(udev, req); | 704 | uinput_request_done(udev, req); |
604 | break; | 705 | break; |
@@ -608,11 +709,13 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
608 | retval = -EFAULT; | 709 | retval = -EFAULT; |
609 | break; | 710 | break; |
610 | } | 711 | } |
712 | |||
611 | req = uinput_request_find(udev, ff_erase.request_id); | 713 | req = uinput_request_find(udev, ff_erase.request_id); |
612 | if (!(req && req->code == UI_FF_ERASE)) { | 714 | if (!req || req->code != UI_FF_ERASE) { |
613 | retval = -EINVAL; | 715 | retval = -EINVAL; |
614 | break; | 716 | break; |
615 | } | 717 | } |
718 | |||
616 | req->retval = ff_erase.retval; | 719 | req->retval = ff_erase.retval; |
617 | uinput_request_done(udev, req); | 720 | uinput_request_done(udev, req); |
618 | break; | 721 | break; |
@@ -626,6 +729,18 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
626 | return retval; | 729 | return retval; |
627 | } | 730 | } |
628 | 731 | ||
732 | static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
733 | { | ||
734 | return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg); | ||
735 | } | ||
736 | |||
737 | #ifdef CONFIG_COMPAT | ||
738 | static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
739 | { | ||
740 | return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); | ||
741 | } | ||
742 | #endif | ||
743 | |||
629 | static const struct file_operations uinput_fops = { | 744 | static const struct file_operations uinput_fops = { |
630 | .owner = THIS_MODULE, | 745 | .owner = THIS_MODULE, |
631 | .open = uinput_open, | 746 | .open = uinput_open, |
@@ -634,6 +749,9 @@ static const struct file_operations uinput_fops = { | |||
634 | .write = uinput_write, | 749 | .write = uinput_write, |
635 | .poll = uinput_poll, | 750 | .poll = uinput_poll, |
636 | .unlocked_ioctl = uinput_ioctl, | 751 | .unlocked_ioctl = uinput_ioctl, |
752 | #ifdef CONFIG_COMPAT | ||
753 | .compat_ioctl = uinput_compat_ioctl, | ||
754 | #endif | ||
637 | }; | 755 | }; |
638 | 756 | ||
639 | static struct miscdevice uinput_misc = { | 757 | static struct miscdevice uinput_misc = { |