diff options
author | Henrik Rydberg <rydberg@euromail.se> | 2012-08-12 14:47:05 -0400 |
---|---|---|
committer | Henrik Rydberg <rydberg@euromail.se> | 2012-09-19 13:50:19 -0400 |
commit | 7c1a87897c75139dec258eb03e1a24fb73385b73 (patch) | |
tree | 0cc927c32fea65d1938881fd499aba6be9f8a6c3 /drivers/input/input-mt.c | |
parent | 55e49089f4589908eb688742d2d7eff33b74ac78 (diff) |
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 <benjamin.tissoires@enac.fr>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Diffstat (limited to 'drivers/input/input-mt.c')
-rw-r--r-- | drivers/input/input-mt.c | 144 |
1 files changed, 142 insertions, 2 deletions
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, | |||
46 | 46 | ||
47 | mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL); | 47 | mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL); |
48 | if (!mt) | 48 | if (!mt) |
49 | return -ENOMEM; | 49 | goto err_mem; |
50 | 50 | ||
51 | mt->num_slots = num_slots; | 51 | mt->num_slots = num_slots; |
52 | mt->flags = flags; | 52 | mt->flags = flags; |
@@ -74,6 +74,12 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, | |||
74 | } | 74 | } |
75 | if (flags & INPUT_MT_DIRECT) | 75 | if (flags & INPUT_MT_DIRECT) |
76 | __set_bit(INPUT_PROP_DIRECT, dev->propbit); | 76 | __set_bit(INPUT_PROP_DIRECT, dev->propbit); |
77 | if (flags & INPUT_MT_TRACK) { | ||
78 | unsigned int n2 = num_slots * num_slots; | ||
79 | mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL); | ||
80 | if (!mt->red) | ||
81 | goto err_mem; | ||
82 | } | ||
77 | 83 | ||
78 | /* Mark slots as 'unused' */ | 84 | /* Mark slots as 'unused' */ |
79 | for (i = 0; i < num_slots; i++) | 85 | for (i = 0; i < num_slots; i++) |
@@ -81,6 +87,9 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, | |||
81 | 87 | ||
82 | dev->mt = mt; | 88 | dev->mt = mt; |
83 | return 0; | 89 | return 0; |
90 | err_mem: | ||
91 | kfree(mt); | ||
92 | return -ENOMEM; | ||
84 | } | 93 | } |
85 | EXPORT_SYMBOL(input_mt_init_slots); | 94 | EXPORT_SYMBOL(input_mt_init_slots); |
86 | 95 | ||
@@ -93,7 +102,10 @@ EXPORT_SYMBOL(input_mt_init_slots); | |||
93 | */ | 102 | */ |
94 | void input_mt_destroy_slots(struct input_dev *dev) | 103 | void input_mt_destroy_slots(struct input_dev *dev) |
95 | { | 104 | { |
96 | kfree(dev->mt); | 105 | if (dev->mt) { |
106 | kfree(dev->mt->red); | ||
107 | kfree(dev->mt); | ||
108 | } | ||
97 | dev->mt = NULL; | 109 | dev->mt = NULL; |
98 | } | 110 | } |
99 | EXPORT_SYMBOL(input_mt_destroy_slots); | 111 | EXPORT_SYMBOL(input_mt_destroy_slots); |
@@ -243,3 +255,131 @@ void input_mt_sync_frame(struct input_dev *dev) | |||
243 | mt->frame++; | 255 | mt->frame++; |
244 | } | 256 | } |
245 | EXPORT_SYMBOL(input_mt_sync_frame); | 257 | EXPORT_SYMBOL(input_mt_sync_frame); |
258 | |||
259 | static int adjust_dual(int *begin, int step, int *end, int eq) | ||
260 | { | ||
261 | int f, *p, s, c; | ||
262 | |||
263 | if (begin == end) | ||
264 | return 0; | ||
265 | |||
266 | f = *begin; | ||
267 | p = begin + step; | ||
268 | s = p == end ? f + 1 : *p; | ||
269 | |||
270 | for (; p != end; p += step) | ||
271 | if (*p < f) | ||
272 | s = f, f = *p; | ||
273 | else if (*p < s) | ||
274 | s = *p; | ||
275 | |||
276 | c = (f + s + 1) / 2; | ||
277 | if (c == 0 || (c > 0 && !eq)) | ||
278 | return 0; | ||
279 | if (s < 0) | ||
280 | c *= 2; | ||
281 | |||
282 | for (p = begin; p != end; p += step) | ||
283 | *p -= c; | ||
284 | |||
285 | return (c < s && s <= 0) || (f >= 0 && f < c); | ||
286 | } | ||
287 | |||
288 | static void find_reduced_matrix(int *w, int nr, int nc, int nrc) | ||
289 | { | ||
290 | int i, k, sum; | ||
291 | |||
292 | for (k = 0; k < nrc; k++) { | ||
293 | for (i = 0; i < nr; i++) | ||
294 | adjust_dual(w + i, nr, w + i + nrc, nr <= nc); | ||
295 | sum = 0; | ||
296 | for (i = 0; i < nrc; i += nr) | ||
297 | sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr); | ||
298 | if (!sum) | ||
299 | break; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | static int input_mt_set_matrix(struct input_mt *mt, | ||
304 | const struct input_mt_pos *pos, int num_pos) | ||
305 | { | ||
306 | const struct input_mt_pos *p; | ||
307 | struct input_mt_slot *s; | ||
308 | int *w = mt->red; | ||
309 | int x, y; | ||
310 | |||
311 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
312 | if (!input_mt_is_active(s)) | ||
313 | continue; | ||
314 | x = input_mt_get_value(s, ABS_MT_POSITION_X); | ||
315 | y = input_mt_get_value(s, ABS_MT_POSITION_Y); | ||
316 | for (p = pos; p != pos + num_pos; p++) { | ||
317 | int dx = x - p->x, dy = y - p->y; | ||
318 | *w++ = dx * dx + dy * dy; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | return w - mt->red; | ||
323 | } | ||
324 | |||
325 | static void input_mt_set_slots(struct input_mt *mt, | ||
326 | int *slots, int num_pos) | ||
327 | { | ||
328 | struct input_mt_slot *s; | ||
329 | int *w = mt->red, *p; | ||
330 | |||
331 | for (p = slots; p != slots + num_pos; p++) | ||
332 | *p = -1; | ||
333 | |||
334 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
335 | if (!input_mt_is_active(s)) | ||
336 | continue; | ||
337 | for (p = slots; p != slots + num_pos; p++) | ||
338 | if (*w++ < 0) | ||
339 | *p = s - mt->slots; | ||
340 | } | ||
341 | |||
342 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
343 | if (input_mt_is_active(s)) | ||
344 | continue; | ||
345 | for (p = slots; p != slots + num_pos; p++) | ||
346 | if (*p < 0) { | ||
347 | *p = s - mt->slots; | ||
348 | break; | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /** | ||
354 | * input_mt_assign_slots() - perform a best-match assignment | ||
355 | * @dev: input device with allocated MT slots | ||
356 | * @slots: the slot assignment to be filled | ||
357 | * @pos: the position array to match | ||
358 | * @num_pos: number of positions | ||
359 | * | ||
360 | * Performs a best match against the current contacts and returns | ||
361 | * the slot assignment list. New contacts are assigned to unused | ||
362 | * slots. | ||
363 | * | ||
364 | * Returns zero on success, or negative error in case of failure. | ||
365 | */ | ||
366 | int input_mt_assign_slots(struct input_dev *dev, int *slots, | ||
367 | const struct input_mt_pos *pos, int num_pos) | ||
368 | { | ||
369 | struct input_mt *mt = dev->mt; | ||
370 | int nrc; | ||
371 | |||
372 | if (!mt || !mt->red) | ||
373 | return -ENXIO; | ||
374 | if (num_pos > mt->num_slots) | ||
375 | return -EINVAL; | ||
376 | if (num_pos < 1) | ||
377 | return 0; | ||
378 | |||
379 | nrc = input_mt_set_matrix(mt, pos, num_pos); | ||
380 | find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc); | ||
381 | input_mt_set_slots(mt, slots, num_pos); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | EXPORT_SYMBOL(input_mt_assign_slots); | ||