linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Henrik Rydberg" <rydberg@euromail.se>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Jiri Kosina <jkosina@suse.cz>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Henrik Rydberg <rydberg@euromail.se>
Subject: [PATCH 06/19] Input: Send events one packet at a time
Date: Sun, 12 Aug 2012 23:42:24 +0200	[thread overview]
Message-ID: <1344807757-2217-7-git-send-email-rydberg@euromail.se> (raw)
In-Reply-To: <1344807757-2217-1-git-send-email-rydberg@euromail.se>

On heavy event loads, such as a multitouch driver, the irqsoff latency
can be as high as 250 us.  By accumulating a frame worth of data
before passing it on, the latency can be dramatically reduced.  As a
side effect, the special EV_SYN handling can be removed, since the
frame is now atomic.

This patch adds the events() handler callback and uses it if it
exists. The latency is improved by 50 us even without the callback.

Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
---
 drivers/input/input-mt.c |   3 +-
 drivers/input/input.c    | 187 ++++++++++++++++++++++++++++-------------------
 include/linux/input.h    |  26 +++++--
 3 files changed, 132 insertions(+), 84 deletions(-)

diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 58bde77..f956b27 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -63,7 +63,6 @@ void input_mt_destroy_slots(struct input_dev *dev)
 {
 	kfree(dev->mt);
 	dev->mt = NULL;
-	dev->slot = 0;
 }
 EXPORT_SYMBOL(input_mt_destroy_slots);
 
@@ -91,7 +90,7 @@ void input_mt_report_slot_state(struct input_dev *dev,
 		return;
 	}
 
-	slot = &mt->slots[dev->slot];
+	slot = &mt->slots[input_abs_get_val(dev, ABS_MT_SLOT)];
 	id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
 	if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
 		id = input_mt_new_trkid(mt);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index a57c4a5..9b6aa15 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -90,46 +90,81 @@ static void input_stop_autorepeat(struct input_dev *dev)
  * filtered out, through all open handles. This function is called with
  * dev->event_lock held and interrupts disabled.
  */
-static void input_pass_event(struct input_dev *dev,
-			     unsigned int type, unsigned int code, int value)
+static size_t input_to_handler(struct input_handle *handle,
+				    struct input_value *vals, size_t count)
 {
-	struct input_handler *handler;
-	struct input_handle *handle;
+	struct input_handler *handler = handle->handler;
+	struct input_value *end = vals;
+	struct input_value *v;
 
-	rcu_read_lock();
+	for (v = vals; v != vals + count; v++) {
+		if (handler->filter &&
+		    handler->filter(handle, v->type, v->code, v->value))
+			continue;
+		if (end != v)
+			*end = *v;
+		end++;
+	}
 
-	handle = rcu_dereference(dev->grab);
-	if (handle)
-		handle->handler->event(handle, type, code, value);
-	else {
-		bool filtered = false;
+	count = end - vals;
+	if (!count)
+		return 0;
 
-		list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
-			if (!handle->open)
-				continue;
+	if (handler->events)
+		handler->events(handle, vals, count);
+	else
+		for (v = vals; v != end; v++)
+			handler->event(handle, v->type, v->code, v->value);
+
+	return count;
+}
+
+/*
+ * Pass values first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static void input_pass_values(struct input_dev *dev,
+			      struct input_value *vals, size_t count)
+{
+	struct input_handle *handle;
+	struct input_value *v;
 
-			handler = handle->handler;
-			if (!handler->filter) {
-				if (filtered)
-					break;
+	if (!count)
+		return;
 
-				handler->event(handle, type, code, value);
+	rcu_read_lock();
 
-			} else if (handler->filter(handle, type, code, value))
-				filtered = true;
-		}
+	handle = rcu_dereference(dev->grab);
+	if (handle) {
+		count = input_to_handler(handle, vals, count);
+	} else {
+		list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+			if (handle->open)
+				count = input_to_handler(handle, vals, count);
 	}
 
 	rcu_read_unlock();
 
+	add_input_randomness(vals->type, vals->code, vals->value);
+
 	/* trigger auto repeat for key events */
-	if (type == EV_KEY && value != 2) {
-		if (value)
-			input_start_autorepeat(dev, code);
-		else
-			input_stop_autorepeat(dev);
+	for (v = vals; v != vals + count; v++) {
+		if (v->type == EV_KEY && v->value != 2) {
+			if (v->value)
+				input_start_autorepeat(dev, v->code);
+			else
+				input_stop_autorepeat(dev);
+		}
 	}
+}
+
+static void input_pass_event(struct input_dev *dev,
+			     unsigned int type, unsigned int code, int value)
+{
+	struct input_value vals[] = { { type, code, value } };
 
+	input_pass_values(dev, vals, 1);
 }
 
 /*
@@ -146,18 +181,12 @@ static void input_repeat_key(unsigned long data)
 
 	if (test_bit(dev->repeat_key, dev->key) &&
 	    is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
+		struct input_value vals[] =  {
+			{ EV_KEY, dev->repeat_key, 2 },
+			{ EV_SYN, SYN_REPORT, 1 },
+		};
 
-		input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
-
-		if (dev->sync) {
-			/*
-			 * Only send SYN_REPORT if we are not in a middle
-			 * of driver parsing a new hardware packet.
-			 * Otherwise assume that the driver will send
-			 * SYN_REPORT once it's done.
-			 */
-			input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
-		}
+		input_pass_values(dev, vals, 2);
 
 		if (dev->rep[REP_PERIOD])
 			mod_timer(&dev->timer, jiffies +
@@ -170,37 +199,23 @@ static void input_repeat_key(unsigned long data)
 #define INPUT_IGNORE_EVENT	0
 #define INPUT_PASS_TO_HANDLERS	1
 #define INPUT_PASS_TO_DEVICE	2
+#define INPUT_FLUSH		4
 #define INPUT_PASS_TO_ALL	(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
 
 static int input_handle_abs_event(struct input_dev *dev,
 				  unsigned int code, int *pval)
 {
 	bool is_mt_event;
-	int *pold;
-
-	if (code == ABS_MT_SLOT) {
-		/*
-		 * "Stage" the event; we'll flush it later, when we
-		 * get actual touch data.
-		 */
-		if (dev->mt && *pval >= 0 && *pval < dev->mt->num_slots)
-			dev->slot = *pval;
-
-		return INPUT_IGNORE_EVENT;
-	}
+	int *pold = NULL;
 
 	is_mt_event = input_is_mt_value(code);
 
 	if (!is_mt_event) {
 		pold = &dev->absinfo[code].value;
 	} else if (dev->mt) {
-		pold = &dev->mt->slots[dev->slot].abs[code - ABS_MT_FIRST];
-	} else {
-		/*
-		 * Bypass filtering for multi-touch events when
-		 * not employing slots.
-		 */
-		pold = NULL;
+		int slot = dev->absinfo[ABS_MT_SLOT].value;
+		if (slot >= 0 && slot < dev->mt->num_slots)
+			pold = &dev->mt->slots[slot].abs[code - ABS_MT_FIRST];
 	}
 
 	if (pold) {
@@ -212,17 +227,11 @@ static int input_handle_abs_event(struct input_dev *dev,
 		*pold = *pval;
 	}
 
-	/* Flush pending "slot" event */
-	if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
-		input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
-		input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
-	}
-
 	return INPUT_PASS_TO_HANDLERS;
 }
 
-static void input_handle_event(struct input_dev *dev,
-			       unsigned int type, unsigned int code, int value)
+static int input_get_disposition(struct input_dev *dev,
+			  unsigned int type, unsigned int code, int value)
 {
 	int disposition = INPUT_IGNORE_EVENT;
 
@@ -235,13 +244,9 @@ static void input_handle_event(struct input_dev *dev,
 			break;
 
 		case SYN_REPORT:
-			if (!dev->sync) {
-				dev->sync = true;
-				disposition = INPUT_PASS_TO_HANDLERS;
-			}
+			disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
 			break;
 		case SYN_MT_REPORT:
-			dev->sync = false;
 			disposition = INPUT_PASS_TO_HANDLERS;
 			break;
 		}
@@ -326,14 +331,35 @@ static void input_handle_event(struct input_dev *dev,
 		break;
 	}
 
-	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
-		dev->sync = false;
+	return disposition;
+}
+
+static void input_handle_event(struct input_dev *dev,
+			       unsigned int type, unsigned int code, int value)
+{
+	struct input_value *v;
+	int disp;
+
+	disp = input_get_disposition(dev, type, code, value);
 
-	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
+	if ((disp & INPUT_PASS_TO_DEVICE) && dev->event)
 		dev->event(dev, type, code, value);
 
-	if (disposition & INPUT_PASS_TO_HANDLERS)
-		input_pass_event(dev, type, code, value);
+	if (!dev->vals)
+		return;
+
+	if (disp & INPUT_PASS_TO_HANDLERS) {
+		v = &dev->vals[dev->num_vals++];
+		v->type = type;
+		v->code = code;
+		v->value = value;
+	}
+
+	if ((disp & INPUT_FLUSH) || (dev->num_vals >= dev->max_vals)) {
+		if (dev->num_vals >= 2)
+			input_pass_values(dev, dev->vals, dev->num_vals);
+		dev->num_vals = 0;
+	}
 }
 
 /**
@@ -361,7 +387,6 @@ void input_event(struct input_dev *dev,
 	if (is_event_supported(type, dev->evbit, EV_MAX)) {
 
 		spin_lock_irqsave(&dev->event_lock, flags);
-		add_input_randomness(type, code, value);
 		input_handle_event(dev, type, code, value);
 		spin_unlock_irqrestore(&dev->event_lock, flags);
 	}
@@ -842,8 +867,7 @@ int input_set_keycode(struct input_dev *dev,
 	    __test_and_clear_bit(old_keycode, dev->key)) {
 
 		input_pass_event(dev, EV_KEY, old_keycode, 0);
-		if (dev->sync)
-			input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+		input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
 	}
 
  out:
@@ -1425,6 +1449,7 @@ static void input_dev_release(struct device *device)
 	input_ff_destroy(dev);
 	input_mt_destroy_slots(dev);
 	kfree(dev->absinfo);
+	kfree(dev->vals);
 	kfree(dev);
 
 	module_put(THIS_MODULE);
@@ -1845,6 +1870,14 @@ int input_register_device(struct input_dev *dev)
 	if (dev->hint_events_per_packet < packet_size)
 		dev->hint_events_per_packet = packet_size;
 
+	dev->num_vals = 0;
+	dev->max_vals = max(dev->hint_events_per_packet, packet_size);
+
+	kfree(dev->vals);
+	dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
+	if (!dev->vals)
+		return -ENOMEM;
+
 	/*
 	 * If delay and period are pre-set by the driver, then autorepeating
 	 * is handled by the driver itself and we don't do it in input.c.
diff --git a/include/linux/input.h b/include/linux/input.h
index 76d6788..1f7f172 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1169,6 +1169,18 @@ struct ff_effect {
 #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
@@ -1204,7 +1216,6 @@ struct ff_effect {
  * @timer: timer for software autorepeat
  * @rep: current values for autorepeat parameters (delay, rate)
  * @mt: pointer to multitouch state
- * @slot: MT slot currently being transmitted
  * @absinfo: array of &struct input_absinfo elements holding information
  *	about absolute axes (current value, min, max, flat, fuzz,
  *	resolution)
@@ -1241,7 +1252,6 @@ struct ff_effect {
  *	last user closes the device
  * @going_away: marks devices that are in a middle of unregistering and
  *	causes input_open_device*() fail with -ENODEV.
- * @sync: set to %true when there were no new events since last EV_SYN
  * @dev: driver model's view of this device
  * @h_list: list of input handles associated with the device. When
  *	accessing the list dev->mutex must be held
@@ -1285,7 +1295,6 @@ struct input_dev {
 	int rep[REP_CNT];
 
 	struct input_mt *mt;
-	int slot;
 
 	struct input_absinfo *absinfo;
 
@@ -1307,12 +1316,14 @@ struct input_dev {
 	unsigned int users;
 	bool going_away;
 
-	bool sync;
-
 	struct device dev;
 
 	struct list_head	h_list;
 	struct list_head	node;
+
+	size_t num_vals;
+	size_t max_vals;
+	struct input_value *vals;
 };
 #define to_input_dev(d) container_of(d, struct input_dev, dev)
 
@@ -1373,6 +1384,9 @@ struct input_handle;
  * @event: event handler. This method is being called by input core with
  *	interrupts disabled and dev->event_lock spinlock held and so
  *	it may not sleep
+ * @events: event sequence handler. This method is being called by
+ *	input core with interrupts disabled and dev->event_lock
+ *	spinlock held and so it may not sleep
  * @filter: similar to @event; separates normal event handlers from
  *	"filters".
  * @match: called after comparing device's id with handler's id_table
@@ -1409,6 +1423,8 @@ struct input_handler {
 	void *private;
 
 	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+	void (*events)(struct input_handle *handle,
+		       const struct input_value *vals, size_t count);
 	bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
 	bool (*match)(struct input_handler *handler, struct input_dev *dev);
 	int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
-- 
1.7.11.4


  parent reply	other threads:[~2012-08-12 21:40 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-12 21:42 [PATCH 00/19] Input and HID updates for 3.7 Henrik Rydberg
2012-08-12 21:42 ` [PATCH 01/19] Input: Break out MT data Henrik Rydberg
2012-08-12 21:42 ` [PATCH 02/19] Input: Improve the events-per-packet estimate Henrik Rydberg
2012-08-14 19:32   ` Ping Cheng
2012-08-14 19:53     ` Dmitry Torokhov
2012-08-14 20:50       ` Ping Cheng
2012-08-14 21:12         ` Dmitry Torokhov
2012-08-15  0:54           ` Ping Cheng
2012-08-14 20:01     ` Henrik Rydberg
2012-08-14 21:06       ` Ping Cheng
2012-08-12 21:42 ` [PATCH 03/19] Input: Remove redundant packet estimates Henrik Rydberg
2012-08-12 21:42 ` [PATCH 04/19] Input: Make sure we follow all EV_KEY events Henrik Rydberg
2012-08-12 21:42 ` [PATCH 05/19] Input: Move autorepeat to the event-passing phase Henrik Rydberg
2012-08-12 21:42 ` Henrik Rydberg [this message]
2012-08-24  4:03   ` [PATCH 06/19] Input: Send events one packet at a time Daniel Kurtz
2012-08-25 19:38     ` Henrik Rydberg
2012-08-12 21:42 ` [PATCH 07/19] Input: evdev - Add the events() callback Henrik Rydberg
2012-08-24  4:07   ` Daniel Kurtz
2012-08-25 19:46     ` Henrik Rydberg
2012-08-12 21:42 ` [PATCH 08/19] Input: MT - Add flags to input_mt_init_slots() Henrik Rydberg
2012-08-12 21:42 ` [PATCH 09/19] Input: MT - Handle frame synchronization in core Henrik Rydberg
2012-08-15 23:28   ` Ping Cheng
2012-08-16 18:07     ` Henrik Rydberg
2012-08-16 19:22       ` Ping Cheng
2012-08-16 20:05         ` Henrik Rydberg
2012-08-16 19:58       ` Ping Cheng
2012-08-20 13:36   ` Benjamin Tissoires
2012-08-20 15:53     ` Henrik Rydberg
2012-08-12 21:42 ` [PATCH 10/19] Input: MT - Add in-kernel tracking Henrik Rydberg
2012-08-12 21:42 ` [PATCH 11/19] Input: MT - Add slot assignment by id Henrik Rydberg
2012-08-12 21:42 ` [PATCH 12/19] Input: bcm5974 - Preparatory renames Henrik Rydberg
2012-08-12 21:42 ` [PATCH 13/19] Input: bcm5974 - Drop pressure and width emulation Henrik Rydberg
2012-08-12 21:42 ` [PATCH 14/19] Input: bcm5974 - Drop the logical dimensions Henrik Rydberg
2012-08-12 21:42 ` [PATCH 15/19] Input: bcm5974 - Convert to MT-B Henrik Rydberg
2012-08-12 21:42 ` [PATCH 16/19] HID: hid-multitouch: Remove misleading null test Henrik Rydberg
2012-08-20 13:35   ` Benjamin Tissoires
2012-08-12 21:42 ` [PATCH 17/19] HID: Only dump input if someone is listening Henrik Rydberg
2012-08-12 21:42 ` [PATCH 18/19] HID: Add an input configured notification callback Henrik Rydberg
2012-08-12 21:42 ` [PATCH 19/19] HID: multitouch: Remove the redundant touch state Henrik Rydberg
2012-08-20 13:36   ` Benjamin Tissoires
2012-08-20 16:01     ` Henrik Rydberg
2012-08-22 20:58     ` [PATCH v2] " Henrik Rydberg
2012-08-28 22:25       ` Jiri Kosina
2012-08-29 13:36         ` Benjamin Tissoires
2012-08-29 17:18           ` Jiri Kosina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1344807757-2217-7-git-send-email-rydberg@euromail.se \
    --to=rydberg@euromail.se \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).