@@ -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;
@@ -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 {