diff mbox

[08/10] elantech: track finger to distinguish coordinates in 2-finger report

Message ID 4C5F3580.2090805@tudelft.nl (mailing list archive)
State New, archived
Headers show

Commit Message

Éric Piel Aug. 8, 2010, 10:53 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 7fa4f29..1dd7e7c 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -242,6 +242,45 @@  static void elantech_report_absolute_v1(struct psmouse *psmouse)
 	input_sync(dev);
 }
 
+/* Store the current position of the finger, for tracking when 2 fingers */
+static void track_one_finger(struct elantech_data *etd, unsigned int x, unsigned int y)
+{
+	etd->prev_x = x;
+	etd->prev_y = y;
+}
+
+/* Ensure that argument a contains a value closest to ref than b */
+static void find_closest_and_swap(unsigned int *ref, unsigned int *a, unsigned int *b)
+{
+	int dist_a = abs(*ref - *a);
+	int dist_b = abs(*ref - *b);
+	int tmp;
+	if (dist_b < dist_a) {
+		tmp = *a;
+		*a = *b;
+		*b = tmp;
+	}
+	*ref = *a;
+}
+
+/*
+ * Track the coordinates reported and swap the values to improve the likelihood
+ * that the coordinates correspond to the real positions of the two fingers.
+ */
+static void
+track_two_fingers(struct elantech_data *etd,
+		  unsigned int *x1, unsigned int *y1, unsigned int *x2, unsigned int *y2)
+{
+	/*
+	 * As the hardware can only report the coordinates without knowing which one
+	 * corresponds to which finger, we try to guess according to the last known
+	 * position, when using only one finger.
+	 */
+	find_closest_and_swap(&etd->prev_x, x1, x2);
+	find_closest_and_swap(&etd->prev_y, y1, y2);
+}
+
+
 /*
  * Interpret complete data packets and report absolute mode input events for
  * hardware version 2. (6 byte packets)
@@ -251,7 +290,7 @@  static void elantech_report_absolute_v2(struct psmouse *psmouse)
 	struct elantech_data *etd = psmouse->private;
 	struct input_dev *dev = psmouse->dev;
 	unsigned char *packet = psmouse->packet;
-	int fingers, x1, y1, x2, y2, width = 0, pres = 0;
+	unsigned int fingers, x1, y1, x2, y2, width = 0, pres = 0;
 
 	/* byte 0: n1  n0   .   .   .   .   R   L */
 	fingers = (packet[0] & 0xc0) >> 6;
@@ -285,6 +324,10 @@  static void elantech_report_absolute_v2(struct psmouse *psmouse)
 		input_report_abs(dev, ABS_X, x1);
 		input_report_abs(dev, ABS_Y, y1);
 
+		/* Only track if we are sure it's real finger coodinates */
+		if (fingers == 1)
+			track_one_finger(etd, x1, y1);
+
 		pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
 		width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
 		break;
@@ -296,37 +339,39 @@  static void elantech_report_absolute_v2(struct psmouse *psmouse)
 		 * byte 0:  .   .  ay8 ax8  .   .   .   .
 		 * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
 		 */
-		x1 = ((packet[0] & 0x10) << 4) | packet[1];
+		x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;
 		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
-		y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
+		y1 = (ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2])) << 2;
 		/*
 		 * byte 3:  .   .  by8 bx8  .   .   .   .
 		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
 		 */
-		x2 = ((packet[3] & 0x10) << 4) | packet[4];
+		x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;
 		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
-		y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
+		y2 = (ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5])) << 2;
+
+		track_two_fingers(etd, &x1, &y1, &x2, &y2);
 		/* Multitouch */
-		input_report_abs(dev, ABS_MT_POSITION_X, x1 << 2);
-		input_report_abs(dev, ABS_MT_POSITION_Y, y1 << 2);
+		input_report_abs(dev, ABS_MT_POSITION_X, x1);
+		input_report_abs(dev, ABS_MT_POSITION_Y, y1);
 		input_mt_sync(dev);
-		input_report_abs(dev, ABS_MT_POSITION_X, x2 << 2);
-		input_report_abs(dev, ABS_MT_POSITION_Y, y2 << 2);
+		input_report_abs(dev, ABS_MT_POSITION_X, x2);
+		input_report_abs(dev, ABS_MT_POSITION_Y, y2);
 		input_mt_sync(dev);
 		/*
 		 * For compatibility with the X Synaptics driver scale up
 		 * one coordinate and report as ordinary mouse movent
 		 */
-		input_report_abs(dev, ABS_X, x1 << 2);
-		input_report_abs(dev, ABS_Y, y1 << 2);
+		input_report_abs(dev, ABS_X, x1);
+		input_report_abs(dev, ABS_Y, y1);
 		/*
 		 * For compatibility with the proprietary X Elantech driver
 		 * report both coordinates as hat coordinates
 		 */
-		input_report_abs(dev, ABS_HAT0X, x1);
-		input_report_abs(dev, ABS_HAT0Y, y1);
-		input_report_abs(dev, ABS_HAT1X, x2);
-		input_report_abs(dev, ABS_HAT1Y, y2);
+		input_report_abs(dev, ABS_HAT0X, x1 >> 2);
+		input_report_abs(dev, ABS_HAT0Y, y1 >> 2);
+		input_report_abs(dev, ABS_HAT1X, x2 >> 2);
+		input_report_abs(dev, ABS_HAT1Y, y2 >> 2);
 
 		/* Unknown so just report sensible values */
 		pres = 127;
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index f6e3be5..fa9a6b4 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -111,6 +111,8 @@  struct elantech_data {
 	unsigned char hw_version;
 	unsigned int  fw_version;
 	unsigned char parity[256];
+	unsigned int prev_x;
+	unsigned int prev_y;
 };
 
 enum paritycheck_types {