diff options
Diffstat (limited to 'mtdev/core.c')
-rw-r--r-- | mtdev/core.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/mtdev/core.c b/mtdev/core.c new file mode 100644 index 0000000..c8eae37 --- /dev/null +++ b/mtdev/core.c @@ -0,0 +1,402 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + +#include "mtdev.h" +#include "mtbit.h" + +/** + * struct MTSlot - represents the state of an input MT slot + * @abs: current values of ABS_MT axes for this slot + */ +struct MTSlot { + int abs[MT_ABS_SIZE]; +}; + +/** + * struct MTDevState - MT slot parsing + * @data: array of scratch slot data + * @used: bitmask of currently used slots + * @slot: slot currently being modified + * @lastid: last used tracking id + */ +struct MTDevState { + struct MTSlot data[DIM_FINGER]; + bitmask_t used; + bitmask_t slot; + bitmask_t lastid; +}; + +/** + * mtdev_init - init MT device + * @dev: device to initialize + * @caps: device capabilities + */ +int mtdev_init(struct MTDev *dev, const struct Capabilities *caps) +{ + memset(dev, 0, sizeof(struct MTDev)); + if (!caps->has_mtdata) + return -ENODEV; + if (!caps->has_slot) { + dev->priv = calloc(1, sizeof(struct MTDevState)); + if (!dev->priv) + return -ENOMEM; + } + return 0; +} + +static inline int istouch(const struct MTSlot *data, + const struct Capabilities *caps) +{ + return data->abs[BIT_TOUCH_MAJOR] || !caps->has_abs[BIT_TOUCH_MAJOR]; +} + +/* Dmitry Torokhov's code from kernel/driver/input/input.c */ +static int defuzz(int value, int old_val, int fuzz) +{ + if (fuzz) { + if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2) + return old_val; + + if (value > old_val - fuzz && value < old_val + fuzz) + return (old_val * 3 + value) / 4; + + if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2) + return (old_val + value) / 2; + } + + return value; +} + +/* + * solve - solve contact matching problem + * @priv: parsing state + * @caps: device capabilities + * @sid: array of current tracking ids + * @sx: array of current position x + * @sy: array of current position y + * @sn: number of current contacts + * @nid: array of new or matched tracking ids, to be filled + * @nx: array of new position x + * @ny: array of new position y + * @nn: number of new contacts + * @touch: which of the new contacts to fill + */ +static void solve(struct MTDevState *priv, const struct Capabilities *caps, + const int *sid, const int *sx, const int *sy, int sn, + int *nid, const int *nx, const int *ny, int nn, + bitmask_t touch) +{ + int A[DIM2_FINGER], *row; + int n2s[DIM_FINGER]; + int id, i, j; + + /* setup distance matrix for contact matching */ + for (j = 0; j < sn; j++) { + row = A + nn * j; + for (i = 0; i < nn; i++) + row[i] = dist2(nx[i] - sx[j], ny[i] - sy[j]); + } + + match_fingers(n2s, A, nn, sn); + + /* update matched contacts and create new ones */ + foreach_bit(i, touch) { + j = n2s[i]; + id = j >= 0 ? sid[j] : caps->nullid; + while (id == caps->nullid) + id = ++priv->lastid; + nid[i] = id; + } +} + +/* + * assign_tracking_id - assign tracking ids to all contacts + * @priv: parsing state + * @caps: device capabilities + * @data: array of all present contacts, to be filled + * @prop: array of all set contacts properties + * @size: number of contacts in array + * @touch: which of the contacts are actual touches + */ +static void assign_tracking_id(struct MTDevState *priv, + const struct Capabilities *caps, + struct MTSlot *data, bitmask_t *prop, + int size, bitmask_t touch) +{ + int sid[DIM_FINGER], sx[DIM_FINGER], sy[DIM_FINGER], sn = 0; + int nid[DIM_FINGER], nx[DIM_FINGER], ny[DIM_FINGER], i; + foreach_bit(i, priv->used) { + sid[sn] = priv->data[i].abs[BIT_TRACKING_ID]; + sx[sn] = priv->data[i].abs[BIT_POSITION_X]; + sy[sn] = priv->data[i].abs[BIT_POSITION_Y]; + sn++; + } + for (i = 0; i < size; i++) { + nx[i] = data[i].abs[BIT_POSITION_X]; + ny[i] = data[i].abs[BIT_POSITION_Y]; + } + solve(priv, caps, sid, sx, sy, sn, nid, nx, ny, size, touch); + for (i = 0; i < size; i++) { + data[i].abs[BIT_TRACKING_ID] = + GETBIT(touch, i) ? nid[i] : caps->nullid; + prop[i] |= BITMASK(BIT_TRACKING_ID); + } +} + +/* + * process_typeA - consume MT events and update parsing state + * @dev: MT device + * @data: array of all present contacts, to be filled + * @prop: array of all set contacts properties, to be filled + * + * This function is called when a SYN_REPORT is seen, right before + * that event is pushed to the queue. + * + * Returns -1 if the packet is not MT related and should not affect + * the current parsing state. + */ +static int process_typeA(struct MTDev *dev, + struct MTSlot *data, bitmask_t *prop) +{ + struct input_event ev; + int consumed, mtcode; + int mtcnt = 0, size = 0; + prop[size] = 0; + while (!evbuf_empty(&dev->inbuf)) { + evbuf_pop(&dev->inbuf, &ev); + consumed = 0; + switch (ev.type) { + case EV_SYN: + switch (ev.code) { + case SYN_MT_REPORT: + if (size < DIM_FINGER && + GETBIT(prop[size], BIT_POSITION_X) && + GETBIT(prop[size], BIT_POSITION_Y)) + size++; + if (size < DIM_FINGER) + prop[size] = 0; + mtcnt++; + consumed = 1; + break; + } + break; + case EV_KEY: + switch (ev.code) { + case BTN_TOUCH: + mtcnt++; + break; + } + break; + case EV_ABS: + if (size < DIM_FINGER && has_abs2mt(ev.code)) { + mtcode = abs2mt(ev.code); + data[size].abs[mtcode] = ev.value; + prop[size] |= BITMASK(mtcode); + mtcnt++; + consumed = 1; + } + break; + } + if (!consumed) + evbuf_push(&dev->outbuf, &ev); + } + return mtcnt ? size : -1; +} + +/* + * process_typeB - propagate events without parsing + * @dev: MT device + * + * This function is called when a SYN_REPORT is seen, right before + * that event is pushed to the queue. + */ +static void process_typeB(struct MTDev *dev) +{ + struct input_event ev; + while (!evbuf_empty(&dev->inbuf)) { + evbuf_pop(&dev->inbuf, &ev); + evbuf_push(&dev->outbuf, &ev); + } +} + +/* + * filter_data - apply input filtering on new incoming data + * @priv: parsing state + * @caps: device capabilities + * @data: the incoming data to filter + * @prop: the properties to filter + * @slot: the slot the data refers to + */ +static void filter_data(const struct MTDevState *priv, + const struct Capabilities *caps, + struct MTSlot *data, bitmask_t prop, + int slot) +{ + int i; + foreach_bit(i, prop) { + int fuzz = caps->abs[i].fuzz; + int oldval = priv->data[slot].abs[i]; + data->abs[i] = defuzz(data->abs[i], oldval, fuzz); + } +} + +/* + * push_slot_changes - propagate state changes + * @dev: MT device + * @data: the incoming data to propagate + * @prop: the properties to propagate + * @slot: the slot the data refers to + * @syn: reference to the SYN_REPORT event + */ +static void push_slot_changes(struct MTDev *dev, + const struct MTSlot *data, bitmask_t prop, + int slot, const struct input_event *syn) +{ + struct MTDevState *priv = dev->priv; + struct input_event ev; + int i, count = 0; + foreach_bit(i, prop) + if (priv->data[slot].abs[i] != data->abs[i]) + count++; + if (!count) + return; + ev.time = syn->time; + ev.type = EV_ABS; + ev.code = ABS_MT_SLOT; + ev.value = slot; + if (priv->slot != ev.value) { + evbuf_push(&dev->outbuf, &ev); + priv->slot = ev.value; + } + foreach_bit(i, prop) { + ev.code = mt2abs(i); + ev.value = data->abs[i]; + if (priv->data[slot].abs[i] != ev.value) { + evbuf_push(&dev->outbuf, &ev); + priv->data[slot].abs[i] = ev.value; + } + } +} + +/* + * apply_typeA_changes - parse and propagate state changes + * @dev: MT device + * @caps: device capabilities + * @data: array of data to apply + * @prop: array of properties to apply + * @size: number of contacts in array + * @syn: reference to the SYN_REPORT event + */ +static void apply_typeA_changes(struct MTDev *dev, + const struct Capabilities *caps, + struct MTSlot *data, const bitmask_t *prop, + int size, const struct input_event *syn) +{ + struct MTDevState *priv = dev->priv; + bitmask_t unused = ~priv->used; + bitmask_t used = 0; + int i, slot, id; + for (i = 0; i < size; i++) { + id = data[i].abs[BIT_TRACKING_ID]; + foreach_bit(slot, priv->used) { + if (priv->data[slot].abs[BIT_TRACKING_ID] != id) + continue; + filter_data(priv, caps, &data[i], prop[i], slot); + push_slot_changes(dev, &data[i], prop[i], slot, syn); + SETBIT(used, slot); + id = caps->nullid; + break; + } + if (id != caps->nullid) { + slot = firstbit(unused); + push_slot_changes(dev, &data[i], prop[i], slot, syn); + SETBIT(used, slot); + CLEARBIT(unused, slot); + } + } + + /* clear unused slots and update slot usage */ + foreach_bit(slot, priv->used & ~used) { + struct MTSlot tdata = priv->data[slot]; + bitmask_t tprop = BITMASK(BIT_TRACKING_ID); + tdata.abs[BIT_TRACKING_ID] = caps->nullid; + push_slot_changes(dev, &tdata, tprop, slot, syn); + } + priv->used = used; +} + +/* + * convert_A_to_B - propagate a type A packet as a type B packet + * @dev: MT device + * @caps: device capabilities + * @syn: reference to the SYN_REPORT event + */ +static void convert_A_to_B(struct MTDev *dev, + const struct Capabilities *caps, + const struct input_event *syn) +{ + struct MTSlot data[DIM_FINGER]; + bitmask_t prop[DIM_FINGER]; + int size = process_typeA(dev, data, prop); + if (size < 0) + return; + if (!caps->has_abs[BIT_TRACKING_ID]) { + bitmask_t touch = 0; + int i; + for (i = 0; i < size; i++) + MODBIT(touch, i, istouch(&data[i], caps)); + assign_tracking_id(dev->priv, caps, data, prop, size, touch); + } + apply_typeA_changes(dev, caps, data, prop, size, syn); +} + +/** + * mtdev_push - insert event into MT device + * @dev: MT device + * @caps: device capabilities + * @syn: reference to the SYN_REPORT event + */ +void mtdev_push(struct MTDev *dev, + const struct Capabilities *caps, + const struct input_event *ev) +{ + if (ev->type == EV_SYN && ev->code == SYN_REPORT) { + bitmask_t head = dev->outbuf.head; + if (dev->priv) + convert_A_to_B(dev, caps, ev); + else + process_typeB(dev); + if (dev->outbuf.head != head) + evbuf_push(&dev->outbuf, ev); + } else { + evbuf_push(&dev->inbuf, ev); + } +} + +/** + * mtdev_destroy - destroy MT device + * @dev: MT device + */ +void mtdev_destroy(struct MTDev *dev) +{ + free(dev->priv); + memset(dev, 0, sizeof(struct MTDev)); +} |