[PATCH 6/6] hid: egalax: Convert to MT slots

From: Henrik Rydberg
Date: Wed Oct 13 2010 - 10:00:19 EST


The joojoo reports touches sequentially, one per report, which
confuses the current driver. Convert to the MT slots protocol and use
the stored slot information to emulate pointer movement in a stable
manner.

Tested-by: Philipp Merkel <mail@xxxxxxxxxxx>
Signed-off-by: Henrik Rydberg <rydberg@xxxxxxxxxxx>
---
drivers/hid/hid-egalax.c | 133 ++++++++++++++++++++++++---------------------
1 files changed, 71 insertions(+), 62 deletions(-)

diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c
index b21e7dc..1ca0f84 100644
--- a/drivers/hid/hid-egalax.c
+++ b/drivers/hid/hid-egalax.c
@@ -2,6 +2,8 @@
* HID driver for eGalax dual-touch panels
*
* Copyright (c) 2010 Stephane Chatty <chatty@xxxxxxx>
+ * Copyright (c) 2010 Henrik Rydberg <rydberg@xxxxxxxxxxx>
+ * Copyright (c) 2010 Canonical, Ltd.
*
*/

@@ -25,19 +27,25 @@ MODULE_LICENSE("GPL");

#include "hid-ids.h"

+#define MAX_SLOTS 2
+#define MAX_TRKID USHRT_MAX
#define MAX_EVENTS 120

/* estimated signal-to-noise ratios */
#define SN_MOVE 1024
#define SN_PRESSURE 32

+struct egalax_contact {
+ int touch;
+ int x, y, z;
+};
+
struct egalax_data {
- __u16 x, y, z;
- __u8 id;
- bool first; /* is this the first finger in the frame? */
- bool valid; /* valid finger data, or just placeholder? */
- bool activity; /* at least one active finger previously? */
- __u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */
+ struct egalax_contact contact[MAX_SLOTS];
+ struct egalax_contact single, tmp;
+ int valid;
+ int slot;
+ int trkid;
};

static void set_abs(struct input_dev *input, unsigned int code,
@@ -91,10 +99,13 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_DG_CONTACTMAX:
return -1;
case HID_DG_CONTACTID:
+ field->logical_maximum = MAX_TRKID;
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TRACKING_ID);
set_abs(input, ABS_MT_TRACKING_ID, field, 0);
input_set_events_per_packet(input, MAX_EVENTS);
+ if (!input->mt)
+ input_mt_create_slots(input, MAX_SLOTS);
return 1;
case HID_DG_TIPPRESSURE:
field->logical_minimum = 0;
@@ -122,64 +133,61 @@ static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi,
return -1;
}

+static void emulate_pointer(struct egalax_data *td, struct input_dev *input)
+{
+ struct egalax_contact *s = &td->single;
+ struct egalax_contact *best = 0;
+ int dbest, i;
+
+ for (i = 0; i < MAX_SLOTS; i++) {
+ struct egalax_contact *f = &td->contact[i];
+ if (f->touch) {
+ int d = abs(f->x - s->x) + abs(f->y - s->y);
+ if (!best || d < dbest) {
+ best = f;
+ dbest = d;
+ }
+ }
+ }
+
+ if (best) {
+ input_event(input, EV_KEY, BTN_TOUCH, 1);
+ input_event(input, EV_ABS, ABS_X, best->x);
+ input_event(input, EV_ABS, ABS_Y, best->y);
+ input_event(input, EV_ABS, ABS_PRESSURE, best->z);
+ *s = *best;
+ } else {
+ input_event(input, EV_KEY, BTN_TOUCH, 0);
+ }
+}
+
+
/*
* this function is called when a whole finger has been parsed,
* so that it can decide what to send to the input layer.
*/
static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
{
- td->first = !td->first; /* touchscreen emulation */
-
- if (td->valid) {
- /* emit multitouch events */
- input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
- input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3);
- input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3);
- input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
-
- input_mt_sync(input);
-
- /*
- * touchscreen emulation: store (x, y) as
- * the last valid values in this frame
- */
- td->lastx = td->x;
- td->lasty = td->y;
- td->lastz = td->z;
- }
-
- /*
- * touchscreen emulation: if this is the second finger and at least
- * one in this frame is valid, the latest valid in the frame is
- * the oldest on the panel, the one we want for single touch
- */
- if (!td->first && td->activity) {
- input_event(input, EV_ABS, ABS_X, td->lastx >> 3);
- input_event(input, EV_ABS, ABS_Y, td->lasty >> 3);
- input_event(input, EV_ABS, ABS_PRESSURE, td->lastz);
- }
-
- if (!td->valid) {
- /*
- * touchscreen emulation: if the first finger is invalid
- * and there previously was finger activity, this is a release
- */
- if (td->first && td->activity) {
- input_event(input, EV_KEY, BTN_TOUCH, 0);
- td->activity = false;
+ struct egalax_contact *old = &td->contact[td->slot];
+ struct egalax_contact *f = &td->tmp;
+
+ input_mt_slot(input, td->slot);
+ if (f->touch) {
+ if (!old->touch) {
+ int id = td->trkid++ & MAX_TRKID;
+ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id);
}
- return;
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
+ input_event(input, EV_ABS, ABS_MT_PRESSURE, f->z);
+ } else {
+ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1);
}
+ *old = *f;

-
- /* touchscreen emulation: if no previous activity, emit touch event */
- if (!td->activity) {
- input_event(input, EV_KEY, BTN_TOUCH, 1);
- td->activity = true;
- }
+ emulate_pointer(td, input);
}

-
static int egalax_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
@@ -189,37 +197,38 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field,
* uses a standard parallel multitouch protocol (product ID ==
* 48xx). The second is capacitive and uses an unusual "serial"
* protocol with a different message for each multitouch finger
- * (product ID == 72xx). We do not yet generate a correct event
- * sequence for the capacitive/serial protocol.
+ * (product ID == 72xx).
*/
if (hid->claimed & HID_CLAIMED_INPUT) {
struct input_dev *input = field->hidinput->input;

switch (usage->hid) {
case HID_DG_INRANGE:
+ td->valid = value;
+ break;
case HID_DG_CONFIDENCE:
/* avoid interference from generic hidinput handling */
break;
case HID_DG_TIPSWITCH:
- td->valid = value;
+ td->tmp.touch = value;
break;
case HID_DG_TIPPRESSURE:
- td->z = value;
+ td->tmp.z = value;
break;
case HID_DG_CONTACTID:
- td->id = value;
+ td->slot = clamp_val(value, 0, MAX_SLOTS - 1);
break;
case HID_GD_X:
- td->x = value;
+ td->tmp.x = value;
break;
case HID_GD_Y:
- td->y = value;
+ td->tmp.y = value;
/* this is the last field in a finger */
- egalax_filter_event(td, input);
+ if (td->valid)
+ egalax_filter_event(td, input);
break;
case HID_DG_CONTACTCOUNT:
/* touch emulation: this is the last field in a frame */
- td->first = false;
break;

default:
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/