From 8d18fba282120a4a8e4416d1202522ffae8cad58 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 15 Sep 2012 15:15:58 +0200 Subject: Input: Break out MT data Move all MT-related things to a separate place. This saves some bytes for non-mt input devices, and prepares for new MT features. Reviewed-and-tested-by: Benjamin Tissoires Tested-by: Ping Cheng Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- include/linux/input.h | 11 ++--------- include/linux/input/mt.h | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index 725dcd0f63a4..9da4f5796fd6 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1203,11 +1203,7 @@ struct ff_effect { * software autorepeat * @timer: timer for software autorepeat * @rep: current values for autorepeat parameters (delay, rate) - * @mt: pointer to array of struct input_mt_slot holding current values - * of tracked contacts - * @mtsize: number of MT slots the device uses - * @slot: MT slot currently being transmitted - * @trkid: stores MT tracking ID for the current contact + * @mt: pointer to multitouch state * @absinfo: array of &struct input_absinfo elements holding information * about absolute axes (current value, min, max, flat, fuzz, * resolution) @@ -1287,10 +1283,7 @@ struct input_dev { int rep[REP_CNT]; - struct input_mt_slot *mt; - int mtsize; - int slot; - int trkid; + struct input_mt *mt; struct input_absinfo *absinfo; diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index f86737586e19..63458bced77d 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -23,6 +23,20 @@ struct input_mt_slot { int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; }; +/** + * struct input_mt - state of tracked contacts + * @trkid: stores MT tracking ID for the next contact + * @num_slots: number of MT slots the device uses + * @slot: MT slot currently being transmitted + * @slots: array of slots holding current values of tracked contacts + */ +struct input_mt { + int trkid; + int num_slots; + int slot; + struct input_mt_slot slots[]; +}; + static inline void input_mt_set_value(struct input_mt_slot *slot, unsigned code, int value) { @@ -38,9 +52,9 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot, int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots); void input_mt_destroy_slots(struct input_dev *dev); -static inline int input_mt_new_trkid(struct input_dev *dev) +static inline int input_mt_new_trkid(struct input_mt *mt) { - return dev->trkid++ & TRKID_MAX; + return mt->trkid++ & TRKID_MAX; } static inline void input_mt_slot(struct input_dev *dev, int slot) -- cgit v1.2.3 From 4369c64c79a22b98d3b7eff9d089196cd878a10a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 15 Sep 2012 15:23:35 +0200 Subject: 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 Tested-by: Benjamin Tissoires Tested-by: Ping Cheng Tested-by: Sedat Dilek Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/input.c | 176 +++++++++++++++++++++++++++++++++++--------------- include/linux/input.h | 24 ++++++- 2 files changed, 144 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/input.c b/drivers/input/input.c index fbe522d0ead7..5244f3d05b12 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex); 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, 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 * 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 unsigned int input_to_handler(struct input_handle *handle, + struct input_value *vals, unsigned int 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 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_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, ARRAY_SIZE(vals)); } /* @@ -146,18 +183,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 }, + input_value_sync + }; - 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, ARRAY_SIZE(vals)); if (dev->rep[REP_PERIOD]) mod_timer(&dev->timer, jiffies + @@ -170,6 +201,8 @@ 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_SLOT 4 +#define INPUT_FLUSH 8 #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) 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 */ 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_pass_event(dev, EV_ABS, ABS_MT_SLOT, mt->slot); + return INPUT_PASS_TO_HANDLERS | INPUT_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; @@ -236,13 +269,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; } @@ -327,14 +356,48 @@ 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) +{ + int disposition; + + disposition = input_get_disposition(dev, type, code, value); if ((disposition & 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 (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)) { 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); } @@ -841,10 +903,12 @@ int input_set_keycode(struct input_dev *dev, if (test_bit(EV_KEY, dev->evbit) && !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && __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); - if (dev->sync) - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_values(dev, vals, ARRAY_SIZE(vals)); } out: @@ -1426,6 +1490,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); @@ -1846,6 +1911,11 @@ int input_register_device(struct input_dev *dev) if (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 * 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 9da4f5796fd6..ba4874302939 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1168,6 +1168,18 @@ struct ff_effect { #include #include +/** + * 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 @@ -1240,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 @@ -1305,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; + + unsigned int num_vals; + unsigned int max_vals; + struct input_value *vals; }; #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 * 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 @@ -1407,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, unsigned int 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); -- cgit v1.2.3 From b4adbbefc2099476a4f1020041c99f52cf3cd67d Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 11 Aug 2012 22:07:55 +0200 Subject: Input: MT - Add flags to input_mt_init_slots() Preparing to move more repeated code into the mt core, add a flags argument to the input_mt_slots_init() function. Reviewed-and-tested-by: Benjamin Tissoires Tested-by: Ping Cheng Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/hid/hid-magicmouse.c | 2 +- drivers/hid/hid-multitouch.c | 2 +- drivers/input/input-mt.c | 4 +++- drivers/input/misc/uinput.c | 2 +- drivers/input/mouse/alps.c | 2 +- drivers/input/mouse/elantech.c | 4 ++-- drivers/input/mouse/sentelic.c | 2 +- drivers/input/mouse/synaptics.c | 4 ++-- drivers/input/tablet/wacom_wac.c | 6 +++--- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- drivers/input/touchscreen/cyttsp_core.c | 2 +- drivers/input/touchscreen/edt-ft5x06.c | 2 +- drivers/input/touchscreen/egalax_ts.c | 2 +- drivers/input/touchscreen/ili210x.c | 2 +- drivers/input/touchscreen/mms114.c | 2 +- drivers/input/touchscreen/penmount.c | 2 +- drivers/input/touchscreen/wacom_w8001.c | 2 +- include/linux/input/mt.h | 5 ++++- 18 files changed, 27 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 73647266daad..25ddf3e3aec6 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd __set_bit(EV_ABS, input->evbit); - error = input_mt_init_slots(input, 16); + error = input_mt_init_slots(input, 16, 0); if (error) return error; input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 59c8b5c1d2de..c400d9008774 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -390,7 +390,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONTACTID: if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; - input_mt_init_slots(hi->input, td->maxcontacts); + input_mt_init_slots(hi->input, td->maxcontacts, 0); mt_store_field(usage, td, hi); td->last_field_index = field->index; td->touches_by_report++; diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 6020d06652a8..1985f27f427a 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -25,7 +25,8 @@ * May be called repeatedly. Returns -EINVAL if attempting to * reinitialize with a different number of slots. */ -int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) +int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, + unsigned int flags) { struct input_mt *mt = dev->mt; int i; @@ -40,6 +41,7 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) return -ENOMEM; mt->num_slots = num_slots; + mt->flags = flags; input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 736056897e50..6b1797503e34 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu goto exit; if (test_bit(ABS_MT_SLOT, dev->absbit)) { int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot); + input_mt_init_slots(dev, nslot, 0); } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { input_set_events_per_packet(dev, 60); } diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 4a1347e91bdc..cf5af1f495ec 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse) case ALPS_PROTO_V3: case ALPS_PROTO_V4: set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); - input_mt_init_slots(dev1, 2); + input_mt_init_slots(dev1, 2, 0); input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 479011004a11..1e8e42fb03a4 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, ETP_WMAX_V2, 0, 0); } - input_mt_init_slots(dev, 2); + input_mt_init_slots(dev, 2, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); break; @@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, ETP_WMAX_V2, 0, 0); /* Multitouch capable pad, up to 5 fingers. */ - input_mt_init_slots(dev, ETP_MAX_FINGERS); + input_mt_init_slots(dev, ETP_MAX_FINGERS, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 3f5649f19082..b47155d8bc8c 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -960,7 +960,7 @@ static int fsp_set_input_params(struct psmouse *psmouse) input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); - input_mt_init_slots(dev, 2); + input_mt_init_slots(dev, 2, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); } diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 14eaecea2b70..37033ade79d3 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { - input_mt_init_slots(dev, 2); + input_mt_init_slots(dev, 2, 0); set_abs_position_params(dev, priv, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ @@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { /* Non-image sensors with AGM use semi-mt */ __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); - input_mt_init_slots(dev, 2); + input_mt_init_slots(dev, 2, 0); set_abs_position_params(dev, priv, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 002041975de9..5837d07e3c9e 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); - input_mt_init_slots(input_dev, features->touch_max); + input_mt_init_slots(input_dev, features->touch_max, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); @@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, case TABLETPC2FG: if (features->device_type == BTN_TOOL_FINGER) { - input_mt_init_slots(input_dev, features->touch_max); + input_mt_init_slots(input_dev, features->touch_max, 0); input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, @@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_FINGER, input_dev->keybit); __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - input_mt_init_slots(input_dev, features->touch_max); + input_mt_init_slots(input_dev, features->touch_max, 0); if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { __set_bit(BTN_TOOL_TRIPLETAP, diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4623cc69fc60..e92615d0b1b0 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client, /* For multi touch */ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; - error = input_mt_init_slots(input_dev, num_mt_slots); + error = input_mt_init_slots(input_dev, num_mt_slots, 0); if (error) goto err_free_object; input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index f030d9ec795d..8e60437ac85b 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, CY_MAXZ, 0, 0); - input_mt_init_slots(input_dev, CY_MAX_ID); + input_mt_init_slots(input_dev, CY_MAX_ID, 0); error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 9afc777a40a7..7b786e757ba2 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -778,7 +778,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client, 0, tsdata->num_x * 64 - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); - error = input_mt_init_slots(input, MAX_SUPPORT_POINTS); + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); goto err_free_mem; diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 70524dd34f42..c1e3460f1195 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client, ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0); - input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS); + input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0); input_set_drvdata(input_dev, ts); diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index c0044175a921..4ac69760ec08 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); /* Multi touch */ - input_mt_init_slots(input, MAX_TOUCHES); + input_mt_init_slots(input, MAX_TOUCHES, 0); input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 49c44bbf548d..560cf09d1c5a 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client, input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0); /* For multi touch */ - input_mt_init_slots(input_dev, MMS114_MAX_TOUCH); + input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MMS114_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c index 4ccde45b9da2..b49f0b836925 100644 --- a/drivers/input/touchscreen/penmount.c +++ b/drivers/input/touchscreen/penmount.c @@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv) input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0); if (pm->maxcontacts > 1) { - input_mt_init_slots(pm->dev, pm->maxcontacts); + input_mt_init_slots(pm->dev, pm->maxcontacts, 0); input_set_abs_params(pm->dev, ABS_MT_POSITION_X, 0, max_x, 0, 0); input_set_abs_params(pm->dev, diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 8f9ad2f893b8..9a83be6b6584 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001) case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - input_mt_init_slots(dev, 2); + input_mt_init_slots(dev, 2, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 63458bced77d..53f5bf168ff9 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -28,12 +28,14 @@ struct input_mt_slot { * @trkid: stores MT tracking ID for the next contact * @num_slots: number of MT slots the device uses * @slot: MT slot currently being transmitted + * @flags: input_mt operation flags * @slots: array of slots holding current values of tracked contacts */ struct input_mt { int trkid; int num_slots; int slot; + unsigned int flags; struct input_mt_slot slots[]; }; @@ -49,7 +51,8 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot, return slot->abs[code - ABS_MT_FIRST]; } -int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots); +int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, + unsigned int flags); void input_mt_destroy_slots(struct input_dev *dev); static inline int input_mt_new_trkid(struct input_mt *mt) -- cgit v1.2.3 From 55e49089f4589908eb688742d2d7eff33b74ac78 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 22 Aug 2012 20:43:22 +0200 Subject: Input: MT - Handle frame synchronization in core Most MT drivers perform the same actions on frame synchronization. Some actions, like dropping unseen contacts, are also unnecessarily complex. Collect common frame synchronization tasks in a new function, input_mt_sync_frame(). Depending on the flags set, it drops unseen contacts and performs pointer emulation. With init flags and frame synchronization in place, most MT drivers can be simplified. First out are the bcm5974 and hid-multitouch drivers, following this patch. Reviewed-and-tested-by: Benjamin Tissoires Tested-by: Ping Cheng Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/input-mt.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-- include/linux/input/mt.h | 9 +++++++ 2 files changed, 77 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 1985f27f427a..96cc2e2a9be2 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -14,6 +14,14 @@ #define TRKID_SGN ((TRKID_MAX + 1) >> 1) +static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src) +{ + if (dev->absinfo && test_bit(src, dev->absbit)) { + dev->absinfo[dst] = dev->absinfo[src]; + dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst); + } +} + /** * input_mt_init_slots() - initialize MT input slots * @dev: input device supporting MT events and finger tracking @@ -45,6 +53,28 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); + if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) { + __set_bit(EV_KEY, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + + copy_abs(dev, ABS_X, ABS_MT_POSITION_X); + copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y); + copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE); + } + if (flags & INPUT_MT_POINTER) { + __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + if (num_slots >= 3) + __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); + if (num_slots >= 4) + __set_bit(BTN_TOOL_QUADTAP, dev->keybit); + if (num_slots >= 5) + __set_bit(BTN_TOOL_QUINTTAP, dev->keybit); + __set_bit(INPUT_PROP_POINTER, dev->propbit); + } + if (flags & INPUT_MT_DIRECT) + __set_bit(INPUT_PROP_DIRECT, dev->propbit); + /* Mark slots as 'unused' */ for (i = 0; i < num_slots; i++) input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1); @@ -87,12 +117,17 @@ void input_mt_report_slot_state(struct input_dev *dev, struct input_mt_slot *slot; int id; - if (!mt || !active) { + if (!mt) + return; + + slot = &mt->slots[mt->slot]; + slot->frame = mt->frame; + + if (!active) { input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); return; } - slot = &mt->slots[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); @@ -177,3 +212,34 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) } } EXPORT_SYMBOL(input_mt_report_pointer_emulation); + +/** + * input_mt_sync_frame() - synchronize mt frame + * @dev: input device with allocated MT slots + * + * Close the frame and prepare the internal state for a new one. + * Depending on the flags, marks unused slots as inactive and performs + * pointer emulation. + */ +void input_mt_sync_frame(struct input_dev *dev) +{ + struct input_mt *mt = dev->mt; + struct input_mt_slot *s; + + if (!mt) + return; + + if (mt->flags & INPUT_MT_DROP_UNUSED) { + for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { + if (s->frame == mt->frame) + continue; + input_mt_slot(dev, s - mt->slots); + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + } + } + + input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER)); + + mt->frame++; +} +EXPORT_SYMBOL(input_mt_sync_frame); diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 53f5bf168ff9..94989189878d 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -15,12 +15,17 @@ #define TRKID_MAX 0xffff +#define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */ +#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */ +#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */ /** * struct input_mt_slot - represents the state of an input MT slot * @abs: holds current values of ABS_MT axes for this slot + * @frame: last frame at which input_mt_report_slot_state() was called */ struct input_mt_slot { int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; + unsigned int frame; }; /** @@ -29,6 +34,7 @@ struct input_mt_slot { * @num_slots: number of MT slots the device uses * @slot: MT slot currently being transmitted * @flags: input_mt operation flags + * @frame: increases every time input_mt_sync_frame() is called * @slots: array of slots holding current values of tracked contacts */ struct input_mt { @@ -36,6 +42,7 @@ struct input_mt { int num_slots; int slot; unsigned int flags; + unsigned int frame; struct input_mt_slot slots[]; }; @@ -81,4 +88,6 @@ void input_mt_report_slot_state(struct input_dev *dev, void input_mt_report_finger_count(struct input_dev *dev, int count); void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count); +void input_mt_sync_frame(struct input_dev *dev); + #endif -- cgit v1.2.3 From 7c1a87897c75139dec258eb03e1a24fb73385b73 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sun, 12 Aug 2012 20:47:05 +0200 Subject: Input: MT - Add in-kernel tracking With the INPUT_MT_TRACK flag set, the function input_mt_assign_slots() can be used to match a new set of contacts against the currently used slots. The algorithm used is based on Lagrange relaxation, and performs very well in practice; slower than mtdev for a few corner cases, but faster in most commonly occuring cases. Tested-by: Benjamin Tissoires Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/input-mt.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++- include/linux/input/mt.h | 21 +++++++ 2 files changed, 163 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 96cc2e2a9be2..caff298832a4 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -46,7 +46,7 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL); if (!mt) - return -ENOMEM; + goto err_mem; mt->num_slots = num_slots; mt->flags = flags; @@ -74,6 +74,12 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, } if (flags & INPUT_MT_DIRECT) __set_bit(INPUT_PROP_DIRECT, dev->propbit); + if (flags & INPUT_MT_TRACK) { + unsigned int n2 = num_slots * num_slots; + mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL); + if (!mt->red) + goto err_mem; + } /* Mark slots as 'unused' */ for (i = 0; i < num_slots; i++) @@ -81,6 +87,9 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, dev->mt = mt; return 0; +err_mem: + kfree(mt); + return -ENOMEM; } EXPORT_SYMBOL(input_mt_init_slots); @@ -93,7 +102,10 @@ EXPORT_SYMBOL(input_mt_init_slots); */ void input_mt_destroy_slots(struct input_dev *dev) { - kfree(dev->mt); + if (dev->mt) { + kfree(dev->mt->red); + kfree(dev->mt); + } dev->mt = NULL; } EXPORT_SYMBOL(input_mt_destroy_slots); @@ -243,3 +255,131 @@ void input_mt_sync_frame(struct input_dev *dev) mt->frame++; } EXPORT_SYMBOL(input_mt_sync_frame); + +static int adjust_dual(int *begin, int step, int *end, int eq) +{ + int f, *p, s, c; + + if (begin == end) + return 0; + + f = *begin; + p = begin + step; + s = p == end ? f + 1 : *p; + + for (; p != end; p += step) + if (*p < f) + s = f, f = *p; + else if (*p < s) + s = *p; + + c = (f + s + 1) / 2; + if (c == 0 || (c > 0 && !eq)) + return 0; + if (s < 0) + c *= 2; + + for (p = begin; p != end; p += step) + *p -= c; + + return (c < s && s <= 0) || (f >= 0 && f < c); +} + +static void find_reduced_matrix(int *w, int nr, int nc, int nrc) +{ + int i, k, sum; + + for (k = 0; k < nrc; k++) { + for (i = 0; i < nr; i++) + adjust_dual(w + i, nr, w + i + nrc, nr <= nc); + sum = 0; + for (i = 0; i < nrc; i += nr) + sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr); + if (!sum) + break; + } +} + +static int input_mt_set_matrix(struct input_mt *mt, + const struct input_mt_pos *pos, int num_pos) +{ + const struct input_mt_pos *p; + struct input_mt_slot *s; + int *w = mt->red; + int x, y; + + for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { + if (!input_mt_is_active(s)) + continue; + x = input_mt_get_value(s, ABS_MT_POSITION_X); + y = input_mt_get_value(s, ABS_MT_POSITION_Y); + for (p = pos; p != pos + num_pos; p++) { + int dx = x - p->x, dy = y - p->y; + *w++ = dx * dx + dy * dy; + } + } + + return w - mt->red; +} + +static void input_mt_set_slots(struct input_mt *mt, + int *slots, int num_pos) +{ + struct input_mt_slot *s; + int *w = mt->red, *p; + + for (p = slots; p != slots + num_pos; p++) + *p = -1; + + for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { + if (!input_mt_is_active(s)) + continue; + for (p = slots; p != slots + num_pos; p++) + if (*w++ < 0) + *p = s - mt->slots; + } + + for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { + if (input_mt_is_active(s)) + continue; + for (p = slots; p != slots + num_pos; p++) + if (*p < 0) { + *p = s - mt->slots; + break; + } + } +} + +/** + * input_mt_assign_slots() - perform a best-match assignment + * @dev: input device with allocated MT slots + * @slots: the slot assignment to be filled + * @pos: the position array to match + * @num_pos: number of positions + * + * Performs a best match against the current contacts and returns + * the slot assignment list. New contacts are assigned to unused + * slots. + * + * Returns zero on success, or negative error in case of failure. + */ +int input_mt_assign_slots(struct input_dev *dev, int *slots, + const struct input_mt_pos *pos, int num_pos) +{ + struct input_mt *mt = dev->mt; + int nrc; + + if (!mt || !mt->red) + return -ENXIO; + if (num_pos > mt->num_slots) + return -EINVAL; + if (num_pos < 1) + return 0; + + nrc = input_mt_set_matrix(mt, pos, num_pos); + find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc); + input_mt_set_slots(mt, slots, num_pos); + + return 0; +} +EXPORT_SYMBOL(input_mt_assign_slots); diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 94989189878d..6b6f7c8e95bf 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -18,6 +18,8 @@ #define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */ #define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */ #define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */ +#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */ + /** * struct input_mt_slot - represents the state of an input MT slot * @abs: holds current values of ABS_MT axes for this slot @@ -35,6 +37,7 @@ struct input_mt_slot { * @slot: MT slot currently being transmitted * @flags: input_mt operation flags * @frame: increases every time input_mt_sync_frame() is called + * @red: reduced cost matrix for in-kernel tracking * @slots: array of slots holding current values of tracked contacts */ struct input_mt { @@ -43,6 +46,7 @@ struct input_mt { int slot; unsigned int flags; unsigned int frame; + int *red; struct input_mt_slot slots[]; }; @@ -58,6 +62,11 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot, return slot->abs[code - ABS_MT_FIRST]; } +static inline bool input_mt_is_active(const struct input_mt_slot *slot) +{ + return input_mt_get_value(slot, ABS_MT_TRACKING_ID) >= 0; +} + int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, unsigned int flags); void input_mt_destroy_slots(struct input_dev *dev); @@ -90,4 +99,16 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count); void input_mt_sync_frame(struct input_dev *dev); +/** + * struct input_mt_pos - contact position + * @x: horizontal coordinate + * @y: vertical coordinate + */ +struct input_mt_pos { + s16 x, y; +}; + +int input_mt_assign_slots(struct input_dev *dev, int *slots, + const struct input_mt_pos *pos, int num_pos); + #endif -- cgit v1.2.3 From 17a465a7f2d6ce31738a3a76591afeab165f185a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 1 Sep 2012 09:27:20 +0200 Subject: Input: MT - Get slot by key Some devices use an internal key for tracking which cannot be directly mapped to slots. This patch provides a key-to-slot mapping, which can be used by drivers of such devices. Reviewed-and-tested-by: Benjamin Tissoires Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/input-mt.c | 32 ++++++++++++++++++++++++++++++++ include/linux/input/mt.h | 4 ++++ 2 files changed, 36 insertions(+) (limited to 'include/linux') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index caff298832a4..44f5a67aaa69 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -383,3 +383,35 @@ int input_mt_assign_slots(struct input_dev *dev, int *slots, return 0; } EXPORT_SYMBOL(input_mt_assign_slots); + +/** + * input_mt_get_slot_by_key() - return slot matching key + * @dev: input device with allocated MT slots + * @key: the key of the sought slot + * + * Returns the slot of the given key, if it exists, otherwise + * set the key on the first unused slot and return. + * + * If no available slot can be found, -1 is returned. + */ +int input_mt_get_slot_by_key(struct input_dev *dev, int key) +{ + struct input_mt *mt = dev->mt; + struct input_mt_slot *s; + + if (!mt) + return -1; + + for (s = mt->slots; s != mt->slots + mt->num_slots; s++) + if (input_mt_is_active(s) && s->key == key) + return s - mt->slots; + + for (s = mt->slots; s != mt->slots + mt->num_slots; s++) + if (!input_mt_is_active(s)) { + s->key = key; + return s - mt->slots; + } + + return -1; +} +EXPORT_SYMBOL(input_mt_get_slot_by_key); diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 6b6f7c8e95bf..cc5cca774bab 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -24,10 +24,12 @@ * struct input_mt_slot - represents the state of an input MT slot * @abs: holds current values of ABS_MT axes for this slot * @frame: last frame at which input_mt_report_slot_state() was called + * @key: optional driver designation of this slot */ struct input_mt_slot { int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; unsigned int frame; + unsigned int key; }; /** @@ -111,4 +113,6 @@ struct input_mt_pos { int input_mt_assign_slots(struct input_dev *dev, int *slots, const struct input_mt_pos *pos, int num_pos); +int input_mt_get_slot_by_key(struct input_dev *dev, int key); + #endif -- cgit v1.2.3 From 9ebf3d7687192923e3d44fdbcd8d9f8375053fb8 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 30 Jul 2012 13:28:18 +0200 Subject: HID: Add an input configured notification callback A hid device may create several input devices, and a driver may need to prepare or finalize the configuration per input device. Currently, there is no sane way for a driver to know when a device has been configured. This patch adds a callback providing that information. Reviewed-and-tested-by: Benjamin Tissoires Tested-by: Ping Cheng Acked-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- drivers/hid/hid-input.c | 11 +++++++++-- include/linux/hid.h | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 811bfad64609..d917c0d53685 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1154,6 +1154,7 @@ static void report_features(struct hid_device *hid) int hidinput_connect(struct hid_device *hid, unsigned int force) { + struct hid_driver *drv = hid->driver; struct hid_report *report; struct hid_input *hidinput = NULL; struct input_dev *input_dev; @@ -1228,6 +1229,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) * UGCI) cram a lot of unrelated inputs into the * same interface. */ hidinput->report = report; + if (drv->input_configured) + drv->input_configured(hid, hidinput); if (input_register_device(hidinput->input)) goto out_cleanup; hidinput = NULL; @@ -1235,8 +1238,12 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } } - if (hidinput && input_register_device(hidinput->input)) - goto out_cleanup; + if (hidinput) { + if (drv->input_configured) + drv->input_configured(hid, hidinput); + if (input_register_device(hidinput->input)) + goto out_cleanup; + } return 0; diff --git a/include/linux/hid.h b/include/linux/hid.h index 42970de1b40c..f37da2803005 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -626,6 +626,7 @@ struct hid_usage_id { * @report_fixup: called before report descriptor parsing (NULL means nop) * @input_mapping: invoked on input registering before mapping an usage * @input_mapped: invoked on input registering after mapping an usage + * @input_configured: invoked just before the device is registered * @feature_mapping: invoked on feature registering * @suspend: invoked on suspend (NULL means nop) * @resume: invoked on resume if device was not reset (NULL means nop) @@ -670,6 +671,8 @@ struct hid_driver { int (*input_mapped)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); + void (*input_configured)(struct hid_device *hdev, + struct hid_input *hidinput); void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); -- cgit v1.2.3 From 7e55bdedfa4a72baa0d4fec8d0948cb65342a9a0 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 3 Sep 2012 12:28:59 +0200 Subject: HID: Allow more fields in the hid report Some recent hardware define more than 128 fields in the report descriptor. Increase the limit to 256. This adds another kilobyte of memory per report. Tested-by: Ping Cheng Acked-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- include/linux/hid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hid.h b/include/linux/hid.h index f37da2803005..7e1f37db7582 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -414,7 +414,7 @@ struct hid_field { __u16 dpad; /* dpad input code */ }; -#define HID_MAX_FIELDS 128 +#define HID_MAX_FIELDS 256 struct hid_report { struct list_head list; -- cgit v1.2.3 From 7f8d4cad1e4e11a45d02bd6e024cc2812963c38a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 8 Oct 2012 09:07:24 -0700 Subject: Input: extend the number of event (and other) devices Extend the amount of character devices, such as eventX, mouseX and jsX, from a hard limit of 32 per input handler to about 1024 shared across all handlers. To be compatible with legacy installations input handlers will start creating char devices with minors in their legacy range, however once legacy range is exhausted they will start allocating minors from the dynamic range 256-1024. Reviewed-by: David Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 99 ++++++++++++----------------------- drivers/input/input.c | 114 +++++++++++++++++++--------------------- drivers/input/joydev.c | 88 ++++++++++++------------------- drivers/input/mousedev.c | 132 ++++++++++++++++++++++------------------------- include/linux/input.h | 10 ++-- 5 files changed, 187 insertions(+), 256 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 118d0300f1fb..6ae2ac47c9c8 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -23,11 +23,11 @@ #include #include #include +#include #include "input-compat.h" struct evdev { int open; - int minor; struct input_handle handle; wait_queue_head_t wait; struct evdev_client __rcu *grab; @@ -35,6 +35,7 @@ struct evdev { spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + struct cdev cdev; bool exist; }; @@ -51,9 +52,6 @@ struct evdev_client { struct input_event buffer[]; }; -static struct evdev *evdev_table[EVDEV_MINORS]; -static DEFINE_MUTEX(evdev_table_mutex); - static void __pass_event(struct evdev_client *client, const struct input_event *event) { @@ -310,35 +308,16 @@ static unsigned int evdev_compute_buffer_size(struct input_dev *dev) static int evdev_open(struct inode *inode, struct file *file) { - struct evdev *evdev; + struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); + unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); struct evdev_client *client; - int i = iminor(inode) - EVDEV_MINOR_BASE; - unsigned int bufsize; int error; - if (i >= EVDEV_MINORS) - return -ENODEV; - - error = mutex_lock_interruptible(&evdev_table_mutex); - if (error) - return error; - evdev = evdev_table[i]; - if (evdev) - get_device(&evdev->dev); - mutex_unlock(&evdev_table_mutex); - - if (!evdev) - return -ENODEV; - - bufsize = evdev_compute_buffer_size(evdev->handle.dev); - client = kzalloc(sizeof(struct evdev_client) + bufsize * sizeof(struct input_event), GFP_KERNEL); - if (!client) { - error = -ENOMEM; - goto err_put_evdev; - } + if (!client) + return -ENOMEM; client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); @@ -352,13 +331,12 @@ static int evdev_open(struct inode *inode, struct file *file) file->private_data = client; nonseekable_open(inode, file); + get_device(&evdev->dev); return 0; err_free_client: evdev_detach_client(evdev, client); kfree(client); - err_put_evdev: - put_device(&evdev->dev); return error; } @@ -942,26 +920,6 @@ static const struct file_operations evdev_fops = { .llseek = no_llseek, }; -static int evdev_install_chrdev(struct evdev *evdev) -{ - /* - * No need to do any locking here as calls to connect and - * disconnect are serialized by the input core - */ - evdev_table[evdev->minor] = evdev; - return 0; -} - -static void evdev_remove_chrdev(struct evdev *evdev) -{ - /* - * Lock evdev table to prevent race with evdev_open() - */ - mutex_lock(&evdev_table_mutex); - evdev_table[evdev->minor] = NULL; - mutex_unlock(&evdev_table_mutex); -} - /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted @@ -980,7 +938,8 @@ static void evdev_cleanup(struct evdev *evdev) evdev_mark_dead(evdev); evdev_hangup(evdev); - evdev_remove_chrdev(evdev); + + cdev_del(&evdev->cdev); /* evdev is marked dead so no one else accesses evdev->open */ if (evdev->open) { @@ -991,43 +950,47 @@ static void evdev_cleanup(struct evdev *evdev) /* * Create new evdev device. Note that input core serializes calls - * to connect and disconnect so we don't need to lock evdev_table here. + * to connect and disconnect. */ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; int minor; + int dev_no; int error; - for (minor = 0; minor < EVDEV_MINORS; minor++) - if (!evdev_table[minor]) - break; - - if (minor == EVDEV_MINORS) { - pr_err("no more free evdev devices\n"); - return -ENFILE; + minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); + if (minor < 0) { + error = minor; + pr_err("failed to reserve new minor: %d\n", error); + return error; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); - if (!evdev) - return -ENOMEM; + if (!evdev) { + error = -ENOMEM; + goto err_free_minor; + } INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); - - dev_set_name(&evdev->dev, "event%d", minor); evdev->exist = true; - evdev->minor = minor; + + dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS) + dev_no -= EVDEV_MINOR_BASE; + dev_set_name(&evdev->dev, "event%d", dev_no); evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; - evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; @@ -1037,7 +1000,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_free_evdev; - error = evdev_install_chrdev(evdev); + cdev_init(&evdev->cdev, &evdev_fops); + error = cdev_add(&evdev->cdev, evdev->dev.devt, 1); if (error) goto err_unregister_handle; @@ -1053,6 +1017,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); + err_free_minor: + input_free_minor(minor); return error; } @@ -1062,6 +1028,7 @@ static void evdev_disconnect(struct input_handle *handle) device_del(&evdev->dev); evdev_cleanup(evdev); + input_free_minor(MINOR(evdev->dev.devt)); input_unregister_handle(handle); put_device(&evdev->dev); } @@ -1078,7 +1045,7 @@ static struct input_handler evdev_handler = { .events = evdev_events, .connect = evdev_connect, .disconnect = evdev_disconnect, - .fops = &evdev_fops, + .legacy_minors = true, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, diff --git a/drivers/input/input.c b/drivers/input/input.c index ace3f7c4226d..53a0ddee7872 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -32,7 +33,9 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Input core"); MODULE_LICENSE("GPL"); -#define INPUT_DEVICES 256 +#define INPUT_MAX_CHAR_DEVICES 1024 +#define INPUT_FIRST_DYNAMIC_DEV 256 +static DEFINE_IDA(input_ida); static LIST_HEAD(input_dev_list); static LIST_HEAD(input_handler_list); @@ -45,8 +48,6 @@ static LIST_HEAD(input_handler_list); */ static DEFINE_MUTEX(input_mutex); -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, @@ -1218,7 +1219,7 @@ static int input_handlers_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); if (handler->filter) seq_puts(seq, " (filter)"); - if (handler->fops) + if (handler->legacy_minors) seq_printf(seq, " Minor=%d", handler->minor); seq_putc(seq, '\n'); @@ -2016,22 +2017,14 @@ EXPORT_SYMBOL(input_unregister_device); int input_register_handler(struct input_handler *handler) { struct input_dev *dev; - int retval; + int error; - retval = mutex_lock_interruptible(&input_mutex); - if (retval) - return retval; + error = mutex_lock_interruptible(&input_mutex); + if (error) + return error; INIT_LIST_HEAD(&handler->h_list); - if (handler->fops != NULL) { - if (input_table[handler->minor >> 5]) { - retval = -EBUSY; - goto out; - } - input_table[handler->minor >> 5] = handler; - } - list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) @@ -2039,9 +2032,8 @@ int input_register_handler(struct input_handler *handler) input_wakeup_procfs_readers(); - out: mutex_unlock(&input_mutex); - return retval; + return 0; } EXPORT_SYMBOL(input_register_handler); @@ -2064,9 +2056,6 @@ void input_unregister_handler(struct input_handler *handler) list_del_init(&handler->node); - if (handler->fops != NULL) - input_table[handler->minor >> 5] = NULL; - input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); @@ -2183,51 +2172,52 @@ void input_unregister_handle(struct input_handle *handle) } EXPORT_SYMBOL(input_unregister_handle); -static int input_open_file(struct inode *inode, struct file *file) +/** + * input_get_new_minor - allocates a new input minor number + * @legacy_base: beginning or the legacy range to be searched + * @legacy_num: size of legacy range + * @allow_dynamic: whether we can also take ID from the dynamic range + * + * This function allocates a new device minor for from input major namespace. + * Caller can request legacy minor by specifying @legacy_base and @legacy_num + * parameters and whether ID can be allocated from dynamic range if there are + * no free IDs in legacy range. + */ +int input_get_new_minor(int legacy_base, unsigned int legacy_num, + bool allow_dynamic) { - struct input_handler *handler; - const struct file_operations *old_fops, *new_fops = NULL; - int err; - - err = mutex_lock_interruptible(&input_mutex); - if (err) - return err; - - /* No load-on-demand here? */ - handler = input_table[iminor(inode) >> 5]; - if (handler) - new_fops = fops_get(handler->fops); - - mutex_unlock(&input_mutex); - /* - * That's _really_ odd. Usually NULL ->open means "nothing special", - * not "no device". Oh, well... + * This function should be called from input handler's ->connect() + * methods, which are serialized with input_mutex, so no additional + * locking is needed here. */ - if (!new_fops || !new_fops->open) { - fops_put(new_fops); - err = -ENODEV; - goto out; + if (legacy_base >= 0) { + int minor = ida_simple_get(&input_ida, + legacy_base, + legacy_base + legacy_num, + GFP_KERNEL); + if (minor >= 0 || !allow_dynamic) + return minor; } - old_fops = file->f_op; - file->f_op = new_fops; - - err = new_fops->open(inode, file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); -out: - return err; + return ida_simple_get(&input_ida, + INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES, + GFP_KERNEL); } +EXPORT_SYMBOL(input_get_new_minor); -static const struct file_operations input_fops = { - .owner = THIS_MODULE, - .open = input_open_file, - .llseek = noop_llseek, -}; +/** + * input_free_minor - release previously allocated minor + * @minor: minor to be released + * + * This function releases previously allocated input minor so that it can be + * reused later. + */ +void input_free_minor(unsigned int minor) +{ + ida_simple_remove(&input_ida, minor); +} +EXPORT_SYMBOL(input_free_minor); static int __init input_init(void) { @@ -2243,7 +2233,8 @@ static int __init input_init(void) if (err) goto fail1; - err = register_chrdev(INPUT_MAJOR, "input", &input_fops); + err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), + INPUT_MAX_CHAR_DEVICES, "input"); if (err) { pr_err("unable to register char major %d", INPUT_MAJOR); goto fail2; @@ -2259,7 +2250,8 @@ static int __init input_init(void) static void __exit input_exit(void) { input_proc_exit(); - unregister_chrdev(INPUT_MAJOR, "input"); + unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0), + INPUT_MAX_CHAR_DEVICES); class_unregister(&input_class); } diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 78f323ea1e4b..b62b5891f399 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -27,6 +27,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Joystick device interfaces"); @@ -39,13 +40,13 @@ MODULE_LICENSE("GPL"); struct joydev { int open; - int minor; struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + struct cdev cdev; bool exist; struct js_corr corr[ABS_CNT]; @@ -70,9 +71,6 @@ struct joydev_client { struct list_head node; }; -static struct joydev *joydev_table[JOYDEV_MINORS]; -static DEFINE_MUTEX(joydev_table_mutex); - static int joydev_correct(int value, struct js_corr *corr) { switch (corr->type) { @@ -252,30 +250,14 @@ static int joydev_release(struct inode *inode, struct file *file) static int joydev_open(struct inode *inode, struct file *file) { + struct joydev *joydev = + container_of(inode->i_cdev, struct joydev, cdev); struct joydev_client *client; - struct joydev *joydev; - int i = iminor(inode) - JOYDEV_MINOR_BASE; int error; - if (i >= JOYDEV_MINORS) - return -ENODEV; - - error = mutex_lock_interruptible(&joydev_table_mutex); - if (error) - return error; - joydev = joydev_table[i]; - if (joydev) - get_device(&joydev->dev); - mutex_unlock(&joydev_table_mutex); - - if (!joydev) - return -ENODEV; - client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); - if (!client) { - error = -ENOMEM; - goto err_put_joydev; - } + if (!client) + return -ENOMEM; spin_lock_init(&client->buffer_lock); client->joydev = joydev; @@ -288,13 +270,12 @@ static int joydev_open(struct inode *inode, struct file *file) file->private_data = client; nonseekable_open(inode, file); + get_device(&joydev->dev); return 0; err_free_client: joydev_detach_client(joydev, client); kfree(client); - err_put_joydev: - put_device(&joydev->dev); return error; } @@ -742,19 +723,6 @@ static const struct file_operations joydev_fops = { .llseek = no_llseek, }; -static int joydev_install_chrdev(struct joydev *joydev) -{ - joydev_table[joydev->minor] = joydev; - return 0; -} - -static void joydev_remove_chrdev(struct joydev *joydev) -{ - mutex_lock(&joydev_table_mutex); - joydev_table[joydev->minor] = NULL; - mutex_unlock(&joydev_table_mutex); -} - /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted @@ -773,7 +741,8 @@ static void joydev_cleanup(struct joydev *joydev) joydev_mark_dead(joydev); joydev_hangup(joydev); - joydev_remove_chrdev(joydev); + + cdev_del(&joydev->cdev); /* joydev is marked dead so no one else accesses joydev->open */ if (joydev->open) @@ -798,30 +767,33 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct joydev *joydev; - int i, j, t, minor; + int i, j, t, minor, dev_no; int error; - for (minor = 0; minor < JOYDEV_MINORS; minor++) - if (!joydev_table[minor]) - break; - - if (minor == JOYDEV_MINORS) { - pr_err("no more free joydev devices\n"); - return -ENFILE; + minor = input_get_new_minor(JOYDEV_MINOR_BASE, JOYDEV_MINORS, true); + if (minor < 0) { + error = minor; + pr_err("failed to reserve new minor: %d\n", error); + return error; } joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); - if (!joydev) - return -ENOMEM; + if (!joydev) { + error = -ENOMEM; + goto err_free_minor; + } INIT_LIST_HEAD(&joydev->client_list); spin_lock_init(&joydev->client_lock); mutex_init(&joydev->mutex); init_waitqueue_head(&joydev->wait); - - dev_set_name(&joydev->dev, "js%d", minor); joydev->exist = true; - joydev->minor = minor; + + dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < JOYDEV_MINOR_BASE + JOYDEV_MINORS) + dev_no -= JOYDEV_MINOR_BASE; + dev_set_name(&joydev->dev, "js%d", dev_no); joydev->handle.dev = input_get_device(dev); joydev->handle.name = dev_name(&joydev->dev); @@ -875,7 +847,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, } } - joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.devt = MKDEV(INPUT_MAJOR, minor); joydev->dev.class = &input_class; joydev->dev.parent = &dev->dev; joydev->dev.release = joydev_free; @@ -885,7 +857,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_free_joydev; - error = joydev_install_chrdev(joydev); + cdev_init(&joydev->cdev, &joydev_fops); + error = cdev_add(&joydev->cdev, joydev->dev.devt, 1); if (error) goto err_unregister_handle; @@ -901,6 +874,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, input_unregister_handle(&joydev->handle); err_free_joydev: put_device(&joydev->dev); + err_free_minor: + input_free_minor(minor); return error; } @@ -910,6 +885,7 @@ static void joydev_disconnect(struct input_handle *handle) device_del(&joydev->dev); joydev_cleanup(joydev); + input_free_minor(MINOR(joydev->dev.devt)); input_unregister_handle(handle); put_device(&joydev->dev); } @@ -961,7 +937,7 @@ static struct input_handler joydev_handler = { .match = joydev_match, .connect = joydev_connect, .disconnect = joydev_disconnect, - .fops = &joydev_fops, + .legacy_minors = true, .minor = JOYDEV_MINOR_BASE, .name = "joydev", .id_table = joydev_ids, diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 88b962aeef13..a1b4c37956b2 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -24,6 +24,7 @@ #include #include #include +#include #include MODULE_AUTHOR("Vojtech Pavlik "); @@ -58,14 +59,15 @@ struct mousedev_hw_data { struct mousedev { int open; - int minor; struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + struct cdev cdev; bool exist; + bool is_mixdev; struct list_head mixdev_node; bool opened_by_mixdev; @@ -111,10 +113,6 @@ struct mousedev_client { static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; -static struct input_handler mousedev_handler; - -static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; -static DEFINE_MUTEX(mousedev_table_mutex); static struct mousedev *mousedev_mix; static LIST_HEAD(mousedev_mix_list); @@ -430,7 +428,7 @@ static int mousedev_open_device(struct mousedev *mousedev) if (retval) return retval; - if (mousedev->minor == MOUSEDEV_MIX) + if (mousedev->is_mixdev) mixdev_open_devices(); else if (!mousedev->exist) retval = -ENODEV; @@ -448,7 +446,7 @@ static void mousedev_close_device(struct mousedev *mousedev) { mutex_lock(&mousedev->mutex); - if (mousedev->minor == MOUSEDEV_MIX) + if (mousedev->is_mixdev) mixdev_close_devices(); else if (mousedev->exist && !--mousedev->open) input_close_device(&mousedev->handle); @@ -535,35 +533,17 @@ static int mousedev_open(struct inode *inode, struct file *file) struct mousedev_client *client; struct mousedev *mousedev; int error; - int i; #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX if (imajor(inode) == MISC_MAJOR) - i = MOUSEDEV_MIX; + mousedev = mousedev_mix; else #endif - i = iminor(inode) - MOUSEDEV_MINOR_BASE; - - if (i >= MOUSEDEV_MINORS) - return -ENODEV; - - error = mutex_lock_interruptible(&mousedev_table_mutex); - if (error) - return error; - - mousedev = mousedev_table[i]; - if (mousedev) - get_device(&mousedev->dev); - mutex_unlock(&mousedev_table_mutex); - - if (!mousedev) - return -ENODEV; + mousedev = container_of(inode->i_cdev, struct mousedev, cdev); client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); - if (!client) { - error = -ENOMEM; - goto err_put_mousedev; - } + if (!client) + return -ENOMEM; spin_lock_init(&client->packet_lock); client->pos_x = xres / 2; @@ -577,13 +557,13 @@ static int mousedev_open(struct inode *inode, struct file *file) file->private_data = client; nonseekable_open(inode, file); + + get_device(&mousedev->dev); return 0; err_free_client: mousedev_detach_client(mousedev, client); kfree(client); - err_put_mousedev: - put_device(&mousedev->dev); return error; } @@ -793,19 +773,6 @@ static const struct file_operations mousedev_fops = { .llseek = noop_llseek, }; -static int mousedev_install_chrdev(struct mousedev *mousedev) -{ - mousedev_table[mousedev->minor] = mousedev; - return 0; -} - -static void mousedev_remove_chrdev(struct mousedev *mousedev) -{ - mutex_lock(&mousedev_table_mutex); - mousedev_table[mousedev->minor] = NULL; - mutex_unlock(&mousedev_table_mutex); -} - /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted @@ -840,24 +807,50 @@ static void mousedev_cleanup(struct mousedev *mousedev) mousedev_mark_dead(mousedev); mousedev_hangup(mousedev); - mousedev_remove_chrdev(mousedev); + + cdev_del(&mousedev->cdev); /* mousedev is marked dead so no one else accesses mousedev->open */ if (mousedev->open) input_close_device(handle); } +static int mousedev_reserve_minor(bool mixdev) +{ + int minor; + + if (mixdev) { + minor = input_get_new_minor(MOUSEDEV_MIX, 1, false); + if (minor < 0) + pr_err("failed to reserve mixdev minor: %d\n", minor); + } else { + minor = input_get_new_minor(MOUSEDEV_MINOR_BASE, + MOUSEDEV_MINORS, true); + if (minor < 0) + pr_err("failed to reserve new minor: %d\n", minor); + } + + return minor; +} + static struct mousedev *mousedev_create(struct input_dev *dev, struct input_handler *handler, - int minor) + bool mixdev) { struct mousedev *mousedev; + int minor; int error; + minor = mousedev_reserve_minor(mixdev); + if (minor < 0) { + error = minor; + goto err_out; + } + mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); if (!mousedev) { error = -ENOMEM; - goto err_out; + goto err_free_minor; } INIT_LIST_HEAD(&mousedev->client_list); @@ -865,16 +858,21 @@ static struct mousedev *mousedev_create(struct input_dev *dev, spin_lock_init(&mousedev->client_lock); mutex_init(&mousedev->mutex); lockdep_set_subclass(&mousedev->mutex, - minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); + mixdev ? SINGLE_DEPTH_NESTING : 0); init_waitqueue_head(&mousedev->wait); - if (minor == MOUSEDEV_MIX) + if (mixdev) { dev_set_name(&mousedev->dev, "mice"); - else - dev_set_name(&mousedev->dev, "mouse%d", minor); + } else { + int dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS) + dev_no -= MOUSEDEV_MINOR_BASE; + dev_set_name(&mousedev->dev, "mouse%d", dev_no); + } - mousedev->minor = minor; mousedev->exist = true; + mousedev->is_mixdev = mixdev; mousedev->handle.dev = input_get_device(dev); mousedev->handle.name = dev_name(&mousedev->dev); mousedev->handle.handler = handler; @@ -883,17 +881,18 @@ static struct mousedev *mousedev_create(struct input_dev *dev, mousedev->dev.class = &input_class; if (dev) mousedev->dev.parent = &dev->dev; - mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor); + mousedev->dev.devt = MKDEV(INPUT_MAJOR, minor); mousedev->dev.release = mousedev_free; device_initialize(&mousedev->dev); - if (minor != MOUSEDEV_MIX) { + if (!mixdev) { error = input_register_handle(&mousedev->handle); if (error) goto err_free_mousedev; } - error = mousedev_install_chrdev(mousedev); + cdev_init(&mousedev->cdev, &mousedev_fops); + error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1); if (error) goto err_unregister_handle; @@ -906,10 +905,12 @@ static struct mousedev *mousedev_create(struct input_dev *dev, err_cleanup_mousedev: mousedev_cleanup(mousedev); err_unregister_handle: - if (minor != MOUSEDEV_MIX) + if (!mixdev) input_unregister_handle(&mousedev->handle); err_free_mousedev: put_device(&mousedev->dev); + err_free_minor: + input_free_minor(minor); err_out: return ERR_PTR(error); } @@ -918,7 +919,8 @@ static void mousedev_destroy(struct mousedev *mousedev) { device_del(&mousedev->dev); mousedev_cleanup(mousedev); - if (mousedev->minor != MOUSEDEV_MIX) + input_free_minor(MINOR(mousedev->dev.devt)); + if (!mousedev->is_mixdev) input_unregister_handle(&mousedev->handle); put_device(&mousedev->dev); } @@ -967,19 +969,9 @@ static int mousedev_connect(struct input_handler *handler, const struct input_device_id *id) { struct mousedev *mousedev; - int minor; int error; - for (minor = 0; minor < MOUSEDEV_MINORS; minor++) - if (!mousedev_table[minor]) - break; - - if (minor == MOUSEDEV_MINORS) { - pr_err("no more free mousedev devices\n"); - return -ENFILE; - } - - mousedev = mousedev_create(dev, handler, minor); + mousedev = mousedev_create(dev, handler, false); if (IS_ERR(mousedev)) return PTR_ERR(mousedev); @@ -1055,7 +1047,7 @@ static struct input_handler mousedev_handler = { .event = mousedev_event, .connect = mousedev_connect, .disconnect = mousedev_disconnect, - .fops = &mousedev_fops, + .legacy_minors = true, .minor = MOUSEDEV_MINOR_BASE, .name = "mousedev", .id_table = mousedev_ids, @@ -1098,7 +1090,7 @@ static int __init mousedev_init(void) { int error; - mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); + mousedev_mix = mousedev_create(NULL, &mousedev_handler, true); if (IS_ERR(mousedev_mix)) return PTR_ERR(mousedev_mix); diff --git a/include/linux/input.h b/include/linux/input.h index ba4874302939..15464ba6bf53 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1396,8 +1396,8 @@ struct input_handle; * @start: starts handler for given handle. This function is called by * input core right after connect() method and also when a process * that "grabbed" a device releases it - * @fops: file operations this driver implements - * @minor: beginning of range of 32 minors for devices this driver + * @legacy_minors: set to %true by drivers using legacy minor ranges + * @minor: beginning of range of 32 legacy minors for devices this driver * can provide * @name: name of the handler, to be shown in /proc/bus/input/handlers * @id_table: pointer to a table of input_device_ids this driver can @@ -1431,7 +1431,7 @@ struct input_handler { void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); - const struct file_operations *fops; + bool legacy_minors; int minor; const char *name; @@ -1499,6 +1499,10 @@ void input_reset_device(struct input_dev *); int __must_check input_register_handler(struct input_handler *); void input_unregister_handler(struct input_handler *); +int __must_check input_get_new_minor(int legacy_base, unsigned int legacy_num, + bool allow_dynamic); +void input_free_minor(unsigned int minor); + int input_handler_for_each_handle(struct input_handler *, void *data, int (*fn)(struct input_handle *, void *)); -- cgit v1.2.3