diff mbox

[1/3] input: evdev: introduce new evdev interface

Message ID 1448618432-32357-2-git-send-email-pingbo.wen@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

WEN Pingbo Nov. 27, 2015, 10 a.m. UTC
The y2038 problem in 'struct input_event' is complained too much. And
after some discussion with other people, I found it's impossible to
solve this in a simple way, and keep backward compatible at the same
time, so we need some new y2038-safe interface here.

This patch add two new evdev interface type - EV_IF_RAW and
EV_IF_COMPOSITE. And leaving the old interface as EV_IF_LEGACY for
compatibility. Userspace can switch between those interface seamlessly
via ioctl, which will be introduced in another patch.

And since evdev doesn't really interest in event timestamp, the patch
has also converted input_event to input_value in evdev entirely, and
move all time-related operations to input_event_to/from_user().

Signed-off-by: WEN Pingbo <pingbo.wen@linaro.org>
---
 drivers/input/evdev.c        |  78 ++++++++---------------
 drivers/input/input-compat.c | 148 ++++++++++++++++++++++++++++---------------
 drivers/input/input-compat.h |  48 ++++++++++----
 include/linux/input.h        |  12 ----
 include/uapi/linux/input.h   |  17 +++++
 5 files changed, 176 insertions(+), 127 deletions(-)

Comments

kernel test robot Nov. 27, 2015, 10:37 a.m. UTC | #1
Hi WEN,

[auto build test ERROR on: input/next]
[also build test ERROR on: v4.4-rc2 next-20151127]

url:    https://github.com/0day-ci/linux/commits/WEN-Pingbo/introduce-new-evdev-interface-type/20151127-180438
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: i386-randconfig-s0-201547 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

Note: the linux-review/WEN-Pingbo/introduce-new-evdev-interface-type/20151127-180438 HEAD fc81990de5842e76f794f755e095e4c5e55f8caa builds fine.
      It only hurts bisectibility.

All error/warnings (new ones prefixed by >>):

   drivers/input/misc/uinput.c: In function 'uinput_inject_events':
>> drivers/input/misc/uinput.c:442:28: error: too few arguments to function 'input_event_size'
     if (count != 0 && count < input_event_size())
                               ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:84:22: note: declared here
    static inline size_t input_event_size(int if_type)
                         ^
   drivers/input/misc/uinput.c:445:17: error: too few arguments to function 'input_event_size'
     while (bytes + input_event_size() <= count) {
                    ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:84:22: note: declared here
    static inline size_t input_event_size(int if_type)
                         ^
>> drivers/input/misc/uinput.c:452:45: warning: passing argument 2 of 'input_event_from_user' from incompatible pointer type [-Wincompatible-pointer-types]
      if (input_event_from_user(buffer + bytes, &ev))
                                                ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:102:5: note: expected 'struct input_value *' but argument is of type 'struct input_event *'
    int input_event_from_user(const char __user *buffer,
        ^
>> drivers/input/misc/uinput.c:452:7: error: too few arguments to function 'input_event_from_user'
      if (input_event_from_user(buffer + bytes, &ev))
          ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:102:5: note: declared here
    int input_event_from_user(const char __user *buffer,
        ^
   drivers/input/misc/uinput.c:456:12: error: too few arguments to function 'input_event_size'
      bytes += input_event_size();
               ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:84:22: note: declared here
    static inline size_t input_event_size(int if_type)
                         ^
   drivers/input/misc/uinput.c: In function 'uinput_events_to_user':
   drivers/input/misc/uinput.c:508:16: error: too few arguments to function 'input_event_size'
     while (read + input_event_size() <= count &&
                   ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:84:22: note: declared here
    static inline size_t input_event_size(int if_type)
                         ^
>> drivers/input/misc/uinput.c:511:42: warning: passing argument 2 of 'input_event_to_user' from incompatible pointer type [-Wincompatible-pointer-types]
      if (input_event_to_user(buffer + read, &event))
                                             ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:105:5: note: expected 'const struct input_value *' but argument is of type 'struct input_event *'
    int input_event_to_user(char __user *buffer, const struct input_value *event,
        ^
>> drivers/input/misc/uinput.c:511:7: error: too few arguments to function 'input_event_to_user'
      if (input_event_to_user(buffer + read, &event))
          ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:105:5: note: declared here
    int input_event_to_user(char __user *buffer, const struct input_value *event,
        ^
   drivers/input/misc/uinput.c:514:11: error: too few arguments to function 'input_event_size'
      read += input_event_size();
              ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:84:22: note: declared here
    static inline size_t input_event_size(int if_type)
                         ^
   drivers/input/misc/uinput.c: In function 'uinput_read':
   drivers/input/misc/uinput.c:526:28: error: too few arguments to function 'input_event_size'
     if (count != 0 && count < input_event_size())
                               ^
   In file included from drivers/input/misc/uinput.c:43:0:
   drivers/input/misc/../input-compat.h:84:22: note: declared here
    static inline size_t input_event_size(int if_type)
                         ^

vim +/input_event_size +442 drivers/input/misc/uinput.c

cbf05413 Ryan Mallon     2013-09-18  436  static ssize_t uinput_inject_events(struct uinput_device *udev,
54ce165e Dmitry Torokhov 2012-07-29  437  				    const char __user *buffer, size_t count)
^1da177e Linus Torvalds  2005-04-16  438  {
^1da177e Linus Torvalds  2005-04-16  439  	struct input_event ev;
cbf05413 Ryan Mallon     2013-09-18  440  	size_t bytes = 0;
^1da177e Linus Torvalds  2005-04-16  441  
cbf05413 Ryan Mallon     2013-09-18 @442  	if (count != 0 && count < input_event_size())
29506415 Dmitry Torokhov 2005-11-20  443  		return -EINVAL;
29506415 Dmitry Torokhov 2005-11-20  444  
cbf05413 Ryan Mallon     2013-09-18 @445  	while (bytes + input_event_size() <= count) {
cbf05413 Ryan Mallon     2013-09-18  446  		/*
cbf05413 Ryan Mallon     2013-09-18  447  		 * Note that even if some events were fetched successfully
cbf05413 Ryan Mallon     2013-09-18  448  		 * we are still going to return EFAULT instead of partial
cbf05413 Ryan Mallon     2013-09-18  449  		 * count to let userspace know that it got it's buffers
cbf05413 Ryan Mallon     2013-09-18  450  		 * all wrong.
cbf05413 Ryan Mallon     2013-09-18  451  		 */
cbf05413 Ryan Mallon     2013-09-18 @452  		if (input_event_from_user(buffer + bytes, &ev))
^1da177e Linus Torvalds  2005-04-16  453  			return -EFAULT;
29506415 Dmitry Torokhov 2005-11-20  454  
^1da177e Linus Torvalds  2005-04-16  455  		input_event(udev->dev, ev.type, ev.code, ev.value);
cbf05413 Ryan Mallon     2013-09-18  456  		bytes += input_event_size();
cbf05413 Ryan Mallon     2013-09-18  457  	}
^1da177e Linus Torvalds  2005-04-16  458  
cbf05413 Ryan Mallon     2013-09-18  459  	return bytes;
29506415 Dmitry Torokhov 2005-11-20  460  }
29506415 Dmitry Torokhov 2005-11-20  461  
54ce165e Dmitry Torokhov 2012-07-29  462  static ssize_t uinput_write(struct file *file, const char __user *buffer,
54ce165e Dmitry Torokhov 2012-07-29  463  			    size_t count, loff_t *ppos)
29506415 Dmitry Torokhov 2005-11-20  464  {
29506415 Dmitry Torokhov 2005-11-20  465  	struct uinput_device *udev = file->private_data;
29506415 Dmitry Torokhov 2005-11-20  466  	int retval;
29506415 Dmitry Torokhov 2005-11-20  467  
22ae19c6 Dmitry Torokhov 2012-07-29  468  	if (count == 0)
22ae19c6 Dmitry Torokhov 2012-07-29  469  		return 0;
22ae19c6 Dmitry Torokhov 2012-07-29  470  
221979aa Dmitry Torokhov 2006-02-19  471  	retval = mutex_lock_interruptible(&udev->mutex);
29506415 Dmitry Torokhov 2005-11-20  472  	if (retval)
29506415 Dmitry Torokhov 2005-11-20  473  		return retval;
29506415 Dmitry Torokhov 2005-11-20  474  
29506415 Dmitry Torokhov 2005-11-20  475  	retval = udev->state == UIST_CREATED ?
cbf05413 Ryan Mallon     2013-09-18  476  			uinput_inject_events(udev, buffer, count) :
29506415 Dmitry Torokhov 2005-11-20  477  			uinput_setup_device(udev, buffer, count);
29506415 Dmitry Torokhov 2005-11-20  478  
221979aa Dmitry Torokhov 2006-02-19  479  	mutex_unlock(&udev->mutex);
29506415 Dmitry Torokhov 2005-11-20  480  
29506415 Dmitry Torokhov 2005-11-20  481  	return retval;
^1da177e Linus Torvalds  2005-04-16  482  }
^1da177e Linus Torvalds  2005-04-16  483  
929d1af5 Dmitry Torokhov 2012-07-29  484  static bool uinput_fetch_next_event(struct uinput_device *udev,
929d1af5 Dmitry Torokhov 2012-07-29  485  				    struct input_event *event)
929d1af5 Dmitry Torokhov 2012-07-29  486  {
929d1af5 Dmitry Torokhov 2012-07-29  487  	bool have_event;
929d1af5 Dmitry Torokhov 2012-07-29  488  
929d1af5 Dmitry Torokhov 2012-07-29  489  	spin_lock_irq(&udev->dev->event_lock);
929d1af5 Dmitry Torokhov 2012-07-29  490  
929d1af5 Dmitry Torokhov 2012-07-29  491  	have_event = udev->head != udev->tail;
929d1af5 Dmitry Torokhov 2012-07-29  492  	if (have_event) {
929d1af5 Dmitry Torokhov 2012-07-29  493  		*event = udev->buff[udev->tail];
929d1af5 Dmitry Torokhov 2012-07-29  494  		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
929d1af5 Dmitry Torokhov 2012-07-29  495  	}
929d1af5 Dmitry Torokhov 2012-07-29  496  
929d1af5 Dmitry Torokhov 2012-07-29  497  	spin_unlock_irq(&udev->dev->event_lock);
929d1af5 Dmitry Torokhov 2012-07-29  498  
929d1af5 Dmitry Torokhov 2012-07-29  499  	return have_event;
929d1af5 Dmitry Torokhov 2012-07-29  500  }
929d1af5 Dmitry Torokhov 2012-07-29  501  
22ae19c6 Dmitry Torokhov 2012-07-29  502  static ssize_t uinput_events_to_user(struct uinput_device *udev,
22ae19c6 Dmitry Torokhov 2012-07-29  503  				     char __user *buffer, size_t count)
^1da177e Linus Torvalds  2005-04-16  504  {
929d1af5 Dmitry Torokhov 2012-07-29  505  	struct input_event event;
22ae19c6 Dmitry Torokhov 2012-07-29  506  	size_t read = 0;
^1da177e Linus Torvalds  2005-04-16  507  
22ae19c6 Dmitry Torokhov 2012-07-29 @508  	while (read + input_event_size() <= count &&
22ae19c6 Dmitry Torokhov 2012-07-29  509  	       uinput_fetch_next_event(udev, &event)) {
f40033ac David Herrmann  2012-07-29  510  
00ce756c Dmitry Torokhov 2012-07-29 @511  		if (input_event_to_user(buffer + read, &event))
00ce756c Dmitry Torokhov 2012-07-29  512  			return -EFAULT;
^1da177e Linus Torvalds  2005-04-16  513  
22ae19c6 Dmitry Torokhov 2012-07-29  514  		read += input_event_size();

:::::: The code at line 442 was first introduced by commit
:::::: cbf0541374e2fcfdfdcaf8365c957a137eb9feea Input: uinput - support injecting multiple events in one write() call

:::::: TO: Ryan Mallon <rmallon@gmail.com>
:::::: CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e9ae3d5..170681b 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -28,13 +28,6 @@ 
 #include <linux/cdev.h>
 #include "input-compat.h"
 
-enum evdev_clock_type {
-	EV_CLK_REAL = 0,
-	EV_CLK_MONO,
-	EV_CLK_BOOT,
-	EV_CLK_MAX
-};
-
 struct evdev {
 	int open;
 	struct input_handle handle;
@@ -57,10 +50,11 @@  struct evdev_client {
 	struct evdev *evdev;
 	struct list_head node;
 	unsigned int clk_type;
+	unsigned int if_type;
 	bool revoked;
 	unsigned long *evmasks[EV_CNT];
 	unsigned int bufsize;
-	struct input_event buffer[];
+	struct input_value buffer[];
 };
 
 static size_t evdev_get_mask_cnt(unsigned int type)
@@ -113,7 +107,7 @@  static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 	unsigned int i, head, num;
 	unsigned int mask = client->bufsize - 1;
 	bool is_report;
-	struct input_event *ev;
+	struct input_value *ev;
 
 	BUG_ON(type == EV_SYN);
 
@@ -135,7 +129,6 @@  static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 			continue;
 		} else if (head != i) {
 			/* move entry to fill the gap */
-			client->buffer[head].time = ev->time;
 			client->buffer[head].type = ev->type;
 			client->buffer[head].code = ev->code;
 			client->buffer[head].value = ev->value;
@@ -155,16 +148,8 @@  static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 
 static void __evdev_queue_syn_dropped(struct evdev_client *client)
 {
-	struct input_event ev;
-	ktime_t time;
+	struct input_value ev;
 
-	time = client->clk_type == EV_CLK_REAL ?
-			ktime_get_real() :
-			client->clk_type == EV_CLK_MONO ?
-				ktime_get() :
-				ktime_get_boottime();
-
-	ev.time = ktime_to_timeval(time);
 	ev.type = EV_SYN;
 	ev.code = SYN_DROPPED;
 	ev.value = 0;
@@ -229,7 +214,7 @@  static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
 }
 
 static void __pass_event(struct evdev_client *client,
-			 const struct input_event *event)
+			 const struct input_value *event)
 {
 	client->buffer[client->head++] = *event;
 	client->head &= client->bufsize - 1;
@@ -241,7 +226,6 @@  static void __pass_event(struct evdev_client *client,
 		 */
 		client->tail = (client->head - 2) & (client->bufsize - 1);
 
-		client->buffer[client->tail].time = event->time;
 		client->buffer[client->tail].type = EV_SYN;
 		client->buffer[client->tail].code = SYN_DROPPED;
 		client->buffer[client->tail].value = 0;
@@ -256,19 +240,15 @@  static void __pass_event(struct evdev_client *client,
 }
 
 static void evdev_pass_values(struct evdev_client *client,
-			const struct input_value *vals, unsigned int count,
-			ktime_t *ev_time)
+			const struct input_value *vals, unsigned int count)
 {
 	struct evdev *evdev = client->evdev;
 	const struct input_value *v;
-	struct input_event event;
 	bool wakeup = false;
 
 	if (client->revoked)
 		return;
 
-	event.time = ktime_to_timeval(ev_time[client->clk_type]);
-
 	/* Interrupts are disabled, just acquire the lock. */
 	spin_lock(&client->buffer_lock);
 
@@ -284,10 +264,7 @@  static void evdev_pass_values(struct evdev_client *client,
 			wakeup = true;
 		}
 
-		event.type = v->type;
-		event.code = v->code;
-		event.value = v->value;
-		__pass_event(client, &event);
+		__pass_event(client, v);
 	}
 
 	spin_unlock(&client->buffer_lock);
@@ -304,22 +281,16 @@  static void evdev_events(struct input_handle *handle,
 {
 	struct evdev *evdev = handle->private;
 	struct evdev_client *client;
-	ktime_t ev_time[EV_CLK_MAX];
-
-	ev_time[EV_CLK_MONO] = ktime_get();
-	ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
-	ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
-						 TK_OFFS_BOOT);
 
 	rcu_read_lock();
 
 	client = rcu_dereference(evdev->grab);
 
 	if (client)
-		evdev_pass_values(client, vals, count, ev_time);
+		evdev_pass_values(client, vals, count);
 	else
 		list_for_each_entry_rcu(client, &evdev->client_list, node)
-			evdev_pass_values(client, vals, count, ev_time);
+			evdev_pass_values(client, vals, count);
 
 	rcu_read_unlock();
 }
@@ -498,7 +469,7 @@  static int evdev_open(struct inode *inode, struct file *file)
 	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
 	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
 	unsigned int size = sizeof(struct evdev_client) +
-					bufsize * sizeof(struct input_event);
+					bufsize * sizeof(struct input_value);
 	struct evdev_client *client;
 	int error;
 
@@ -533,10 +504,10 @@  static ssize_t evdev_write(struct file *file, const char __user *buffer,
 {
 	struct evdev_client *client = file->private_data;
 	struct evdev *evdev = client->evdev;
-	struct input_event event;
+	struct input_value event;
 	int retval = 0;
 
-	if (count != 0 && count < input_event_size())
+	if (count != 0 && count < input_event_size(client->if_type))
 		return -EINVAL;
 
 	retval = mutex_lock_interruptible(&evdev->mutex);
@@ -548,13 +519,19 @@  static ssize_t evdev_write(struct file *file, const char __user *buffer,
 		goto out;
 	}
 
-	while (retval + input_event_size() <= count) {
+	while (retval + input_event_size(client->if_type) <= count) {
 
-		if (input_event_from_user(buffer + retval, &event)) {
+		if (input_event_from_user(buffer + retval, &event,
+					client->if_type)) {
 			retval = -EFAULT;
 			goto out;
 		}
-		retval += input_event_size();
+
+		/*
+		 * We aren't interested in timestamp from userspace,
+		 * skip it if in composite interface
+		 */
+		retval += input_event_size(client->if_type);
 
 		input_inject_event(&evdev->handle,
 				   event.type, event.code, event.value);
@@ -566,7 +543,7 @@  static ssize_t evdev_write(struct file *file, const char __user *buffer,
 }
 
 static int evdev_fetch_next_event(struct evdev_client *client,
-				  struct input_event *event)
+				  struct input_value *event)
 {
 	int have_event;
 
@@ -588,11 +565,11 @@  static ssize_t evdev_read(struct file *file, char __user *buffer,
 {
 	struct evdev_client *client = file->private_data;
 	struct evdev *evdev = client->evdev;
-	struct input_event event;
+	struct input_value event;
 	size_t read = 0;
 	int error;
 
-	if (count != 0 && count < input_event_size())
+	if (count != 0 && count < input_event_size(client->if_type))
 		return -EINVAL;
 
 	for (;;) {
@@ -610,13 +587,14 @@  static ssize_t evdev_read(struct file *file, char __user *buffer,
 		if (count == 0)
 			break;
 
-		while (read + input_event_size() <= count &&
+		while (read + input_event_size(client->if_type) <= count &&
 		       evdev_fetch_next_event(client, &event)) {
 
-			if (input_event_to_user(buffer + read, &event))
+			if (input_event_to_user(buffer + read, &event,
+					client->clk_type, client->if_type))
 				return -EFAULT;
 
-			read += input_event_size();
+			read += input_event_size(client->if_type);
 		}
 
 		if (read)
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
index 64ca711..09162c6 100644
--- a/drivers/input/input-compat.c
+++ b/drivers/input/input-compat.c
@@ -12,56 +12,118 @@ 
 #include <asm/uaccess.h>
 #include "input-compat.h"
 
-#ifdef CONFIG_COMPAT
+static ktime_t input_get_time(int clk_type)
+{
+	switch (clk_type) {
+	case EV_CLK_MONO:
+		return ktime_get();
+	case EV_CLK_BOOT:
+		return ktime_get_boottime();
+	case EV_CLK_REAL:
+	default:
+		return ktime_get_real();
+	}
+}
 
 int input_event_from_user(const char __user *buffer,
-			  struct input_event *event)
+			  struct input_value *event, int if_type)
 {
-	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
-		struct input_event_compat compat_event;
-
-		if (copy_from_user(&compat_event, buffer,
-				   sizeof(struct input_event_compat)))
-			return -EFAULT;
-
-		event->time.tv_sec = compat_event.time.tv_sec;
-		event->time.tv_usec = compat_event.time.tv_usec;
-		event->type = compat_event.type;
-		event->code = compat_event.code;
-		event->value = compat_event.value;
-
-	} else {
-		if (copy_from_user(event, buffer, sizeof(struct input_event)))
+	if (if_type == EV_IF_LEGACY) {
+#ifdef CONFIG_COMPAT
+		if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
+			struct input_event_compat compat_event;
+
+			if (copy_from_user(&compat_event, buffer,
+					   sizeof(struct input_event_compat)))
+				return -EFAULT;
+
+			event->type = compat_event.type;
+			event->code = compat_event.code;
+			event->value = compat_event.value;
+		} else {
+#endif
+			struct input_event ev;
+
+			if (copy_from_user(&ev, buffer,
+						sizeof(struct input_event)))
+				return -EFAULT;
+
+			/* drop timestamp from userspace */
+			event->type = ev.type;
+			event->code = ev.code;
+			event->value = ev.value;
+#ifdef CONFIG_COMPAT
+		}
+#endif
+	} else if (if_type == EV_IF_RAW || if_type == EV_IF_COMPOSITE) {
+		if (copy_from_user(event, buffer, sizeof(struct input_value)))
 			return -EFAULT;
-	}
+	} else
+		return -EINVAL;
 
 	return 0;
 }
 
-int input_event_to_user(char __user *buffer,
-			const struct input_event *event)
+int input_event_to_user(char __user *buffer, const struct input_value *event,
+			int clk_type, int if_type)
 {
-	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
-		struct input_event_compat compat_event;
-
-		compat_event.time.tv_sec = event->time.tv_sec;
-		compat_event.time.tv_usec = event->time.tv_usec;
-		compat_event.type = event->type;
-		compat_event.code = event->code;
-		compat_event.value = event->value;
+	if (if_type == EV_IF_LEGACY) {
+		struct timeval timestamp = ktime_to_timeval(
+				input_get_time(clk_type));
 
-		if (copy_to_user(buffer, &compat_event,
-				 sizeof(struct input_event_compat)))
+#ifdef CONFIG_COMPAT
+		if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
+			struct input_event_compat compat_event;
+
+			compat_event.time.tv_sec = timestamp.tv_sec;
+			compat_event.time.tv_usec = timestamp.tv_usec;
+			compat_event.type = event->type;
+			compat_event.code = event->code;
+			compat_event.value = event->value;
+
+			if (copy_to_user(buffer, &compat_event,
+					 sizeof(struct input_event_compat)))
+				return -EFAULT;
+		} else {
+#endif
+			struct input_event ev;
+
+			ev.time = timestamp;
+			ev.type = event->type;
+			ev.code = event->code;
+			ev.value = event->value;
+
+			if (copy_to_user(buffer, &ev,
+					sizeof(struct input_event)))
+				return -EFAULT;
+#ifdef CONFIG_COMPAT
+		}
+#endif
+	} else if (if_type == EV_IF_RAW || if_type == EV_IF_COMPOSITE) {
+		if (copy_to_user(buffer, event, sizeof(struct input_value)))
 			return -EFAULT;
 
-	} else {
-		if (copy_to_user(buffer, event, sizeof(struct input_event)))
-			return -EFAULT;
-	}
+		if (if_type != EV_IF_RAW) {
+			/*
+			 * composite interface, send timestamp event
+			 *
+			 * s64 and input_value are the same size, use s64
+			 * directly here.
+			 */
+			s64 time = ktime_to_ns(input_get_time(clk_type));
+
+			if (copy_to_user(buffer + sizeof(struct input_value),
+						&time, sizeof(s64)))
+				return -EFAULT;
+		}
+	} else
+		return -EINVAL;
 
 	return 0;
 }
 
+#ifdef CONFIG_COMPAT
+
 int input_ff_effect_from_user(const char __user *buffer, size_t size,
 			      struct ff_effect *effect)
 {
@@ -99,24 +161,6 @@  int input_ff_effect_from_user(const char __user *buffer, size_t size,
 
 #else
 
-int input_event_from_user(const char __user *buffer,
-			 struct input_event *event)
-{
-	if (copy_from_user(event, buffer, sizeof(struct input_event)))
-		return -EFAULT;
-
-	return 0;
-}
-
-int input_event_to_user(char __user *buffer,
-			const struct input_event *event)
-{
-	if (copy_to_user(buffer, event, sizeof(struct input_event)))
-		return -EFAULT;
-
-	return 0;
-}
-
 int input_ff_effect_from_user(const char __user *buffer, size_t size,
 			      struct ff_effect *effect)
 {
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h
index 148f66f..4ee8095 100644
--- a/drivers/input/input-compat.h
+++ b/drivers/input/input-compat.h
@@ -65,26 +65,48 @@  struct ff_effect_compat {
 	} u;
 };
 
-static inline size_t input_event_size(void)
-{
-	return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ?
-		sizeof(struct input_event_compat) : sizeof(struct input_event);
-}
+#endif /* CONFIG_COMPAT */
 
-#else
+enum event_clock_type {
+	EV_CLK_REAL = 0,
+	EV_CLK_MONO,
+	EV_CLK_BOOT,
+	EV_CLK_MAX
+};
+
+enum event_if_type {
+	EV_IF_LEGACY = 0,
+	EV_IF_RAW,
+	EV_IF_COMPOSITE,
+	EV_IF_MAX
+};
 
-static inline size_t input_event_size(void)
+static inline size_t input_event_size(int if_type)
 {
-	return sizeof(struct input_event);
+	switch (if_type) {
+	case EV_IF_LEGACY:
+#ifdef CONFIG_COMPAT
+		if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME)
+			return sizeof(struct input_event_compat);
+#endif
+		return sizeof(struct input_event);
+	case EV_IF_RAW:
+		return sizeof(struct input_value);
+	case EV_IF_COMPOSITE:
+		return sizeof(struct input_composite_event);
+	default:
+		return 0;
+	}
 }
 
-#endif /* CONFIG_COMPAT */
-
 int input_event_from_user(const char __user *buffer,
-			 struct input_event *event);
+			  struct input_value *event, int if_type);
+
+int input_event_to_user(char __user *buffer, const struct input_value *event,
+			int clk_type, int if_type);
 
-int input_event_to_user(char __user *buffer,
-			const struct input_event *event);
+#define input_value_to_user(buffer, event, if_type)	\
+	input_event_to_user(buffer, event, 0, if_type)
 
 int input_ff_effect_from_user(const char __user *buffer, size_t size,
 			      struct ff_effect *effect);
diff --git a/include/linux/input.h b/include/linux/input.h
index 1e96769..9f9d551 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -25,18 +25,6 @@ 
 #include <linux/mod_devicetable.h>
 
 /**
- * struct input_value - input value representation
- * @type: type of value (EV_KEY, EV_ABS, etc)
- * @code: the value code
- * @value: the value
- */
-struct input_value {
-	__u16 type;
-	__u16 code;
-	__s32 value;
-};
-
-/**
  * struct input_dev - represents an input device
  * @name: name of the device
  * @phys: physical path to the device in the system hierarchy
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 2758687..79b35ff 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -29,6 +29,23 @@  struct input_event {
 	__s32 value;
 };
 
+/**
+ * struct input_value - input value representation
+ * @type: type of value (EV_KEY, EV_ABS, etc)
+ * @code: the value code
+ * @value: the value
+ */
+struct input_value {
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+
+struct input_composite_event {
+	struct input_value v;
+	__s64 time;
+};
+
 /*
  * Protocol version.
  */