From: Daniel Kurtz <[email protected]>
Hello,
This patch set (against next) is intended to add support for synaptics
"image sensor" touchpads.
Patches 1-8 clean up the current driver slightly and prepare for the image
sensor patches which follow.
Patches 9-11 add up to 3 finger support for image sensor touchpads.
Image sensors do not suffer from the finger tracking issues that plagued
the earlier "profile sensors", and which required the invention of "semi-mt"
(which reports a bounding box around two fingers instead of the fingers
themselves). Instead, the image sensors report the actual positions of two
fingers using the same "Advanced Gesture Mode". Unfortunately, the limitations
of the Synaptics PS/2 protocol make it difficult to keep track of which two
fingers are being reported at any given time.
Patch 12 adds up to 5 finger support.
In fact, the image sensor touchpads actually track 5 fingers while reporting
just 2 finger positions. This patch tracks which fingers are being reported,
and passes them up to userspace using 5 MT-B slots.
These patches are similar to, and inspired by, a similar patchset recently
submitted by Derek Foreman and Daniel Stone. However, it is not directly built
upon, nor is it compatible with, those patches.
Thanks,
Daniel
Daniel Kurtz (12):
Input: synaptics - cleanup 0x0c query documentation
Input: synaptics - do not invert y if 0
Input: synaptics - fix minimum reported ABS_TOOL_WIDTH
Input: synaptics - set resolution for MT_POSITION_X/Y axes
Input: synaptics - process button bits in AGM packets
Input: synaptics - fuzz position if touchpad reports reduced
filtering
Input: synaptics - rename synaptics_data.mt to agm
Input: synaptics - rename set_slot to be more descriptive
Input: synaptics - add image sensor support
Input: synaptics - decode AGM packet types
Input: synaptics - process finger (<=3) transitions
Input: synaptics - process finger (<=5) transitions
drivers/input/mouse/synaptics.c | 442 +++++++++++++++++++++++++++++++++++----
drivers/input/mouse/synaptics.h | 41 +++-
include/linux/input.h | 1 +
3 files changed, 440 insertions(+), 44 deletions(-)
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Convert some spaces to tabs.
Reorder defines to match bit ordering.
Add defines for all bits.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.h | 16 +++++++++++-----
1 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 7453938..34bedde 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -66,18 +66,24 @@
* 1 0x60 multifinger mode identifies firmware finger counting
* (not reporting!) algorithm.
* Not particularly meaningful
- * 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered
- * 2 0x01 clickpad bit 1 2-button ClickPad
- * 2 0x02 deluxe LED controls touchpad support LED commands
+ * 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered
+ * 2 0x01 clickpad bit 1 2-button ClickPad
+ * 2 0x02 deluxe LED controls touchpad support LED commands
* ala multimedia control bar
* 2 0x04 reduced filtering firmware does less filtering on
* position data, driver should watch
* for noise.
*/
-#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
-#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
+#define SYN_CAP_ADJ_THRESHOLD(ex0c) ((ex0c) & 0x010000)
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
+#define SYN_CAP_CLEARPAD(ex0c) ((ex0c) & 0x040000)
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
+#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
+#define SYN_CAP_MULTIFINGER_MODE(ex0c) ((ex0c) & 0x600000)
+#define SYN_CAP_COVERED_PAD(ex0c) ((ex0c) & 0x800000)
+#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
+#define SYN_CAP_DELUXE_LED(ex0c) ((ex0c) & 0x000200)
+#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Synaptics touchpads report increasing y from bottom to top.
This is inverted from normal userspace "top of screen is 0" coordinates.
Thus, the kernel driver reports inverted y coordinates to userspace.
In some cases, however, y = 0 is sent by the touchpad.
In these cases, the kernel driver should not invert, and just report 0.
This patch also refactors the inversion into a macro, and moves it
into packet processing instead of during position reporting.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 16 +++++++++-------
1 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index e06e045..f6d0c04 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -44,6 +44,8 @@
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
+#define INVERT_Y(y) (((y) != 0) ? (YMAX_NOMINAL + YMIN_NOMINAL - (y)) : 0)
+
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
@@ -409,9 +411,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->x = (((buf[3] & 0x10) << 8) |
((buf[1] & 0x0f) << 8) |
buf[4]);
- hw->y = (((buf[3] & 0x20) << 7) |
+ hw->y = INVERT_Y((((buf[3] & 0x20) << 7) |
((buf[1] & 0xf0) << 4) |
- buf[5]);
+ buf[5]));
hw->z = buf[2];
hw->w = (((buf[0] & 0x30) >> 2) |
@@ -421,7 +423,8 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
/* Gesture packet: (x, y, z) at half resolution */
priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
- priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+ priv->mt.y = INVERT_Y((((buf[4] & 0xf0) << 4)
+ | buf[2]) << 1);
priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
return 1;
}
@@ -473,7 +476,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
}
} else {
hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
- hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
+ hw->y = INVERT_Y(((buf[4] & 0x1f) << 8) | buf[5]);
hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
@@ -491,8 +494,7 @@ static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
if (active) {
input_report_abs(dev, ABS_MT_POSITION_X, x);
- input_report_abs(dev, ABS_MT_POSITION_Y,
- YMAX_NOMINAL + YMIN_NOMINAL - y);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
}
}
@@ -584,7 +586,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (num_fingers > 0) {
input_report_abs(dev, ABS_X, hw.x);
- input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+ input_report_abs(dev, ABS_Y, hw.y);
}
input_report_abs(dev, ABS_PRESSURE, hw.z);
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Synaptics touchpads report a 'w' value in each data report.
For touchpads that support palm detection, when there is a single finger
on the pad, the 'w' value reports its width in the range 4 to 15.
Thus, the minimum valid width is 4.
Note: Other values of 'w' are used to report special conditions:
w=0: 2 fingers are on the pad
w=1: 3 or more fingers are on the pad
w=2: the packet contains "Advanced Gesture Mode" data.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index f6d0c04..a4b7801 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -706,7 +706,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
}
if (SYN_CAP_PALMDETECT(priv->capabilities))
- input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+ input_set_abs_params(dev, ABS_TOOL_WIDTH, 4, 15, 0, 0);
__set_bit(EV_KEY, dev->evbit);
__set_bit(BTN_TOUCH, dev->keybit);
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Set resolution for MT_POSITION_X and MT_POSITION_Y to match ABS_X and
ABS_Y, respectively.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a4b7801..1ce47b7 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -703,6 +703,9 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
priv->x_max ?: XMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
priv->y_max ?: YMAX_NOMINAL, 0, 0);
+
+ input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
+ input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
}
if (SYN_CAP_PALMDETECT(priv->capabilities))
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
AGM packets contain valid button bits, too.
This patch refactors packet processing to parse button bits in AGM packets.
However, they aren't actually used or reported.
The point is to more completely process AGM packets,
and prepare for future patches that may actually use AGM packet button bits.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 35 ++++++++++++++++++-----------------
1 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 1ce47b7..74b1222 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -408,27 +408,10 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
memset(hw, 0, sizeof(struct synaptics_hw_state));
if (SYN_MODEL_NEWABS(priv->model_id)) {
- hw->x = (((buf[3] & 0x10) << 8) |
- ((buf[1] & 0x0f) << 8) |
- buf[4]);
- hw->y = INVERT_Y((((buf[3] & 0x20) << 7) |
- ((buf[1] & 0xf0) << 4) |
- buf[5]));
-
- hw->z = buf[2];
hw->w = (((buf[0] & 0x30) >> 2) |
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
- /* Gesture packet: (x, y, z) at half resolution */
- priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
- priv->mt.y = INVERT_Y((((buf[4] & 0xf0) << 4)
- | buf[2]) << 1);
- priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
- return 1;
- }
-
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
@@ -451,6 +434,24 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
+ if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
+ /* Gesture packet: (x, y, z) at half resolution */
+ priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+ priv->mt.y = INVERT_Y((((buf[4] & 0xf0) << 4)
+ | buf[2]) << 1);
+ priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+ return 1;
+ } else {
+ hw->x = (((buf[3] & 0x10) << 8) |
+ ((buf[1] & 0x0f) << 8) |
+ buf[4]);
+ hw->y = INVERT_Y((((buf[3] & 0x20) << 7) |
+ ((buf[1] & 0xf0) << 4) |
+ buf[5]));
+
+ hw->z = buf[2];
+ }
+
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
((buf[0] ^ buf[3]) & 0x02)) {
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Synaptics touchpads indicate via a capability bit when they perform
reduced filtering on position data.
In such a case, use a non-zero fuzz value.
Fuzz = 8 was chosen empirically by observing the raw position data
reported by a clickpad indicating it had reduced filtering.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 17 ++++++++++-------
drivers/input/mouse/synaptics.h | 3 +++
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 74b1222..dc54675 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -687,23 +687,27 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
int i;
+ int fuzz = 0;
__set_bit(INPUT_PROP_POINTER, dev->propbit);
+ if (SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c))
+ fuzz = SYN_REDUCED_FILTER_FUZZ;
+
__set_bit(EV_ABS, dev->evbit);
- input_set_abs_params(dev, ABS_X,
- XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
- input_set_abs_params(dev, ABS_Y,
- YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
+ input_set_abs_params(dev, ABS_X, XMIN_NOMINAL,
+ priv->x_max ?: XMAX_NOMINAL, fuzz, 0);
+ input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL,
+ priv->y_max ?: YMAX_NOMINAL, fuzz, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
- priv->x_max ?: XMAX_NOMINAL, 0, 0);
+ priv->x_max ?: XMAX_NOMINAL, fuzz, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
- priv->y_max ?: YMAX_NOMINAL, 0, 0);
+ priv->y_max ?: YMAX_NOMINAL, fuzz, 0);
input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
@@ -977,4 +981,3 @@ bool synaptics_supported(void)
}
#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
-
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 34bedde..8a68e66 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -110,6 +110,9 @@
#define SYN_NEWABS_RELAXED 2
#define SYN_OLDABS 3
+/* amount to fuzz position data when touchpad reports reduced filtering */
+#define SYN_REDUCED_FILTER_FUZZ 8
+
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Rename the synaptics_data.mt field to priv->agm to indicate it is the
hardware state reported in a synaptics AGM packet.
When a Synaptics touchpad is in "AGM" mode, and multiple fingers are
detected, the touchpad sends alternating "Advanced Gesture Mode" (AGM) and
"Simple Gesture Mode" (SGM) packets.
The AGM packets have w=2, and contain reduced resolution finger data.
The SGM packets have w={0,1} and contain full resolution finger data.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 9 +++++----
drivers/input/mouse/synaptics.h | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index dc54675..4e5e454 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -436,10 +436,10 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
/* Gesture packet: (x, y, z) at half resolution */
- priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
- priv->mt.y = INVERT_Y((((buf[4] & 0xf0) << 4)
+ priv->agm.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+ priv->agm.y = INVERT_Y((((buf[4] & 0xf0) << 4)
| buf[2]) << 1);
- priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+ priv->agm.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
return 1;
} else {
hw->x = (((buf[3] & 0x10) << 8) |
@@ -576,7 +576,8 @@ static void synaptics_process_packet(struct psmouse *psmouse)
}
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
- synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
+ synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
+ num_fingers);
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 8a68e66..e367239 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -147,7 +147,7 @@ struct synaptics_data {
struct serio *pt_port; /* Pass-through serio port */
- struct synaptics_hw_state mt; /* current gesture packet */
+ struct synaptics_hw_state agm; /* last AGM packet */
};
void synaptics_module_init(void);
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 17 ++++++++++-------
1 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 4e5e454..0a51b0ba 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -489,7 +489,8 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
return 0;
}
-static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
+static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot,
+ bool active, int x, int y)
{
input_mt_slot(dev, slot);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
@@ -505,14 +506,16 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev,
int num_fingers)
{
if (num_fingers >= 2) {
- set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y));
- set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y));
+ synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x),
+ min(a->y, b->y));
+ synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x),
+ max(a->y, b->y));
} else if (num_fingers == 1) {
- set_slot(dev, 0, true, a->x, a->y);
- set_slot(dev, 1, false, 0, 0);
+ synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y);
+ synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
} else {
- set_slot(dev, 0, false, 0, 0);
- set_slot(dev, 1, false, 0, 0);
+ synaptics_report_semi_mt_slot(dev, 0, false, 0, 0);
+ synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
}
}
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Synaptics makes (at least) two kinds of touchpad sensors:
* Older pads use a profile sensor that could only infer the location
of individual fingers based on the projection of their profiles
onto row and column sensors.
* Newer pads use an image sensor that can track true finger position
using a two-dimensional sensor grid.
Both sensor types support an "Advanced Gesture Mode":
When multiple fingers are detected, the touchpad sends alternating
"Advanced Gesture Mode" (AGM) and "Simple Gesture Mode" (SGM)
packets.
The AGM packets have w=2, and contain reduced resolution finger data
The SGM packets have w={0,1} and contain full resolution finger data
Profile sensors try to report the "upper" (larger y value) finger in
the SGM packet, and the lower (smaller y value) in the AGM packet.
However, due to the nature of the profile sensor, they easily get
confused when fingers cross, and can start reporting the x-coordinate
of one with the y-coordinate of the other. Thus, for profile
sensors, "semi-mt" was created, which reports a "bounding box"
created by pairing min and max coordinates of the two pairs of
reported fingers.
Image sensors can report the actual coordinates of two of the fingers
present. This patch detects if the touchpad is an image sensor and
reports finger data using the MT-B protocol.
NOTE: This patch only adds partial support for 2-finger gestures.
The proper interpretation of the slot contents when more than
two fingers are present is left to later patches. Also,
handling of 'number of fingers' transitions is incomplete.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 116 ++++++++++++++++++++++++++++++++++++++-
drivers/input/mouse/synaptics.h | 13 ++++
2 files changed, 126 insertions(+), 3 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 0a51b0ba..2d7ac0a 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -286,7 +286,8 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static unsigned char param = 0xc8;
struct synaptics_data *priv = psmouse->private;
- if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
return 0;
if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
@@ -434,7 +435,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
+ if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)
+ || SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))
+ && hw->w == 2) {
/* Gesture packet: (x, y, z) at half resolution */
priv->agm.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
priv->agm.y = INVERT_Y((((buf[4] & 0xf0) << 4)
@@ -519,6 +522,92 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev,
}
}
+static void synaptics_report_mt_slot(struct input_dev *dev, int slot, int state,
+ const struct synaptics_hw_state *sgm,
+ const struct synaptics_hw_state *agm)
+{
+ input_mt_slot(dev, slot);
+ switch (state) {
+ case SYN_SLOT_EMPTY:
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
+ break;
+ case SYN_SLOT_SGM:
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, sgm->x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, sgm->y);
+ input_report_abs(dev, ABS_MT_PRESSURE, sgm->z);
+ if (sgm->w >= 4)
+ input_report_abs(dev, ABS_MT_TOUCH_MAJOR, sgm->w);
+ break;
+ case SYN_SLOT_AGM:
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, agm->x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, agm->y);
+ input_report_abs(dev, ABS_MT_PRESSURE, agm->z);
+ break;
+ }
+}
+
+static void synaptics_update_slots(struct synaptics_data *priv, int count,
+ int sgm, int agm)
+{
+ int i;
+
+ /* First, clear previous slots. */
+ for (i = 0; i < SYN_TRACK_SLOT_COUNT; i++)
+ priv->slot[i] = SYN_SLOT_EMPTY;
+
+ if (count < 1)
+ return;
+ priv->slot[sgm] = SYN_SLOT_SGM;
+
+ if (count < 2)
+ return;
+ priv->slot[agm] = SYN_SLOT_AGM;
+}
+
+static void synaptics_process_hw_state(struct synaptics_data *priv,
+ struct synaptics_hw_state *sgm)
+{
+ int new_num_fingers;
+
+ if (sgm->z == 0)
+ new_num_fingers = 0;
+ else if (sgm->w >= 4)
+ new_num_fingers = 1;
+ else if (sgm->w == 0)
+ new_num_fingers = 2;
+ else if (sgm->w == 1)
+ new_num_fingers = 3;
+
+ switch (new_num_fingers) {
+ case 0:
+ synaptics_update_slots(priv, 0, 0, 0);
+ break;
+ case 1:
+ synaptics_update_slots(priv, 1, 0, 0);
+ break;
+ case 2:
+ case 3: /* Fall-through case */
+ synaptics_update_slots(priv, 2, 0, 1);
+ break;
+ }
+
+ priv->num_fingers = new_num_fingers;
+}
+
+static void synaptics_report_mt_data(struct psmouse *psmouse,
+ struct synaptics_hw_state *sgm)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state *agm = &priv->agm;
+ int i;
+
+ for (i = 0; i < SYN_TRACK_SLOT_COUNT; i++)
+ synaptics_report_mt_slot(dev, i, priv->slot[i], sgm, agm);
+}
+
/*
* called for each full received packet from the touchpad
*/
@@ -534,6 +623,15 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
return;
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ synaptics_process_hw_state(priv, &hw);
+ synaptics_report_mt_data(psmouse, &hw);
+ input_mt_report_pointer_emulation(dev, true);
+ input_report_key(dev, BTN_LEFT, hw.left);
+ input_sync(dev);
+ return;
+ }
+
if (hw.scroll) {
priv->scroll += hw.scroll;
@@ -705,7 +803,19 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
priv->y_max ?: YMAX_NOMINAL, fuzz, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ input_mt_init_slots(dev, SYN_TRACK_SLOT_COUNT);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
+ priv->x_max ?: XMAX_NOMINAL, fuzz, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
+ priv->y_max ?: YMAX_NOMINAL, fuzz, 0);
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 4, 15, 0, 0);
+
+ input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
+ input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
+
+ } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index e367239..1de2256 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -73,6 +73,8 @@
* 2 0x04 reduced filtering firmware does less filtering on
* position data, driver should watch
* for noise.
+ * 2 0x08 image sensor image sensor tracks 5 fingers, but only
+ * reports 2.
*/
#define SYN_CAP_ADJ_THRESHOLD(ex0c) ((ex0c) & 0x010000)
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
@@ -84,6 +86,7 @@
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
#define SYN_CAP_DELUXE_LED(ex0c) ((ex0c) & 0x000200)
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
+#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
@@ -113,6 +116,14 @@
/* amount to fuzz position data when touchpad reports reduced filtering */
#define SYN_REDUCED_FILTER_FUZZ 8
+/* synaptics tracking slot states */
+#define SYN_SLOT_EMPTY 0
+#define SYN_SLOT_SGM 1
+#define SYN_SLOT_AGM 2
+
+/* number of tracking slots for Image Sensor firmware */
+#define SYN_TRACK_SLOT_COUNT 2
+
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
@@ -148,6 +159,8 @@ struct synaptics_data {
struct serio *pt_port; /* Pass-through serio port */
struct synaptics_hw_state agm; /* last AGM packet */
+ int num_fingers; /* current finger count */
+ int slot[SYN_TRACK_SLOT_COUNT]; /* finger slot state */
};
void synaptics_module_init(void);
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
A Synaptics image sensor tracks 5 fingers, but can only report 2.
This behavior is called "T5R2" = Track 5 Report 2
Algorithm for choosing which 2 fingers to report in which packet:
Touchpad maintains 5 slots, numbered 0 to 4.
Initially all slots are empty.
As new fingers are detected, they are assigned the lowest available
slot.
Touchpad always reports:
SGM: lowest numbered non-empty slot
AGM: highest numbered non-empty slot, if there is one.
In addition, T5R2 touchpads have a special AGM packet type which reports
the number of fingers currently being tracked, and which finger is in
each of the two slots. Unfortunately, these "TYPE=2" packets are only used
when more than 3 fingers are being tracked. When less than 4 fingers
are present, the 'w' value must be used to track how many fingers are
present, and knowing which fingers are being reported is much more
difficult, if not impossible.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 39 ++++++++++++++++++++++++++++++++++-----
drivers/input/mouse/synaptics.h | 7 ++++++-
include/linux/input.h | 1 +
3 files changed, 41 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 2d7ac0a..19a9b7f 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -401,6 +401,14 @@ static void synaptics_pt_create(struct psmouse *psmouse)
/*****************************************************************************
* Functions to interpret the absolute mode packets
****************************************************************************/
+/* Set AGM-CONTACT finger state */
+static void synaptics_agm_finger_update(struct synaptics_data *priv, int count,
+ int sgm, int agm)
+{
+ priv->agm.finger_count = count;
+ priv->agm.finger_sgm = sgm;
+ priv->agm.finger_agm = agm;
+}
static int synaptics_parse_hw_state(const unsigned char buf[],
struct synaptics_data *priv,
@@ -438,11 +446,31 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)
|| SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))
&& hw->w == 2) {
- /* Gesture packet: (x, y, z) at half resolution */
- priv->agm.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
- priv->agm.y = INVERT_Y((((buf[4] & 0xf0) << 4)
- | buf[2]) << 1);
- priv->agm.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+ int type; /* Packet type */
+
+ type = (buf[5] & 0x30) >> 4;
+
+ switch (type) {
+ case 1:
+ /* Gesture packet: (x, y, z) half resolution */
+ priv->agm.w = hw->w;
+ priv->agm.x = (((buf[4] & 0x0f) << 8)
+ | buf[1]) << 1;
+ priv->agm.y = INVERT_Y((((buf[4] & 0xf0) << 4)
+ | buf[2]) << 1);
+ priv->agm.z = ((buf[3] & 0x30)
+ | (buf[5] & 0x0f)) << 1;
+ break;
+
+ case 2:
+ /* Finger slot update */
+ synaptics_agm_finger_update(priv, buf[1],
+ buf[2], buf[4]);
+ break;
+
+ default:
+ break;
+ }
return 1;
} else {
hw->x = (((buf[3] & 0x10) << 8) |
@@ -804,6 +832,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ __set_bit(INPUT_PROP_SYNAPTICS_T5R2, dev->propbit);
input_mt_init_slots(dev, SYN_TRACK_SLOT_COUNT);
input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
priv->x_max ?: XMAX_NOMINAL, fuzz, 0);
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 1de2256..2214af6 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -122,7 +122,7 @@
#define SYN_SLOT_AGM 2
/* number of tracking slots for Image Sensor firmware */
-#define SYN_TRACK_SLOT_COUNT 2
+#define SYN_TRACK_SLOT_COUNT 5
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
@@ -140,6 +140,11 @@ struct synaptics_hw_state {
unsigned int down:1;
unsigned char ext_buttons;
signed char scroll;
+
+ /* Reported in AGM-CONTACT packets */
+ unsigned int finger_count; /* num fingers being tracked */
+ unsigned int finger_sgm; /* finger described by sgm */
+ unsigned int finger_agm; /* finger described by agm */
};
struct synaptics_data {
diff --git a/include/linux/input.h b/include/linux/input.h
index 771d6d8..732c14e 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -137,6 +137,7 @@ struct input_keymap_entry {
#define INPUT_PROP_DIRECT 0x01 /* direct input devices */
#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */
#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */
+#define INPUT_PROP_SYNAPTICS_T5R2 0x04 /* synaptics track 5 report 2 */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
Synaptics T5R2 touchpads track 5 fingers, but only report 2.
This patch attempts to deal with some idiosyncrasies of the T5R2
protocol:
* When there are 3 fingers, one finger is 'hidden', meaning its position is
never sent to the host.
* 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.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 197 +++++++++++++++++++++++++++++++++++++-
drivers/input/mouse/synaptics.h | 2 +
2 files changed, 193 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 19a9b7f..8b38e08 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -448,6 +448,8 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
&& hw->w == 2) {
int type; /* Packet type */
+ priv->agm_pending = true;
+
type = (buf[5] & 0x30) >> 4;
switch (type) {
@@ -573,13 +575,20 @@ static void synaptics_report_mt_slot(struct input_dev *dev, int slot, int state,
input_report_abs(dev, ABS_MT_POSITION_Y, agm->y);
input_report_abs(dev, ABS_MT_PRESSURE, agm->z);
break;
+ case SYN_SLOT_HIDDEN:
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ break;
}
}
+/*
+ * Invariant: hidden_count = (count - 2); hidden_count < (agm - sgm)
+ */
static void synaptics_update_slots(struct synaptics_data *priv, int count,
int sgm, int agm)
{
int i;
+ int hidden_count;
/* First, clear previous slots. */
for (i = 0; i < SYN_TRACK_SLOT_COUNT; i++)
@@ -592,12 +601,45 @@ static void synaptics_update_slots(struct synaptics_data *priv, int count,
if (count < 2)
return;
priv->slot[agm] = SYN_SLOT_AGM;
+
+ /* Assign hidden slots between sgm and agm */
+ hidden_count = count - 2;
+ if (hidden_count < 1)
+ return;
+ if (hidden_count >= (agm-sgm))
+ hidden_count = agm-sgm-1;
+ for (i = 0; i < hidden_count; i++)
+ priv->slot[sgm + 1 + i] = SYN_SLOT_HIDDEN;
+}
+
+static int synaptics_find_sgm(struct synaptics_data *priv)
+{
+ int i;
+
+ for (i = 0; i < SYN_TRACK_SLOT_COUNT; i++)
+ if (priv->slot[i] == SYN_SLOT_SGM)
+ return i;
+ return -ENOENT;
+}
+
+static int synaptics_find_agm(struct synaptics_data *priv)
+{
+ int i;
+
+ for (i = 1; i < SYN_TRACK_SLOT_COUNT; i++)
+ if (priv->slot[i] == SYN_SLOT_AGM)
+ return i;
+ return -ENOENT;
}
static void synaptics_process_hw_state(struct synaptics_data *priv,
struct synaptics_hw_state *sgm)
{
+ struct synaptics_hw_state *agm = &priv->agm;
+ int slot_sgm;
+ int slot_agm;
int new_num_fingers;
+ int old_num_fingers = priv->num_fingers;
if (sgm->z == 0)
new_num_fingers = 0;
@@ -608,20 +650,163 @@ static void synaptics_process_hw_state(struct synaptics_data *priv,
else if (sgm->w == 1)
new_num_fingers = 3;
- switch (new_num_fingers) {
- case 0:
+ if (new_num_fingers == 0) {
synaptics_update_slots(priv, 0, 0, 0);
- break;
- case 1:
+ goto process_mt_data_done;
+ }
+
+ /*
+ * If the last agm was (0,0,0), then SGM is in slot[0], and all other
+ * fingers have been removed.
+ */
+ if (priv->agm_pending && agm->z == 0) {
synaptics_update_slots(priv, 1, 0, 0);
+ goto process_mt_data_done;
+ }
+
+ /*
+ * Update slots to in response to number of fingers changing from
+ * old_num_fingers to new_num_fingers.
+ */
+ switch (new_num_fingers) {
+ case 1:
+ switch (old_num_fingers) {
+ case 0:
+ synaptics_update_slots(priv, 1, 0, 0);
+ break;
+ case 1:
+ /*
+ * If received AGM and previous SGM slot was 0, or
+ * there was no SGM slot, then switch SGM slot to 1.
+ *
+ * The "SGM 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.
+ * 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 these cases, slot 0 becomes empty, only
+ * slot 1, is reported with the SGM)
+ *
+ * Else if there was no previous SGM, use slot 0.
+ *
+ * Else, reuse the current SGM slot.
+ */
+
+ /* Determine previous SGM slot, if there was one. */
+ slot_sgm = synaptics_find_sgm(priv);
+
+ if (priv->agm_pending && slot_sgm <= 0)
+ slot_sgm = 1;
+ else if (slot_sgm < 0)
+ slot_sgm = 0;
+
+ synaptics_update_slots(priv, 1, slot_sgm, 0);
+ break;
+ case 2:
+ /*
+ * Since last AGM was not (0,0,0),
+ * previous AGM is now the SGM.
+ */
+ slot_agm = synaptics_find_agm(priv);
+ synaptics_update_slots(priv, 1, slot_agm, 0);
+ break;
+ case 3:
+ /*
+ * Since last AGM was not (0,0,0), we don't know what
+ * finger is left. So empty all slots.
+ * The slot gets filled on a subsequent 1->1
+ */
+ synaptics_update_slots(priv, 0, 0, 0);
+ break;
+ }
break;
+
case 2:
- case 3: /* Fall-through case */
- synaptics_update_slots(priv, 2, 0, 1);
+ switch (old_num_fingers) {
+ case 0:
+ synaptics_update_slots(priv, 2, 0, 1);
+ break;
+ case 1:
+ /*
+ * If previous slot 0 had SGM,
+ * the new finger, slot 1, is in AGM.
+ * Otherwise, new finger, slot 0, is in SGM.
+ */
+ slot_sgm = synaptics_find_sgm(priv);
+ if (slot_sgm < 1)
+ slot_sgm = 1;
+ synaptics_update_slots(priv, 2, 0, slot_sgm);
+ break;
+ case 2:
+ /* Assuming no change in fingers... */
+ slot_sgm = synaptics_find_sgm(priv);
+ slot_agm = synaptics_find_agm(priv);
+ if (slot_sgm < 0)
+ slot_sgm = 0;
+ if (slot_agm < slot_sgm + 1)
+ slot_agm = slot_sgm + 1;
+ synaptics_update_slots(priv, 2, slot_sgm, slot_agm);
+ 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.
+ * The slots get filled on a subsequent 2->2
+ */
+ synaptics_update_slots(priv, 0, 0, 0);
+ break;
+ }
+ break;
+
+ case 3:
+ switch (old_num_fingers) {
+ case 0:
+ synaptics_update_slots(priv, 3, 0, 2);
+ break;
+ case 1:
+ /*
+ * If old SGM was slot 2 or higher, that slot is now
+ * AGM, with one of the new fingers in slot 0 as SGM.
+ * Otherwise, the 3 used slots are 0,1,2.
+ */
+ slot_sgm = synaptics_find_sgm(priv);
+ if (slot_sgm < 2)
+ slot_sgm = 2;
+ synaptics_update_slots(priv, 3, 0, slot_sgm);
+ 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.
+ * The slots get filled on a subsequent 3->3
+ */
+ synaptics_update_slots(priv, 0, 0, 0);
+ break;
+ case 3:
+ /* Assuming no change in fingers... */
+ slot_sgm = synaptics_find_sgm(priv);
+ slot_agm = synaptics_find_agm(priv);
+ if (slot_sgm < 0)
+ slot_sgm = 0;
+ if (slot_agm < slot_sgm + 2)
+ slot_agm = slot_sgm + 2;
+ synaptics_update_slots(priv, 3, slot_sgm, slot_agm);
+ break;
+ }
break;
}
+process_mt_data_done:
priv->num_fingers = new_num_fingers;
+ priv->agm_pending = false;
}
static void synaptics_report_mt_data(struct psmouse *psmouse,
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 2214af6..cc193b6 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -120,6 +120,7 @@
#define SYN_SLOT_EMPTY 0
#define SYN_SLOT_SGM 1
#define SYN_SLOT_AGM 2
+#define SYN_SLOT_HIDDEN 3
/* number of tracking slots for Image Sensor firmware */
#define SYN_TRACK_SLOT_COUNT 5
@@ -166,6 +167,7 @@ struct synaptics_data {
struct synaptics_hw_state agm; /* last AGM packet */
int num_fingers; /* current finger count */
int slot[SYN_TRACK_SLOT_COUNT]; /* finger slot state */
+ bool agm_pending; /* new AGM packet received */
};
void synaptics_module_init(void);
--
1.7.3.1
From: Daniel Kurtz <[email protected]>
T5R2 touchpads track up to 5 fingers, but only report 2.
They introduce a special "TYPE=2" (AGM-CONTACT) packet type that reports
the number of tracked fingers and which finger is reported in the SGM
and AGM packets.
With this new packet type, it is possible to send up to 5 MTB slots to
userspace.
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/input/mouse/synaptics.c | 33 +++++++++++++++++++++++++++++++--
1 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 8b38e08..5f227be 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -647,11 +647,16 @@ static void synaptics_process_hw_state(struct synaptics_data *priv,
new_num_fingers = 1;
else if (sgm->w == 0)
new_num_fingers = 2;
- else if (sgm->w == 1)
- new_num_fingers = 3;
+ else if (sgm->w == 1) {
+ if (agm->finger_count > 3)
+ new_num_fingers = agm->finger_count;
+ else
+ new_num_fingers = 3;
+ }
if (new_num_fingers == 0) {
synaptics_update_slots(priv, 0, 0, 0);
+ synaptics_agm_finger_update(priv, 0, 0, 0);
goto process_mt_data_done;
}
@@ -661,6 +666,7 @@ static void synaptics_process_hw_state(struct synaptics_data *priv,
*/
if (priv->agm_pending && agm->z == 0) {
synaptics_update_slots(priv, 1, 0, 0);
+ synaptics_agm_finger_update(priv, 0, 0, 0);
goto process_mt_data_done;
}
@@ -720,6 +726,11 @@ static void synaptics_process_hw_state(struct synaptics_data *priv,
*/
synaptics_update_slots(priv, 0, 0, 0);
break;
+
+ case 4:
+ case 5:
+ synaptics_update_slots(priv, 1, agm->finger_sgm, 0);
+ break;
}
break;
@@ -761,6 +772,12 @@ static void synaptics_process_hw_state(struct synaptics_data *priv,
*/
synaptics_update_slots(priv, 0, 0, 0);
break;
+
+ case 4:
+ case 5:
+ synaptics_update_slots(priv, 2, agm->finger_sgm,
+ agm->finger_agm);
+ break;
}
break;
@@ -800,8 +817,20 @@ static void synaptics_process_hw_state(struct synaptics_data *priv,
slot_agm = slot_sgm + 2;
synaptics_update_slots(priv, 3, slot_sgm, slot_agm);
break;
+
+ case 4:
+ case 5:
+ synaptics_update_slots(priv, 3, agm->finger_sgm,
+ agm->finger_agm);
+ break;
}
break;
+
+ case 4:
+ case 5:
+ synaptics_update_slots(priv, agm->finger_count, agm->finger_sgm,
+ agm->finger_agm);
+ break;
}
process_mt_data_done:
--
1.7.3.1
On 06/29/2011 06:07 AM, [email protected] wrote:
> From: Daniel Kurtz <[email protected]>
>
> A Synaptics image sensor tracks 5 fingers, but can only report 2.
> This behavior is called "T5R2" = Track 5 Report 2
>
> Algorithm for choosing which 2 fingers to report in which packet:
> Touchpad maintains 5 slots, numbered 0 to 4.
> Initially all slots are empty.
> As new fingers are detected, they are assigned the lowest available
> slot.
> Touchpad always reports:
> SGM: lowest numbered non-empty slot
> AGM: highest numbered non-empty slot, if there is one.
Hi Daniel,
Thanks for the new patches! I will review them soon in detail, but I
would like to first confirm some details about the above.
In the older "profile" devices, we got essentially the bounding box of
all touches. This allows us to detect pinch gestures between the two
most extreme touches when there are three touches on the touchpad.
The above description makes it sound like we will no longer have a
bounding box of touches. According to the description, if I put four
fingers down in a square formation and then touch a fifth finger in the
middle of the square, I will only see the locations of one corner and
the middle of the square. This would make meaningful gesture detection
beyond two touches nearly impossible. Is this really how the touchpad
works, or is the above description not quite right?
Thanks!
-- Chase
Hi Chase,
On Wed, Jun 29, 2011 at 11:02:53AM +0100, Chase Douglas wrote:
> In the older "profile" devices, we got essentially the bounding box of
> all touches. This allows us to detect pinch gestures between the two
> most extreme touches when there are three touches on the touchpad.
This isn't necessarily true for all pads. If you look at Derek's
patchset from a couple of weeks ago, it was for a profile sensor which
had the same reporting behaviour. Internally, the touchpad could track
many fingers, but would always report the first (in SGM packet) and most
recent (in AGM packet) fingers. You could fake a bounding-box
calculation, but it would be the bounding box of fingers 0 and n, rather
than the bounding box of all fingers.
> The above description makes it sound like we will no longer have a
> bounding box of touches. According to the description, if I put four
> fingers down in a square formation and then touch a fifth finger in the
> middle of the square, I will only see the locations of one corner and
> the middle of the square. This would make meaningful gesture detection
> beyond two touches nearly impossible. Is this really how the touchpad
> works, or is the above description not quite right?
Yes, I believe that's how the touchpad works. It would be nice to get
all fingers, but unfortunately PS/2 doesn't give us enough bandwidth to
do that.
Cheers,
Daniel
On 06/29/2011 11:07 AM, Daniel Stone wrote:
> Hi Chase,
>
> On Wed, Jun 29, 2011 at 11:02:53AM +0100, Chase Douglas wrote:
>> In the older "profile" devices, we got essentially the bounding box of
>> all touches. This allows us to detect pinch gestures between the two
>> most extreme touches when there are three touches on the touchpad.
>
> This isn't necessarily true for all pads. If you look at Derek's
> patchset from a couple of weeks ago, it was for a profile sensor which
> had the same reporting behaviour. Internally, the touchpad could track
> many fingers, but would always report the first (in SGM packet) and most
> recent (in AGM packet) fingers. You could fake a bounding-box
> calculation, but it would be the bounding box of fingers 0 and n, rather
> than the bounding box of all fingers.
Are we really sure that Derek's trackpad was a profile device? I mean
*really* sure. Because if Synaptics is putting out two different devices
that are identical in protocol but behave differently, then that's a
*big* problem for us gesture developers :).
I would be surprised if a profile device were providing the first and
third touch locations as opposed to the bounding box because then
there's no difference between it and this new "image sensor" device type.
>> The above description makes it sound like we will no longer have a
>> bounding box of touches. According to the description, if I put four
>> fingers down in a square formation and then touch a fifth finger in the
>> middle of the square, I will only see the locations of one corner and
>> the middle of the square. This would make meaningful gesture detection
>> beyond two touches nearly impossible. Is this really how the touchpad
>> works, or is the above description not quite right?
>
> Yes, I believe that's how the touchpad works. It would be nice to get
> all fingers, but unfortunately PS/2 doesn't give us enough bandwidth to
> do that.
If this is true for these new image devices, then we'll need to set a
different property bit other than SEMI_MT (maybe FIRST_LAST_MT? I'm not
good with names :). I'm thinking we should disable gesture support for
these trackpads in uTouch when there are more than two touches because
we can't be sure what is going on in any meaningful way. The bounding
box is more useful than this, which is really sad.
-- Chase
Hi Chase,
Thanks for looking at the patches!
On Wed, Jun 29, 2011 at 6:02 PM, Chase Douglas
<[email protected]> wrote:
>
> On 06/29/2011 06:07 AM, [email protected] wrote:
> > From: Daniel Kurtz <[email protected]>
> >
> > A Synaptics image sensor tracks 5 fingers, but can only report 2.
> > This behavior is called "T5R2" = Track 5 Report 2
> >
> > Algorithm for choosing which 2 fingers to report in which packet:
> > ? Touchpad maintains 5 slots, numbered 0 to 4.
> > ? Initially all slots are empty.
> > ? As new fingers are detected, they are assigned the lowest available
> > ? slot.
> > ? Touchpad always reports:
> > ? ? SGM: lowest numbered non-empty slot
> > ? ? AGM: highest numbered non-empty slot, if there is one.
>
> Hi Daniel,
>
> Thanks for the new patches! I will review them soon in detail, but I
> would like to first confirm some details about the above.
>
> In the older "profile" devices, we got essentially the bounding box of
> all touches. This allows us to detect pinch gestures between the two
> most extreme touches when there are three touches on the touchpad.
>
> The above description makes it sound like we will no longer have a
> bounding box of touches. According to the description, if I put four
> fingers down in a square formation and then touch a fifth finger in the
> middle of the square, I will only see the locations of one corner and
> the middle of the square. This would make meaningful gesture detection
> beyond two touches nearly impossible. Is this really how the touchpad
> works, or is the above description not quite right?
This is how the image sensor works, from my observations.
As for profile sensors the kernel driver creates a "best guess
bounding box" by picking:
top_left = (min(x1,x2), min(y1,y2))
bottom_right = (max(x1,x2), max(y1,y2))
However, at least from my experiments with one profile sensor, there
is no guarantee that this rectangle actually contains all touches on
the pad.
In fact, I think it is trying really hard to return the position of
the first two touches that it is tracking.
When there are two fingers, it returns the finger with the larger
y-value (and what the trackpad believes to be its x-), and the SGM
packet, and the other finger in the AGM.
When there are three or more fingers, it still reports the first two,
irregardless of where the new fingers are added.
It would be very curious if different profile sensors behaved differently.
In any case, this patch should not change the behavior of profile
sensors, since it only applies to image sensors.
Thanks,
-Daniel
Hi Daniel, Chase,
On Wed, Jun 29, 2011 at 6:32 PM, Chase Douglas
<[email protected]> wrote:
> On 06/29/2011 11:07 AM, Daniel Stone wrote:
>> Hi Chase,
>>
>> On Wed, Jun 29, 2011 at 11:02:53AM +0100, Chase Douglas wrote:
>>> In the older "profile" devices, we got essentially the bounding box of
>>> all touches. This allows us to detect pinch gestures between the two
>>> most extreme touches when there are three touches on the touchpad.
>>
>> This isn't necessarily true for all pads. ?If you look at Derek's
>> patchset from a couple of weeks ago, it was for a profile sensor which
>> had the same reporting behaviour. ?Internally, the touchpad could track
>> many fingers, but would always report the first (in SGM packet) and most
>> recent (in AGM packet) fingers. ?You could fake a bounding-box
>> calculation, but it would be the bounding box of fingers 0 and n, rather
>> than the bounding box of all fingers.
>
> Are we really sure that Derek's trackpad was a profile device? I mean
> *really* sure. Because if Synaptics is putting out two different devices
> that are identical in protocol but behave differently, then that's a
> *big* problem for us gesture developers :).
>
> I would be surprised if a profile device were providing the first and
> third touch locations as opposed to the bounding box because then
> there's no difference between it and this new "image sensor" device type.
I agree with Chase, the 0:n box behavior sounds more like image sensor
than profile sensor.
However, as I just mentioned in the previous email, I have looked at a
profile sensor that tries to report first and second finger positions,
not a bounding box.
In any case, the result is:
You end up computing (usually) a 0:1 "bounding box", with n >= 2
fingers on the pad.
I say usually because it is pretty easy to confuse the finger tracking
of a profile sensor.
>>> The above description makes it sound like we will no longer have a
>>> bounding box of touches. According to the description, if I put four
>>> fingers down in a square formation and then touch a fifth finger in the
>>> middle of the square, I will only see the locations of one corner and
>>> the middle of the square. This would make meaningful gesture detection
>>> beyond two touches nearly impossible. Is this really how the touchpad
>>> works, or is the above description not quite right?
>>
>> Yes, I believe that's how the touchpad works. ?It would be nice to get
>> all fingers, but unfortunately PS/2 doesn't give us enough bandwidth to
>> do that.
>
> If this is true for these new image devices, then we'll need to set a
> different property bit other than SEMI_MT (maybe FIRST_LAST_MT? I'm not
> good with names :). I'm thinking we should disable gesture support for
> these trackpads in uTouch when there are more than two touches because
> we can't be sure what is going on in any meaningful way. The bounding
> box is more useful than this, which is really sad.
>
> -- Chase
This is exactly what this patch set does by defining the T5R2 property.
It is very challenging to support any 3+ finger gestures with a
touchpad that only reports two fingers... whether it is semi-mt or
T5R2.
You must always make an assumption about where those hidden fingers are.
But, that's a problem for userspace... :)
-Daniel
On Wed, Jun 29, 2011 at 12:07 AM, <[email protected]> wrote:
> From: Daniel Kurtz <[email protected]>
>
> Synaptics touchpads report a 'w' value in each data report.
> For touchpads that support palm detection, when there is a single finger
> on the pad, the 'w' value reports its width in the range 4 to 15.
> Thus, the minimum valid width is 4.
FYI: I had debated on this as well. When driver was first modified to
report min/max width, the min width of zero was chosen because when
not touching the pad a value of zero is forced by driver to user. So
the range is 0, 4-15.
>
> Note: Other values of 'w' are used to report special conditions:
> ?w=0: 2 fingers are on the pad
> ?w=1: 3 or more fingers are on the pad
> ?w=2: the packet contains "Advanced Gesture Mode" data.
>
> Signed-off-by: Daniel Kurtz <[email protected]>
> ---
> ?drivers/input/mouse/synaptics.c | ? ?2 +-
> ?1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
> index f6d0c04..a4b7801 100644
> --- a/drivers/input/mouse/synaptics.c
> +++ b/drivers/input/mouse/synaptics.c
> @@ -706,7 +706,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
> ? ? ? ?}
>
> ? ? ? ?if (SYN_CAP_PALMDETECT(priv->capabilities))
> - ? ? ? ? ? ? ? input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
> + ? ? ? ? ? ? ? input_set_abs_params(dev, ABS_TOOL_WIDTH, 4, 15, 0, 0);
>
> ? ? ? ?__set_bit(EV_KEY, dev->evbit);
> ? ? ? ?__set_bit(BTN_TOUCH, dev->keybit);
> --
> 1.7.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>
Hi Chris,
Thanks for taking a look!
On Wed, Jun 29, 2011 at 9:28 PM, Chris Bagwell <[email protected]> wrote:
> On Wed, Jun 29, 2011 at 12:07 AM, ?<[email protected]> wrote:
>> From: Daniel Kurtz <[email protected]>
>>
>> Synaptics touchpads report a 'w' value in each data report.
>> For touchpads that support palm detection, when there is a single finger
>> on the pad, the 'w' value reports its width in the range 4 to 15.
>> Thus, the minimum valid width is 4.
>
> FYI: I had debated on this as well. ?When driver was first modified to
> report min/max width, the min width of zero was chosen because when
> not touching the pad a value of zero is forced by driver to user. ?So
> the range is 0, 4-15.
Yup, good catch. I now see that this is true for the legacy single-touch case.
For consistency with the existing implementation, I'm ok to drop this patch.
I think we can still keep the range 4-15 for the ABS_MT_TOUCH_MAJOR
axis, though, right?
A pure-mt userspace app can get the correct width range, and deduce 0
fingers from the fact that all MT-B slots have tracking_id == -1.
-Dan
>>
>> Note: Other values of 'w' are used to report special conditions:
>> ?w=0: 2 fingers are on the pad
>> ?w=1: 3 or more fingers are on the pad
>> ?w=2: the packet contains "Advanced Gesture Mode" data.
>>
>> Signed-off-by: Daniel Kurtz <[email protected]>
>> ---
>> ?drivers/input/mouse/synaptics.c | ? ?2 +-
>> ?1 files changed, 1 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
>> index f6d0c04..a4b7801 100644
>> --- a/drivers/input/mouse/synaptics.c
>> +++ b/drivers/input/mouse/synaptics.c
>> @@ -706,7 +706,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
>> ? ? ? ?}
>>
>> ? ? ? ?if (SYN_CAP_PALMDETECT(priv->capabilities))
>> - ? ? ? ? ? ? ? input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
>> + ? ? ? ? ? ? ? input_set_abs_params(dev, ABS_TOOL_WIDTH, 4, 15, 0, 0);
>>
>> ? ? ? ?__set_bit(EV_KEY, dev->evbit);
>> ? ? ? ?__set_bit(BTN_TOUCH, dev->keybit);
>> --
>> 1.7.3.1
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>> the body of a message to [email protected]
>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>
On Wed, Jun 29, 2011 at 11:48 AM, Daniel Kurtz <[email protected]> wrote:
> Hi Chris,
>
> Thanks for taking a look!
>
> On Wed, Jun 29, 2011 at 9:28 PM, Chris Bagwell <[email protected]> wrote:
>> On Wed, Jun 29, 2011 at 12:07 AM, ?<[email protected]> wrote:
>>> From: Daniel Kurtz <[email protected]>
>>>
>>> Synaptics touchpads report a 'w' value in each data report.
>>> For touchpads that support palm detection, when there is a single finger
>>> on the pad, the 'w' value reports its width in the range 4 to 15.
>>> Thus, the minimum valid width is 4.
>>
>> FYI: I had debated on this as well. ?When driver was first modified to
>> report min/max width, the min width of zero was chosen because when
>> not touching the pad a value of zero is forced by driver to user. ?So
>> the range is 0, 4-15.
>
> Yup, good catch. ?I now see that this is true for the legacy single-touch case.
> For consistency with the existing implementation, I'm ok to drop this patch.
>
> I think we can still keep the range 4-15 for the ABS_MT_TOUCH_MAJOR
> axis, though, right?
> A pure-mt userspace app can get the correct width range, and deduce 0
> fingers from the fact that all MT-B slots have tracking_id == -1.
Yeah, at least I'd consider it an app bug.
For that matter, it should be true of ABS_TOOL_WIDTH as well (we could
change to always send minimum 4 as apart of this patch).
Since we are on it, maybe a year ago I tested a version of this patch
and notice xf86-input-synaptics defaulted to non-useful value for
EmulateTwoFingerMinW because of the scale change. The
xf86-input-synaptics logic in question has since be modified/reverted
so I think thats not an issue anymore.
I found no issues with apps see'ing ABS_TOOL_WIDTH >= 4 always.
Chris
>
> -Dan
>
>>>
>>> Note: Other values of 'w' are used to report special conditions:
>>> ?w=0: 2 fingers are on the pad
>>> ?w=1: 3 or more fingers are on the pad
>>> ?w=2: the packet contains "Advanced Gesture Mode" data.
>>>
>>> Signed-off-by: Daniel Kurtz <[email protected]>
>>> ---
>>> ?drivers/input/mouse/synaptics.c | ? ?2 +-
>>> ?1 files changed, 1 insertions(+), 1 deletions(-)
>>>
>>> diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
>>> index f6d0c04..a4b7801 100644
>>> --- a/drivers/input/mouse/synaptics.c
>>> +++ b/drivers/input/mouse/synaptics.c
>>> @@ -706,7 +706,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
>>> ? ? ? ?}
>>>
>>> ? ? ? ?if (SYN_CAP_PALMDETECT(priv->capabilities))
>>> - ? ? ? ? ? ? ? input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
>>> + ? ? ? ? ? ? ? input_set_abs_params(dev, ABS_TOOL_WIDTH, 4, 15, 0, 0);
>>>
>>> ? ? ? ?__set_bit(EV_KEY, dev->evbit);
>>> ? ? ? ?__set_bit(BTN_TOUCH, dev->keybit);
>>> --
>>> 1.7.3.1
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>>> the body of a message to [email protected]
>>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>> the body of a message to [email protected]
>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>>
>