Input: Send events one packet at a time
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. Cc: Daniel Kurtz <djkurtz@chromium.org> Tested-by: Benjamin Tissoires <benjamin.tissoires@enac.fr> Tested-by: Ping Cheng <pingc@wacom.com> Tested-by: Sedat Dilek <sedat.dilek@gmail.com> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
This commit is contained in:
parent
352ac4bd01
commit
4369c64c79
|
@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex);
|
||||||
|
|
||||||
static struct input_handler *input_table[8];
|
static struct input_handler *input_table[8];
|
||||||
|
|
||||||
|
static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
|
||||||
|
|
||||||
static inline int is_event_supported(unsigned int code,
|
static inline int is_event_supported(unsigned int code,
|
||||||
unsigned long *bm, unsigned int max)
|
unsigned long *bm, unsigned int max)
|
||||||
{
|
{
|
||||||
|
@ -90,46 +92,81 @@ static void input_stop_autorepeat(struct input_dev *dev)
|
||||||
* filtered out, through all open handles. This function is called with
|
* filtered out, through all open handles. This function is called with
|
||||||
* dev->event_lock held and interrupts disabled.
|
* dev->event_lock held and interrupts disabled.
|
||||||
*/
|
*/
|
||||||
static void input_pass_event(struct input_dev *dev,
|
static unsigned int input_to_handler(struct input_handle *handle,
|
||||||
unsigned int type, unsigned int code, int value)
|
struct input_value *vals, unsigned int count)
|
||||||
|
{
|
||||||
|
struct input_handler *handler = handle->handler;
|
||||||
|
struct input_value *end = vals;
|
||||||
|
struct input_value *v;
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = end - vals;
|
||||||
|
if (!count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (handler->events)
|
||||||
|
handler->events(handle, vals, count);
|
||||||
|
else if (handler->event)
|
||||||
|
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, unsigned int count)
|
||||||
{
|
{
|
||||||
struct input_handler *handler;
|
|
||||||
struct input_handle *handle;
|
struct input_handle *handle;
|
||||||
|
struct input_value *v;
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
return;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
handle = rcu_dereference(dev->grab);
|
handle = rcu_dereference(dev->grab);
|
||||||
if (handle)
|
if (handle) {
|
||||||
handle->handler->event(handle, type, code, value);
|
count = input_to_handler(handle, vals, count);
|
||||||
else {
|
} else {
|
||||||
bool filtered = false;
|
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
||||||
|
if (handle->open)
|
||||||
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
|
count = input_to_handler(handle, vals, count);
|
||||||
if (!handle->open)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
handler = handle->handler;
|
|
||||||
if (!handler->filter) {
|
|
||||||
if (filtered)
|
|
||||||
break;
|
|
||||||
|
|
||||||
handler->event(handle, type, code, value);
|
|
||||||
|
|
||||||
} else if (handler->filter(handle, type, code, value))
|
|
||||||
filtered = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
/* trigger auto repeat for key events */
|
add_input_randomness(vals->type, vals->code, vals->value);
|
||||||
if (type == EV_KEY && value != 2) {
|
|
||||||
if (value)
|
|
||||||
input_start_autorepeat(dev, code);
|
|
||||||
else
|
|
||||||
input_stop_autorepeat(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* trigger auto repeat for key events */
|
||||||
|
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, ARRAY_SIZE(vals));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -146,18 +183,12 @@ static void input_repeat_key(unsigned long data)
|
||||||
|
|
||||||
if (test_bit(dev->repeat_key, dev->key) &&
|
if (test_bit(dev->repeat_key, dev->key) &&
|
||||||
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
|
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
|
||||||
|
struct input_value vals[] = {
|
||||||
|
{ EV_KEY, dev->repeat_key, 2 },
|
||||||
|
input_value_sync
|
||||||
|
};
|
||||||
|
|
||||||
input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
|
input_pass_values(dev, vals, ARRAY_SIZE(vals));
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->rep[REP_PERIOD])
|
if (dev->rep[REP_PERIOD])
|
||||||
mod_timer(&dev->timer, jiffies +
|
mod_timer(&dev->timer, jiffies +
|
||||||
|
@ -170,6 +201,8 @@ static void input_repeat_key(unsigned long data)
|
||||||
#define INPUT_IGNORE_EVENT 0
|
#define INPUT_IGNORE_EVENT 0
|
||||||
#define INPUT_PASS_TO_HANDLERS 1
|
#define INPUT_PASS_TO_HANDLERS 1
|
||||||
#define INPUT_PASS_TO_DEVICE 2
|
#define INPUT_PASS_TO_DEVICE 2
|
||||||
|
#define INPUT_SLOT 4
|
||||||
|
#define INPUT_FLUSH 8
|
||||||
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
|
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
|
||||||
|
|
||||||
static int input_handle_abs_event(struct input_dev *dev,
|
static int input_handle_abs_event(struct input_dev *dev,
|
||||||
|
@ -216,14 +249,14 @@ static int input_handle_abs_event(struct input_dev *dev,
|
||||||
/* Flush pending "slot" event */
|
/* Flush pending "slot" event */
|
||||||
if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
|
if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
|
||||||
input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
|
input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
|
||||||
input_pass_event(dev, EV_ABS, ABS_MT_SLOT, mt->slot);
|
return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return INPUT_PASS_TO_HANDLERS;
|
return INPUT_PASS_TO_HANDLERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_handle_event(struct input_dev *dev,
|
static int input_get_disposition(struct input_dev *dev,
|
||||||
unsigned int type, unsigned int code, int value)
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
int disposition = INPUT_IGNORE_EVENT;
|
int disposition = INPUT_IGNORE_EVENT;
|
||||||
|
|
||||||
|
@ -236,13 +269,9 @@ static void input_handle_event(struct input_dev *dev,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYN_REPORT:
|
case SYN_REPORT:
|
||||||
if (!dev->sync) {
|
disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
|
||||||
dev->sync = true;
|
|
||||||
disposition = INPUT_PASS_TO_HANDLERS;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SYN_MT_REPORT:
|
case SYN_MT_REPORT:
|
||||||
dev->sync = false;
|
|
||||||
disposition = INPUT_PASS_TO_HANDLERS;
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -327,14 +356,48 @@ static void input_handle_event(struct input_dev *dev,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
return disposition;
|
||||||
dev->sync = false;
|
}
|
||||||
|
|
||||||
|
static void input_handle_event(struct input_dev *dev,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
|
{
|
||||||
|
int disposition;
|
||||||
|
|
||||||
|
disposition = input_get_disposition(dev, type, code, value);
|
||||||
|
|
||||||
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
||||||
dev->event(dev, type, code, value);
|
dev->event(dev, type, code, value);
|
||||||
|
|
||||||
if (disposition & INPUT_PASS_TO_HANDLERS)
|
if (!dev->vals)
|
||||||
input_pass_event(dev, type, code, value);
|
return;
|
||||||
|
|
||||||
|
if (disposition & INPUT_PASS_TO_HANDLERS) {
|
||||||
|
struct input_value *v;
|
||||||
|
|
||||||
|
if (disposition & INPUT_SLOT) {
|
||||||
|
v = &dev->vals[dev->num_vals++];
|
||||||
|
v->type = EV_ABS;
|
||||||
|
v->code = ABS_MT_SLOT;
|
||||||
|
v->value = dev->mt->slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = &dev->vals[dev->num_vals++];
|
||||||
|
v->type = type;
|
||||||
|
v->code = code;
|
||||||
|
v->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposition & INPUT_FLUSH) {
|
||||||
|
if (dev->num_vals >= 2)
|
||||||
|
input_pass_values(dev, dev->vals, dev->num_vals);
|
||||||
|
dev->num_vals = 0;
|
||||||
|
} else if (dev->num_vals >= dev->max_vals - 2) {
|
||||||
|
dev->vals[dev->num_vals++] = input_value_sync;
|
||||||
|
input_pass_values(dev, dev->vals, dev->num_vals);
|
||||||
|
dev->num_vals = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,7 +425,6 @@ void input_event(struct input_dev *dev,
|
||||||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->event_lock, flags);
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
add_input_randomness(type, code, value);
|
|
||||||
input_handle_event(dev, type, code, value);
|
input_handle_event(dev, type, code, value);
|
||||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -841,10 +903,12 @@ int input_set_keycode(struct input_dev *dev,
|
||||||
if (test_bit(EV_KEY, dev->evbit) &&
|
if (test_bit(EV_KEY, dev->evbit) &&
|
||||||
!is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
|
!is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
|
||||||
__test_and_clear_bit(old_keycode, dev->key)) {
|
__test_and_clear_bit(old_keycode, dev->key)) {
|
||||||
|
struct input_value vals[] = {
|
||||||
|
{ EV_KEY, old_keycode, 0 },
|
||||||
|
input_value_sync
|
||||||
|
};
|
||||||
|
|
||||||
input_pass_event(dev, EV_KEY, old_keycode, 0);
|
input_pass_values(dev, vals, ARRAY_SIZE(vals));
|
||||||
if (dev->sync)
|
|
||||||
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -1426,6 +1490,7 @@ static void input_dev_release(struct device *device)
|
||||||
input_ff_destroy(dev);
|
input_ff_destroy(dev);
|
||||||
input_mt_destroy_slots(dev);
|
input_mt_destroy_slots(dev);
|
||||||
kfree(dev->absinfo);
|
kfree(dev->absinfo);
|
||||||
|
kfree(dev->vals);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
|
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
|
@ -1846,6 +1911,11 @@ int input_register_device(struct input_dev *dev)
|
||||||
if (dev->hint_events_per_packet < packet_size)
|
if (dev->hint_events_per_packet < packet_size)
|
||||||
dev->hint_events_per_packet = packet_size;
|
dev->hint_events_per_packet = packet_size;
|
||||||
|
|
||||||
|
dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
|
||||||
|
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
|
* 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.
|
* is handled by the driver itself and we don't do it in input.c.
|
||||||
|
|
|
@ -1168,6 +1168,18 @@ struct ff_effect {
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#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
|
* struct input_dev - represents an input device
|
||||||
* @name: name of the device
|
* @name: name of the device
|
||||||
|
@ -1240,7 +1252,6 @@ struct ff_effect {
|
||||||
* last user closes the device
|
* last user closes the device
|
||||||
* @going_away: marks devices that are in a middle of unregistering and
|
* @going_away: marks devices that are in a middle of unregistering and
|
||||||
* causes input_open_device*() fail with -ENODEV.
|
* 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
|
* @dev: driver model's view of this device
|
||||||
* @h_list: list of input handles associated with the device. When
|
* @h_list: list of input handles associated with the device. When
|
||||||
* accessing the list dev->mutex must be held
|
* accessing the list dev->mutex must be held
|
||||||
|
@ -1305,12 +1316,14 @@ struct input_dev {
|
||||||
unsigned int users;
|
unsigned int users;
|
||||||
bool going_away;
|
bool going_away;
|
||||||
|
|
||||||
bool sync;
|
|
||||||
|
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
struct list_head h_list;
|
struct list_head h_list;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
|
||||||
|
unsigned int num_vals;
|
||||||
|
unsigned int max_vals;
|
||||||
|
struct input_value *vals;
|
||||||
};
|
};
|
||||||
#define to_input_dev(d) container_of(d, struct input_dev, dev)
|
#define to_input_dev(d) container_of(d, struct input_dev, dev)
|
||||||
|
|
||||||
|
@ -1371,6 +1384,9 @@ struct input_handle;
|
||||||
* @event: event handler. This method is being called by input core with
|
* @event: event handler. This method is being called by input core with
|
||||||
* interrupts disabled and dev->event_lock spinlock held and so
|
* interrupts disabled and dev->event_lock spinlock held and so
|
||||||
* it may not sleep
|
* 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
|
* @filter: similar to @event; separates normal event handlers from
|
||||||
* "filters".
|
* "filters".
|
||||||
* @match: called after comparing device's id with handler's id_table
|
* @match: called after comparing device's id with handler's id_table
|
||||||
|
@ -1407,6 +1423,8 @@ struct input_handler {
|
||||||
void *private;
|
void *private;
|
||||||
|
|
||||||
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
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, unsigned int count);
|
||||||
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
||||||
bool (*match)(struct input_handler *handler, struct input_dev *dev);
|
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);
|
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
|
||||||
|
|
Loading…
Reference in a new issue