All of lore.kernel.org
 help / color / mirror / Atom feed
From: djkurtz@chromium.org
To: dmitry.torokhov@gmail.com, rydberg@euromail.se,
	chase.douglas@canonical.com, rubini@cvml.unipv.it
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	derek.foreman@collabora.co.uk, daniel.stone@collabora.co.uk,
	olofj@chromium.org, Daniel Kurtz <djkurtz@chromium.org>
Subject: [PATCH 6/9 v2] Input: synaptics - process finger (<=3) transitions
Date: Wed, 20 Jul 2011 21:39:03 +0800	[thread overview]
Message-ID: <1311169146-20066-7-git-send-email-djkurtz@chromium.org> (raw)
In-Reply-To: <1311169146-20066-1-git-send-email-djkurtz@chromium.org>

From: Daniel Kurtz <djkurtz@chromium.org>

Synaptics image sensor touchpads track 5 fingers, but only report 2.

This patch attempts to deal with some idiosyncrasies of these touchpads:

 * When there are 3 or more fingers, only two are reported.
 * The number of fingers can change at any time, but is only reported in
   SGM packets, thus at a number-of-fingers change, it is not possible
   to tell whether the AGM finger is for the original or new number of
   fingers.
 * When the number of fingers changes from 2->3 it is not
   possible to tell which of the 2 fingers are now reported.
 * When number of fingers changes from 3->2 it is often not possible to
   tell which finger was removed, and which are now being reported.

When 2 or more packets are present on the touchpad, the kernel reports
exactly two MT-B slots containing the position data for the two fingers
reported by the touchpad.
In addition, it reports the total number of fingers using one of the
EV_KEY/BTN_TOOL_*TAP events.  Thus, this is a hybrid singletouch/MT-B scheme.

Userspace can detect this condition by noting that the driver supports
more EV_KEY/BTN_TOOL_*TAP events than its maximum EV_ABS/ABS_MT_SLOT.

Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
---
 drivers/input/mouse/synaptics.c |  229 ++++++++++++++++++++++++++++++++++++---
 drivers/input/mouse/synaptics.h |    3 +
 2 files changed, 216 insertions(+), 16 deletions(-)

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index ff8c839..b626b98 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -453,6 +453,9 @@ static void synaptics_parse_agm(const unsigned char buf[],
 	default:
 		break;
 	}
+
+	/* Record that at least one AGM has been received since last SGM */
+	priv->agm_pending = true;
 }
 
 static int synaptics_parse_hw_state(const unsigned char buf[],
@@ -600,26 +603,44 @@ static void synaptics_report_slot_agm(struct input_dev *dev, int slot,
 }
 
 static void synaptics_report_mt(struct psmouse *psmouse,
-				int count,
+				struct synaptics_mt_state *mt_state,
 				const struct synaptics_hw_state *sgm)
 {
 	struct input_dev *dev = psmouse->dev;
 	struct synaptics_data *priv = psmouse->private;
 	struct synaptics_hw_state *agm = &priv->agm;
+	struct synaptics_mt_state *old = &priv->mt_state;
 
-	switch (count) {
+	switch (mt_state->count) {
 	case 0:
 		synaptics_report_slot_empty(dev, 0);
 		synaptics_report_slot_empty(dev, 1);
 		break;
 	case 1:
-		synaptics_report_slot_sgm(dev, 0, sgm);
-		synaptics_report_slot_empty(dev, 1);
+		if (mt_state->sgm == 0) {
+			synaptics_report_slot_sgm(dev, 0, sgm);
+			synaptics_report_slot_empty(dev, 1);
+		} else {
+			synaptics_report_slot_empty(dev, 0);
+			synaptics_report_slot_sgm(dev, 1, sgm);
+		}
 		break;
-	case 2:
-	case 3: /* Fall-through case */
-		synaptics_report_slot_sgm(dev, 0, sgm);
-		synaptics_report_slot_agm(dev, 1, agm);
+	default:
+		/*
+		 * For all other finger counts, report sgm in 0 and agm in 1,
+		 * but only if the sgm/agm is reporting the same finger.
+		 * If either reported finger has changed, invalidate its slot's
+		 * old tracking id, instead.
+		 */
+		if ((old->sgm == -1) || (old->sgm == mt_state->sgm))
+			synaptics_report_slot_sgm(dev, 0, sgm);
+		else
+			synaptics_report_slot_empty(dev, 0);
+
+		if ((old->agm == -1) || (old->agm == mt_state->agm))
+			synaptics_report_slot_agm(dev, 1, agm);
+		else
+			synaptics_report_slot_empty(dev, 1);
 		break;
 	}
 
@@ -627,28 +648,204 @@ static void synaptics_report_mt(struct psmouse *psmouse,
 	input_mt_report_pointer_emulation(dev, false);
 
 	/* Send the number of fingers reported by touchpad itself. */
-	input_mt_report_finger_count(dev, count);
+	input_mt_report_finger_count(dev, mt_state->count);
 
 	input_report_key(dev, BTN_LEFT, sgm->left);
 	input_sync(dev);
 }
 
+/* Handle case where mt_state->count = 0 */
+static void synaptics_image_sensor_0f(struct synaptics_data *priv,
+				      struct synaptics_mt_state *mt_state)
+{
+	synaptics_mt_state_set(mt_state, 0, -1, -1);
+}
+
+/* Handle case where mt_state->count = 1 */
+static void synaptics_image_sensor_1f(struct synaptics_data *priv,
+				      struct synaptics_mt_state *mt_state)
+{
+	struct synaptics_hw_state *agm = &priv->agm;
+	struct synaptics_mt_state *old = &priv->mt_state;
+
+	/*
+	 * If the last AGM was (0,0,0), and there is only one finger left,
+	 * then SGM contains slot 0, and all other fingers have been removed.
+	 */
+	if (priv->agm_pending && agm->z == 0) {
+		synaptics_mt_state_set(mt_state, 1, 0, -1);
+		return;
+	}
+
+	switch (old->count) {
+	case 0:
+		synaptics_mt_state_set(mt_state, 1, 0, -1);
+		break;
+	case 1:
+		/*
+		 * If pending AGM and either:
+		 *   (a) the previous SGM slot contains slot 0, or
+		 *   (b) there was no SGM slot
+		 * then SGM now contains slot 1
+		 *
+		 *  (a) The "SGM contains slot 0" case happens with very rapid
+		 * "drum roll" gestures, where slot 0 finger is lifted and a
+		 * new slot 1 finger touches within one reporting interval.
+		 *
+		 *  (b) The "no SGM slot" case happens if initially two or more
+		 * fingers tap briefly, and all but one lift before the end of
+		 * the first reporting interval.
+		 *
+		 * (In both these cases, slot 0 will become empty, and SGM
+		 * contains a new finger in slot 1)
+		 *
+		 * Else, if there was no previous SGM, it now contains slot 0.
+		 *
+		 * Otherwise, SGM still contains the same slot.
+		 */
+
+		if (priv->agm_pending && old->sgm <= 0)
+			synaptics_mt_state_set(mt_state, 1, 1, -1);
+		else if (old->sgm == -1)
+			synaptics_mt_state_set(mt_state, 1, 0, -1);
+		break;
+	case 2:
+		/*
+		 * Since the last AGM was NOT (0,0,0), it was the finger in
+		 * slot 0 that has been removed.
+		 * So, SGM now contains previous AGM's slot, and AGM is now
+		 * empty.
+		 */
+		synaptics_mt_state_set(mt_state, 1, old->agm, -1);
+		break;
+	case 3:
+		/*
+		 * Since last AGM was not (0,0,0), we don't know which finger
+		 * is left.
+		 *
+		 * So, empty all slots. We will guess slot 0 on subsequent 1->1
+		 */
+		synaptics_mt_state_set(mt_state, 0, -1, -1);
+		break;
+	}
+}
+
+/* Handle case where mt_state->count = 2 */
+static void synaptics_image_sensor_2f(struct synaptics_data *priv,
+				      struct synaptics_mt_state *mt_state)
+{
+	struct synaptics_mt_state *old = &priv->mt_state;
+
+	switch (old->count) {
+	case 0:
+		synaptics_mt_state_set(mt_state, 2, 0, 1);
+		break;
+	case 1:
+		/*
+		 * If previous SGM contained slot 1 or higher, SGM now contains
+		 * slot 0 (the newly touching finger) and AGM contains SGM's
+		 * previous slot.
+		 *
+		 * Otherwise, SGM still contains slot 0 and AGM now contains
+		 * slot 1.
+		 */
+		if (old->sgm >= 1)
+			synaptics_mt_state_set(mt_state, 2, 0, old->sgm);
+		else
+			synaptics_mt_state_set(mt_state, 2, 0, 1);
+		break;
+	case 2:
+		/*
+		 * mt_state either hasn't changed, or was updated by a recently
+		 * received AGM-CONTACT packet.
+		 */
+		break;
+	case 3:
+		/*
+		 * 3->2 transitions have two unsolvable problems:
+		 *  1) no indication is given which finger was removed
+		 *  2) no way to tell if agm packet was for finger 3
+		 *     before 3->2, or finger 2 after 3->2.
+		 *
+		 * So, empty all slots. We will guess slots [0,1] on
+		 * subsequent 2->2
+		 */
+		synaptics_mt_state_set(mt_state, 0, -1, -1);
+		break;
+	}
+}
+
+/* Handle case where mt_state->count = 3 */
+static void synaptics_image_sensor_3f(struct synaptics_data *priv,
+				      struct synaptics_mt_state *mt_state)
+{
+	struct synaptics_mt_state *old = &priv->mt_state;
+
+	switch (old->count) {
+	case 0:
+		synaptics_mt_state_set(mt_state, 3, 0, 2);
+		break;
+	case 1:
+		/*
+		 * If previous SGM contained slot 2 or higher, SGM now contains
+		 * slot 0 (one of the newly touching fingers) and AGM contains
+		 * SGM's previous slot.
+		 *
+		 * Otherwise, SGM now contains slot 0 and AGM contains slot 2.
+		 */
+		if (old->sgm >= 2)
+			synaptics_mt_state_set(mt_state, 3, 0, old->sgm);
+		else
+			synaptics_mt_state_set(mt_state, 3, 0, 2);
+		break;
+	case 2:
+		/*
+		 * On 2->3 transitions, we are given no indication which finger
+		 * was added.
+		 * We don't even know what finger the current AGM packet
+		 * contained.
+		 *
+		 * So, empty all slots. They get filled on a subsequent 3->3
+		 */
+		synaptics_mt_state_set(mt_state, 0, -1, -1);
+		break;
+	case 3:
+		/*
+		 * mt_state either hasn't changed, or was updated by a recently
+		 * received AGM-CONTACT packet.
+		 */
+		break;
+	}
+}
+
 static void synaptics_image_sensor_process(struct psmouse *psmouse,
 					   struct synaptics_hw_state *sgm)
 {
-	int count;
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_hw_state *agm = &priv->agm;
+	struct synaptics_mt_state mt_state;
+
+	/* Initialize using current mt_state (as updated by last agm) */
+	mt_state = agm->mt_state;
 
+	/*
+	 * Update mt_state using the new finger count and current mt_state.
+	 */
 	if (sgm->z == 0)
-		count = 0;
+		synaptics_image_sensor_0f(priv, &mt_state);
 	else if (sgm->w >= 4)
-		count = 1;
+		synaptics_image_sensor_1f(priv, &mt_state);
 	else if (sgm->w == 0)
-		count = 2;
-	else
-		count = 3;
+		synaptics_image_sensor_2f(priv, &mt_state);
+	else if (sgm->w == 1)
+		synaptics_image_sensor_3f(priv, &mt_state);
 
 	/* Send resulting input events to user space */
-	synaptics_report_mt(psmouse, count, sgm);
+	synaptics_report_mt(psmouse, &mt_state, sgm);
+
+	/* Store updated mt_state */
+	priv->mt_state = agm->mt_state = mt_state;
+	priv->agm_pending = false;
 }
 
 /*
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 0c63357..87be1fe 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -161,11 +161,14 @@ struct synaptics_data {
 
 	struct serio *pt_port;			/* Pass-through serio port */
 
+	struct synaptics_mt_state mt_state;	/* Current mt finger state */
+
 	/*
 	 * Last received Advanced Gesture Mode (AGM) packet. An AGM packet
 	 * contains position data for a second contact, at half resolution.
 	 */
 	struct synaptics_hw_state agm;
+	bool agm_pending;			/* new AGM packet received */
 };
 
 void synaptics_module_init(void);
-- 
1.7.3.1


  parent reply	other threads:[~2011-07-20 13:39 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-20 13:38 [PATCH 0/9 v2] Synaptics image sensor support djkurtz
2011-07-20 13:38 ` [PATCH 1/9 v2] Input: synaptics - refactor y inversion djkurtz
2011-07-23  0:30   ` Chase Douglas
2011-07-25  8:27   ` Dmitry Torokhov
2011-07-26  2:19     ` Daniel Kurtz
2011-07-26 22:59     ` Chase Douglas
2011-07-20 13:38 ` [PATCH 2/9 v2] Input: synaptics - refactor agm packet parsing djkurtz
2011-07-23  0:32   ` Chase Douglas
2011-07-20 13:39 ` [PATCH 3/9 v2] Input: synaptics - refactor initialization of abs position axes djkurtz
2011-07-23  0:36   ` Chase Douglas
2011-07-20 13:39 ` [PATCH 4/9 v2] Input: synaptics - add image sensor support djkurtz
2011-07-20 13:39 ` [PATCH 5/9 v2] Input: synaptics - decode AGM packet types djkurtz
2011-07-20 13:39 ` djkurtz [this message]
2011-07-23  1:11   ` [PATCH 6/9 v2] Input: synaptics - process finger (<=3) transitions Chase Douglas
2011-07-20 13:39 ` [PATCH 7/9 v2] Input: synaptics - improved 2->3 finger transition handling djkurtz
2011-07-23  1:07   ` Chase Douglas
2011-07-23  4:36     ` Daniel Kurtz
2011-07-23  4:36       ` Daniel Kurtz
2011-07-26 23:14       ` Chase Douglas
2011-07-27  4:48         ` Daniel Kurtz
2011-07-27  4:48           ` Daniel Kurtz
2011-07-27 21:13           ` Chase Douglas
2011-07-28  1:00             ` Daniel Kurtz
2011-07-28  2:07               ` Chase Douglas
2011-07-28 13:56                 ` Daniel Kurtz
2011-07-28 13:56                   ` Daniel Kurtz
2011-07-28 17:31                   ` Chase Douglas
2011-07-20 13:39 ` [PATCH 8/9 v2] Input: add BTN_TOOL_QUINTTAP for reporting 5 fingers on touchpad djkurtz
2011-07-23  0:59   ` Chase Douglas
2011-07-25  8:29   ` Dmitry Torokhov
2011-07-25  9:14     ` Daniel Kurtz
2011-07-25 18:16       ` Dmitry Torokhov
2011-07-26  2:18         ` Daniel Kurtz
2011-08-11 20:07           ` Henrik Rydberg
2011-08-11 20:07             ` Henrik Rydberg
2011-07-26 23:03         ` Chase Douglas
2011-07-20 13:39 ` [PATCH 9/9 v2] Input: synaptics - process finger (<=5) transitions djkurtz
2011-07-23  1:02   ` Chase Douglas
2011-07-23  4:11     ` Daniel Kurtz
2011-07-26 23:17       ` Chase Douglas
2011-07-23  1:13 ` [PATCH 0/9 v2] Synaptics image sensor support Chase Douglas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1311169146-20066-7-git-send-email-djkurtz@chromium.org \
    --to=djkurtz@chromium.org \
    --cc=chase.douglas@canonical.com \
    --cc=daniel.stone@collabora.co.uk \
    --cc=derek.foreman@collabora.co.uk \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=olofj@chromium.org \
    --cc=rubini@cvml.unipv.it \
    --cc=rydberg@euromail.se \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.