Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH] input: wm97xx: adapt parameters to tosa touchscreen.
From: Dmitry Eremin-Solenikov @ 2014-10-24 17:18 UTC (permalink / raw)
  To: linux-input
  Cc: patches, Dmitry Torokhov, Mark Brown, Liam Girdwood,
	Dmitry Eremin-Solenikov

From: Dmitry Eremin-Solenikov <dmitry_eremin@mentor.com>

Sharp SL-6000 (tosa) touchscreen needs wider limits to properly map all
points on the screen. Expand ranges in abs_x and abs_y arrays according
to the touchscreen area.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
 drivers/input/touchscreen/wm97xx-core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
index d0ef91f..b1ae779 100644
--- a/drivers/input/touchscreen/wm97xx-core.c
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -70,11 +70,11 @@
  * Documentation/input/input-programming.txt for more details.
  */
 
-static int abs_x[3] = {350, 3900, 5};
+static int abs_x[3] = {150, 4000, 5};
 module_param_array(abs_x, int, NULL, 0);
 MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
 
-static int abs_y[3] = {320, 3750, 40};
+static int abs_y[3] = {200, 4000, 40};
 module_param_array(abs_y, int, NULL, 0);
 MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
 
-- 
2.1.1


^ permalink raw reply related

* [PATCH] Input: opencores-kbd - fix error hanling
From: Dmitry Torokhov @ 2014-10-24 21:34 UTC (permalink / raw)
  To: linux-input; +Cc: Pramod Gurav, linux-kernel

When I was adjusting patch in 848d479361793edb79aa68140cb64d4ec9032d88 to
use devm_ioremap_resource() I messed it up.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/keyboard/opencores-kbd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c
index 62abe2c..f8502bb 100644
--- a/drivers/input/keyboard/opencores-kbd.c
+++ b/drivers/input/keyboard/opencores-kbd.c
@@ -70,7 +70,7 @@ static int opencores_kbd_probe(struct platform_device *pdev)
 
 	opencores_kbd->addr = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(opencores_kbd->addr))
-		error = PTR_ERR(opencores_kbd->addr);
+		return PTR_ERR(opencores_kbd->addr);
 
 	input->name = pdev->name;
 	input->phys = "opencores-kbd/input0";
-- 
2.1.0.rc2.206.gedb03e5


-- 
Dmitry

^ permalink raw reply related

* Re: [PATCH] input: stmpe: fix valid key line bitmask
From: Dmitry Torokhov @ 2014-10-24 21:37 UTC (permalink / raw)
  To: Linus Walleij; +Cc: linux-input
In-Reply-To: <1413636729-2985-1-git-send-email-linus.walleij@linaro.org>

On Sat, Oct 18, 2014 at 02:52:09PM +0200, Linus Walleij wrote:
> The bitmask comment says it will enable GPIO 8-14 and 16-20
> for keypad use, but it actually enables GPIO 8-11 and 13-20
> due to a bit error.
> 
> Instead of masking of the "hole" at GPIO 12 (which is used
> for keypad output 4) mask of the proper "hole" at GPIO 15.
> 
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> Hi Dmitry, this is for fixes I think.

Applied, thank you.

> ---
>  drivers/input/keyboard/stmpe-keypad.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
> index c6727dda68f2..ef5e67fb567e 100644
> --- a/drivers/input/keyboard/stmpe-keypad.c
> +++ b/drivers/input/keyboard/stmpe-keypad.c
> @@ -86,7 +86,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
>  		.max_cols		= 8,
>  		.max_rows		= 12,
>  		.col_gpios		= 0x0000ff,	/* GPIO 0 - 7*/
> -		.row_gpios		= 0x1fef00,	/* GPIO 8-14, 16-20 */
> +		.row_gpios		= 0x1f7f00,	/* GPIO 8-14, 16-20 */
>  	},
>  	[STMPE2403] = {
>  		.auto_increment		= true,
> -- 
> 1.9.3
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 1/3] mfd: stmpe: add pull up/down register offsets for STMPE
From: Dmitry Torokhov @ 2014-10-24 21:42 UTC (permalink / raw)
  To: Lee Jones; +Cc: Linus Walleij, Samuel Ortiz, linux-kernel, linux-input
In-Reply-To: <20141020064605.GB25349@x1>

On Mon, Oct 20, 2014 at 07:46:05AM +0100, Lee Jones wrote:
> On Sat, 18 Oct 2014, Linus Walleij wrote:
> 
> > This adds the register offsets for pull up/down for the STMPE
> > 1601, 1801 and 24xx expanders. This is used to bias GPIO lines
> > and keypad lines.
> > 
> > Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> > ---
> > Hi Sam, Lee: I think you should just ACK this so Dmitry can take the
> > patch series through the input tree, where the registers need to be
> > used to enable the keypad on the STMPE2401.
> > ---
> >  drivers/mfd/stmpe.c       | 4 ++++
> >  drivers/mfd/stmpe.h       | 3 +++
> >  include/linux/mfd/stmpe.h | 2 ++
> >  3 files changed, 9 insertions(+)
> 
> Does this need to go in with the other patches in the set?
> 
> I'm _guessing_ that, as you've sent them separately, they can be
> applied separately?
> 
> This needs to go in via MFD, but for my own reference:
> 
> Acked-by: Lee Jones <lee.jones@linaro.org>

The new register definitions are needed for patch #3.  If you insist on
this going through MFD then please take the other 2 patches with my Ack
through MFD tree as well I do not think we need the whole song and dance
trying to synchronize 2 trees.

Thanks.

-- 
Dmitry

^ permalink raw reply

* [PATCH] Input: ims-pcu - fix dead code in ims_pcu_ofn_reg_addr_store()
From: Dmitry Torokhov @ 2014-10-24 22:28 UTC (permalink / raw)
  To: linux-input; +Cc: linux-kernel

Coverity pointed out that at return point error is always 0 so the
conditional is not needed.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/misc/ims-pcu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index 719410f..afed8e2 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -1381,7 +1381,7 @@ static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
 	pcu->ofn_reg_addr = value;
 	mutex_unlock(&pcu->cmd_mutex);
 
-	return error ?: count;
+	return count;
 }
 
 static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR,
-- 
2.1.0.rc2.206.gedb03e5


-- 
Dmitry

^ permalink raw reply related

* [PATCH] Input: vsxxxaa - fix code dropping bytes from queue
From: Dmitry Torokhov @ 2014-10-24 22:38 UTC (permalink / raw)
  To: linux-input; +Cc: Jan-Benedict Glaw, linux-kernel

I believe the intent of the code was to drop oldest bytes from the queue,
not the latest if we drop one byte and both latest and some oldest of we
are dropping more than one.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/mouse/vsxxxaa.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index 3829823..abd4944 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -128,7 +128,7 @@ static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num)
 	if (num >= mouse->count) {
 		mouse->count = 0;
 	} else {
-		memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num);
+		memmove(mouse->buf, mouse->buf + num, BUFLEN - num);
 		mouse->count -= num;
 	}
 }
-- 
2.1.0.rc2.206.gedb03e5


-- 
Dmitry

^ permalink raw reply related

* Re: [PATCH] input: wm97xx: adapt parameters to tosa touchscreen.
From: Dmitry Torokhov @ 2014-10-25  0:28 UTC (permalink / raw)
  To: Dmitry Eremin-Solenikov
  Cc: linux-input, patches, Mark Brown, Liam Girdwood,
	Dmitry Eremin-Solenikov
In-Reply-To: <1414171086-18330-1-git-send-email-dbaryshkov@gmail.com>

On Fri, Oct 24, 2014 at 09:18:06PM +0400, Dmitry Eremin-Solenikov wrote:
> From: Dmitry Eremin-Solenikov <dmitry_eremin@mentor.com>
> 
> Sharp SL-6000 (tosa) touchscreen needs wider limits to properly map all
> points on the screen. Expand ranges in abs_x and abs_y arrays according
> to the touchscreen area.
> 
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>

Applied, thank you.

> ---
>  drivers/input/touchscreen/wm97xx-core.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
> index d0ef91f..b1ae779 100644
> --- a/drivers/input/touchscreen/wm97xx-core.c
> +++ b/drivers/input/touchscreen/wm97xx-core.c
> @@ -70,11 +70,11 @@
>   * Documentation/input/input-programming.txt for more details.
>   */
>  
> -static int abs_x[3] = {350, 3900, 5};
> +static int abs_x[3] = {150, 4000, 5};
>  module_param_array(abs_x, int, NULL, 0);
>  MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
>  
> -static int abs_y[3] = {320, 3750, 40};
> +static int abs_y[3] = {200, 4000, 40};
>  module_param_array(abs_y, int, NULL, 0);
>  MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
>  
> -- 
> 2.1.1
> 

-- 
Dmitry

^ permalink raw reply

* Re: FocalTech touchpad issues on K750
From: Hans de Goede @ 2014-10-25 17:56 UTC (permalink / raw)
  To: Mathias Gottschlag; +Cc: Dmitry Torokhov, linux-usb
In-Reply-To: <544B93A6.6010801@gmail.com>

Hi,

On 10/25/2014 02:12 PM, Mathias Gottschlag wrote:
> Hi,
> 
> I've had Nikola Nikolov test my touchpad code for me and got the report
> that the driver worked, but only when he passed i8042.noloop=1 and
> psmouse.proto=bare to the kernel. Now I have been pretty careful to keep
> your previous patch in place when writing my code. So, I'd like to
> further debug this issue, but I don't really understand the matter.
> 
> Especially, what is that AUX LOOP command that is disabled by noloop=1?
> Your patch does not have anything to do with that option, does it?

WRT passing noloop that laptop has a weird i8042 implementation where the
kernel does not detect the aux port at all unless noloop is passed, a
patch to do this automatically is already upstream:

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/input/serio/i8042-x86ia64io.h?id=9ff84a17302aeb8913ff244ecc0d8f9d219fecb5

WRT needing to pass psmouse.proto=bare, that should not be necessary
with recent kernels, but the fix for this has only gone upstream very
recently, so likely he is just using a somewhat older kernel.

Regards,

Hans

^ permalink raw reply

* [PATCH] psmouse: Add some support for the FocalTech PS/2 protocol extensions.
From: Mathias Gottschlag @ 2014-10-25 12:01 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, Hans de Goede

Most of the protocol for these touchpads has been reverse engineered. This
commit adds a basic multitouch-capable driver.

A lot of the protocol is still unknown. Especially, we don't know how to
identify the device yet apart from the PNP ID. The code currently compares
known good values with all data from the touchpad which could be usable for
identification, so by design even minor variations will cause the driver
to fail to detect the device and fall back to compatibility mode.

The previous workaround for these devices has been left in place in case
the driver is not compiled into the kernel or in case some other device
with the same PNP ID is not recognized by the driver yet still has the same
problems with the device probing code.
---

Hi,

this patch is related to the following bugs:
https://bugzilla.redhat.com/show_bug.cgi?id=1110011
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1372609

It has been tested on the Asus UX303LN, UX303LA and K750L laptops. The
latter is affected by bug 1110011 above and used to require additional
flags before the touchpad would work. This behaviour should have been
fixed by the previous FocalTech patch, but is still present after this
patch. I have not yet gotten any feedback yet, but my theory is that the
original patch failed to fix the kernel on that device. I have to admit
though that I haven't 100% understood that patch.

When reviewing, keep in mind that this is my first linux patch. I am
using the code for quite some time now without issues, but I might have
overlooked quite some problems.

Regards,
Mathias

 drivers/input/mouse/Kconfig        |  10 ++
 drivers/input/mouse/focaltech.c    | 326
++++++++++++++++++++++++++++++++++++-
 drivers/input/mouse/focaltech.h    |  61 +++++++
 drivers/input/mouse/psmouse-base.c |  15 ++
 drivers/input/mouse/psmouse.h      |   1 +
 5 files changed, 406 insertions(+), 7 deletions(-)

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 366fc7a..db973e5 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
  	  If unsure, say N.
 +config MOUSE_PS2_FOCALTECH
+	bool "FocalTech PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have a FocalTech PS/2 TouchPad connected to
+	  your system.
+
+	  If unsure, say Y.
+
 config MOUSE_SERIAL
 	tristate "Serial mouse"
 	select SERIO
diff --git a/drivers/input/mouse/focaltech.c
b/drivers/input/mouse/focaltech.c
index f4d657e..ada97cc 100644
--- a/drivers/input/mouse/focaltech.c
+++ b/drivers/input/mouse/focaltech.c
@@ -2,6 +2,7 @@
  * Focaltech TouchPad PS/2 mouse driver
  *
  * Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -13,15 +14,14 @@
  * Hans de Goede <hdegoede@redhat.com>
  */
 -/*
- * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
- * detection only, to avoid further detection attempts confusing the
touchpad
- * this way it at least works in PS/2 mouse compatibility mode.
- */
  #include <linux/device.h>
 #include <linux/libps2.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
 #include "psmouse.h"
+#include "focaltech.h"
  static const char * const focaltech_pnp_ids[] = {
 	"FLT0101",
@@ -30,7 +30,14 @@ static const char * const focaltech_pnp_ids[] = {
 	NULL
 };
 -int focaltech_detect(struct psmouse *psmouse, bool set_properties)
+/*
+ * Even if the kernel is built without support for Focaltech PS/2
touchpads (or
+ * when the real driver fails to recognize the device), we still have
to detect
+ * them in order to avoid further detection attempts confusing the
touchpad.
+ * This way it at least works in PS/2 mouse compatibility mode.
+ */
+
+int focaltech_detect_fallback(struct psmouse *psmouse, bool set_properties)
 {
 	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
 		return -ENODEV;
@@ -43,10 +50,315 @@ int focaltech_detect(struct psmouse *psmouse, bool
set_properties)
 	return 0;
 }
 -int focaltech_init(struct psmouse *psmouse)
+int focaltech_init_fallback(struct psmouse *psmouse)
+{
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+	psmouse_reset(psmouse);
+
+	return 0;
+}
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
+
+static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
+		unsigned char *param)
+{
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
+		return -1;
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -1;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -1;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -1;
+	param[0] = reg;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -1;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+	return 0;
+}
+
+int focaltech_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+
+	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
+		return -ENODEV;
+
+	/*
+	 * We don't know how to identify the device, so we just check the
+	 * content of all registers.
+	 */
+	if (focaltech_read_register(ps2dev, 0, param))
+		return -EIO;
+	if (param[0] != 0x69 || param[1] != 0x80 || param[2] != 0x80)
+		return -ENODEV;
+	if (focaltech_read_register(ps2dev, 1, param))
+		return -EIO;
+	if (param[0] != 0x36 || param[1] != 0x53 || param[2] != 0x03)
+		return -ENODEV;
+	if (focaltech_read_register(ps2dev, 2, param))
+		return -EIO;
+	/* 0x13 and 0x0d might be the touchpad size? */
+	if (param[0] != 0x00 || param[1] != 0x13 || param[2] != 0x0d)
+		return -ENODEV;
+	if (focaltech_read_register(ps2dev, 5, param))
+		return -EIO;
+	if (param[0] != 0x0b || param[1] != 0x03 || param[2] != 0x00)
+		return -ENODEV;
+	if (focaltech_read_register(ps2dev, 6, param))
+		return -EIO;
+	if (param[0] != 0x23 || param[1] != 0xbd || param[2] != 0xf8)
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "FocalTech";
+		psmouse->name = "FocalTech Touchpad";
+	}
+
+	return 0;
+}
+
+static void focaltech_report_state(struct psmouse *psmouse)
+{
+	int i;
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	struct input_dev *dev = psmouse->dev;
+	int finger_count = 0;
+
+	for (i = 0; i < FOC_MAX_FINGERS; i++) {
+		struct focaltech_finger_state *finger = &state->fingers[i];
+		int active = finger->active && finger->valid;
+		input_mt_slot(dev, i);
+		input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+		if (active) {
+			finger_count++;
+			input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
+			input_report_abs(dev, ABS_MT_POSITION_Y,
+					focaltech_invert_y(finger->y));
+		}
+	}
+	input_mt_report_pointer_emulation(dev, false);
+	input_mt_report_finger_count(dev, finger_count);
+
+	input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
+	input_sync(psmouse->dev);
+}
+
+static void process_touch_packet(struct focaltech_hw_state *state,
+		unsigned char *packet)
+{
+	int i;
+	unsigned char fingers = packet[1];
+
+	state->pressed = (packet[0] >> 4) & 1;
+	/* the second byte contains a bitmap of all fingers touching the pad */
+	for (i = 0; i < FOC_MAX_FINGERS; i++) {
+		if ((fingers & 0x1) && !state->fingers[i].active) {
+			/* we do not have a valid position for the finger yet */
+			state->fingers[i].valid = 0;
+		}
+		state->fingers[i].active = fingers & 0x1;
+		fingers >>= 1;
+	}
+}
+
+static void process_abs_packet(struct focaltech_hw_state *state,
+		unsigned char *packet)
+{
+	unsigned int finger = (packet[1] >> 4) - 1;
+
+	state->pressed = (packet[0] >> 4) & 1;
+	if (finger >= FOC_MAX_FINGERS)
+		return;
+	/*
+	 * packet[5] contains some kind of tool size in the most significant
+	 * nibble. 0xff is a special value (latching) that signals a large
+	 * contact area.
+	 */
+	if (packet[5] == 0xff)
+		return;
+	state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
+	state->fingers[finger].y = (packet[3] << 8) | packet[4];
+	state->fingers[finger].valid = 1;
+}
+static void process_rel_packet(struct focaltech_hw_state *state,
+		unsigned char *packet)
+{
+	int finger1 = ((packet[0] >> 4) & 0x7) - 1;
+	int finger2 = ((packet[3] >> 4) & 0x7) - 1;
+
+	state->pressed = packet[0] >> 7;
+	if (finger1 < FOC_MAX_FINGERS) {
+		state->fingers[finger1].x += (char)packet[1];
+		state->fingers[finger1].y += (char)packet[2];
+	}
+	/*
+	 * If there is an odd number of fingers, the last relative packet only
+	 * contains one finger. In this case, the second finger index in the
+	 * packet is 0 (we subtract 1 in the lines above to create array
+	 * indices).
+	 */
+	if (finger2 != -1 && finger2 < FOC_MAX_FINGERS) {
+		state->fingers[finger2].x += (char)packet[4];
+		state->fingers[finger2].y += (char)packet[5];
+	}
+}
+
+static void focaltech_process_packet(struct psmouse *psmouse)
+{
+	struct focaltech_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+
+	switch (packet[0] & 0xf) {
+	case FOC_TOUCH:
+		process_touch_packet(&priv->state, packet);
+		break;
+	case FOC_ABS:
+		process_abs_packet(&priv->state, packet);
+		break;
+	case FOC_REL:
+		process_rel_packet(&priv->state, packet);
+		break;
+	default:
+		psmouse_err(psmouse, "Unknown packet type: %02x", packet[0]);
+		break;
+	}
+
+	focaltech_report_state(psmouse);
+}
+
+static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
 {
+	if (psmouse->pktcnt >= 6) { /* Full packet received */
+		focaltech_process_packet(psmouse);
+		return PSMOUSE_FULL_PACKET;
+	}
+	/*
+	 * we might want to do some validation of the data here, but we do not
+	 * know the protocoll well enough
+	 */
+	return PSMOUSE_GOOD_DATA;
+}
+
+static void focaltech_reset(struct psmouse *psmouse)
+{
+	psmouse_info(psmouse, "focaltech_reset");
 	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
 	psmouse_reset(psmouse);
+}
+
+static int focaltech_switch_protocol(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+	param[0] = 1;
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
+		return -EIO;
  	return 0;
 }
+
+static void focaltech_disconnect(struct psmouse *psmouse)
+{
+	focaltech_reset(psmouse);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+static int focaltech_reconnect(struct psmouse *psmouse)
+{
+	focaltech_reset(psmouse);
+	if (focaltech_switch_protocol(psmouse)) {
+		psmouse_err(psmouse,
+			    "Unable to initialize the device.");
+		return -1;
+	}
+	return 0;
+}
+
+static void set_input_params(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+
+	__set_bit(EV_KEY, dev->evbit);
+	__set_bit(EV_ABS, dev->evbit);
+	input_set_abs_params(dev, ABS_X, 0, FOC_MAX_X, 0, 0);
+	input_set_abs_params(dev, ABS_Y, 0, FOC_MAX_Y, 0, 0);
+	input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
+	input_set_abs_params(dev, ABS_MT_POSITION_X, 0, FOC_MAX_X, 0, 0);
+	input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, FOC_MAX_Y, 0, 0);
+	__set_bit(BTN_TOUCH, dev->keybit);
+	__set_bit(BTN_TOOL_FINGER, dev->keybit);
+	__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+	__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+	__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+	__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+	__clear_bit(EV_REL, dev->evbit);
+	__clear_bit(REL_X, dev->relbit);
+	__clear_bit(REL_Y, dev->relbit);
+	__set_bit(BTN_LEFT, dev->keybit);
+	__clear_bit(BTN_RIGHT, dev->keybit);
+	__clear_bit(BTN_MIDDLE, dev->keybit);
+	__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
+}
+
+int focaltech_init(struct psmouse *psmouse)
+{
+	struct focaltech_data *priv;
+
+	focaltech_reset(psmouse);
+	if (focaltech_switch_protocol(psmouse)) {
+		focaltech_reset(psmouse);
+		psmouse_info(psmouse,
+			     "Unable to initialize the device.");
+		return -ENOSYS;
+	}
+
+	psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	set_input_params(psmouse);
+
+	psmouse->protocol_handler = focaltech_process_byte;
+	psmouse->pktsize = 6;
+	psmouse->disconnect = focaltech_disconnect;
+	psmouse->reconnect = focaltech_reconnect;
+	psmouse->cleanup = focaltech_reset;
+	/* resync is not supported yet */
+	psmouse->resync_time = 0;
+
+	return 0;
+}
+
+#else /* CONFIG_MOUSE_PS2_FOCALTECH */
+
+int focaltech_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+
+int focaltech_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
diff --git a/drivers/input/mouse/focaltech.h
b/drivers/input/mouse/focaltech.h
index 498650c..3c553b2 100644
--- a/drivers/input/mouse/focaltech.h
+++ b/drivers/input/mouse/focaltech.h
@@ -2,6 +2,7 @@
  * Focaltech TouchPad PS/2 mouse driver
  *
  * Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,7 +17,67 @@
 #ifndef _FOCALTECH_H
 #define _FOCALTECH_H
 +/*
+ * Packet types - the numbers are not consecutive, so we might be missing
+ * something here.
+ */
+#define FOC_TOUCH 0x3 /* bitmap of active fingers */
+#define FOC_ABS 0x6 /* absolute position of one finger */
+#define FOC_REL 0x9 /* relative position of 1-2 fingers */
+
+#define FOC_MAX_FINGERS 5
+
+#define FOC_MAX_X 2431
+#define FOC_MAX_Y 1663
+
+static inline int focaltech_invert_y(int y)
+{
+	return FOC_MAX_Y - y;
+}
+
+/*
+ * Current state of a single finger on the touchpad.
+ */
+struct focaltech_finger_state {
+	/* the touchpad has generated a touch event for the finger */
+	bool active;
+	/*
+	 * The touchpad has sent position data for the finger. Touch event
+	 * packages reset this flag for new fingers, and there is a time
+	 * between the first touch event and the following absolute position
+	 * packet for the finger where the touchpad has declared the finger to
+	 * be valid, but we do not have any valid position yet.
+	 */
+	bool valid;
+	/* absolute position (from the bottom left corner) of the finger */
+	unsigned int x;
+	unsigned int y;
+};
+
+/*
+ * Description of the current state of the touchpad hardware.
+ */
+struct focaltech_hw_state {
+	/*
+	 * The touchpad tracks the positions of the fingers for us, the array
+	 * indices correspond to the finger indices returned in the report
+	 * packages.
+	 */
+	struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
+	/*
+	 * True if the clickpad has been pressed.
+	 */
+	bool pressed;
+};
+
+struct focaltech_data {
+	struct focaltech_hw_state state;
+};
+
 int focaltech_detect(struct psmouse *psmouse, bool set_properties);
 int focaltech_init(struct psmouse *psmouse);
 +int focaltech_detect_fallback(struct psmouse *psmouse, bool
set_properties);
+int focaltech_init_fallback(struct psmouse *psmouse);
+
 #endif
diff --git a/drivers/input/mouse/psmouse-base.c
b/drivers/input/mouse/psmouse-base.c
index 26994f6..2e2b98d 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -726,6 +726,12 @@ static int psmouse_extensions(struct psmouse *psmouse,
 /* Always check for focaltech, this is safe as it uses pnp-id matching */
 	if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
 		if (!set_properties || focaltech_init(psmouse) == 0) {
+			return PSMOUSE_FOCALTECH;
+		}
+	}
+	if (psmouse_do_detect(focaltech_detect_fallback,
+			      psmouse, set_properties) == 0) {
+		if (!set_properties || focaltech_init_fallback(psmouse) == 0) {
 			/*
 			 * Not supported yet, use bare protocol.
 			 * Note that we need to also restrict
@@ -1063,6 +1069,15 @@ static const struct psmouse_protocol
psmouse_protocols[] = {
 		.alias		= "cortps",
 		.detect		= cortron_detect,
 	},
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
+	{
+		.type		= PSMOUSE_FOCALTECH,
+		.name		= "FocalTechPS/2",
+		.alias		= "focaltech",
+		.detect		= focaltech_detect,
+		.init		= focaltech_init,
+	},
+#endif
 	{
 		.type		= PSMOUSE_AUTO,
 		.name		= "auto",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index f4cf664..c2ff137 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -96,6 +96,7 @@ enum psmouse_type {
 	PSMOUSE_FSP,
 	PSMOUSE_SYNAPTICS_RELATIVE,
 	PSMOUSE_CYPRESS,
+	PSMOUSE_FOCALTECH,
 	PSMOUSE_AUTO		/* This one should always be last */
 };
 -- 1.9.1


^ permalink raw reply related

* Re: [PATCH] Input: vsxxxaa - fix code dropping bytes from queue
From: Jan-Benedict Glaw @ 2014-10-25 12:39 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <20141024223846.GA1914@dtor-ws>

[-- Attachment #1: Type: text/plain, Size: 1299 bytes --]

On Fri, 2014-10-24 15:38:46 -0700, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
> I believe the intent of the code was to drop oldest bytes from the queue,
> not the latest if we drop one byte and both latest and some oldest of we
> are dropping more than one.
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---
>  drivers/input/mouse/vsxxxaa.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
> index 3829823..abd4944 100644
> --- a/drivers/input/mouse/vsxxxaa.c
> +++ b/drivers/input/mouse/vsxxxaa.c
> @@ -128,7 +128,7 @@ static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num)
>  	if (num >= mouse->count) {
>  		mouse->count = 0;
>  	} else {
> -		memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num);
> +		memmove(mouse->buf, mouse->buf + num, BUFLEN - num);
>  		mouse->count -= num;
>  	}
>  }

Good catch!  I fucked up the error recovery path.

Acked-by: Jan-Benedict Glaw <jbglaw@lug-owl.de>

MfG, JBG

-- 
      Jan-Benedict Glaw      jbglaw@lug-owl.de              +49-172-7608481
Signature of:                     Eine Freie Meinung in einem Freien Kopf
the second  :                   für einen Freien Staat voll Freier Bürger.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Congratulation, you've won a prize
From: Admin @ 2014-10-24 12:41 UTC (permalink / raw)
  To: choosewell

[-- Attachment #1: Type: text/plain, Size: 14 bytes --]

Congratulation

[-- Attachment #2: MICROSOFT AWARD PROMOTION.doc --]
[-- Type: application/msword, Size: 53248 bytes --]

^ permalink raw reply

* Re: [PATCH] psmouse: Add some support for the FocalTech PS/2 protocol extensions.
From: Mathias Gottschlag @ 2014-10-26 19:30 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, Hans de Goede
In-Reply-To: <544B910A.6070304@gmail.com>

Am 25.10.2014 um 14:01 schrieb Mathias Gottschlag:
> Most of the protocol for these touchpads has been reverse engineered. This
> commit adds a basic multitouch-capable driver.
>
> A lot of the protocol is still unknown. Especially, we don't know how to
> identify the device yet apart from the PNP ID. The code currently compares
> known good values with all data from the touchpad which could be
usable for
> identification, so by design even minor variations will cause the driver
> to fail to detect the device and fall back to compatibility mode.
>
> The previous workaround for these devices has been left in place in case
> the driver is not compiled into the kernel or in case some other device
> with the same PNP ID is not recognized by the driver yet still has the
same
> problems with the device probing code.
> ---
>
> Hi,
>
> this patch is related to the following bugs:
> https://bugzilla.redhat.com/show_bug.cgi?id=1110011
> https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1372609
>
> It has been tested on the Asus UX303LN, UX303LA and K750L laptops. The
> latter is affected by bug 1110011 above and used to require additional
> flags before the touchpad would work. This behaviour should have been
> fixed by the previous FocalTech patch, but is still present after this
> patch. I have not yet gotten any feedback yet, but my theory is that the
> original patch failed to fix the kernel on that device. I have to admit
> though that I haven't 100% understood that patch.
Update: I failed to include one of the related patches when testing.
Everything should be fine.

Regards,
Mathias
>
>
> When reviewing, keep in mind that this is my first linux patch. I am
> using the code for quite some time now without issues, but I might have
> overlooked quite some problems.
>
> Regards,
> Mathias
>
>  drivers/input/mouse/Kconfig        |  10 ++
>  drivers/input/mouse/focaltech.c    | 326
> ++++++++++++++++++++++++++++++++++++-
>  drivers/input/mouse/focaltech.h    |  61 +++++++
>  drivers/input/mouse/psmouse-base.c |  15 ++
>  drivers/input/mouse/psmouse.h      |   1 +
>  5 files changed, 406 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
> index 366fc7a..db973e5 100644
> --- a/drivers/input/mouse/Kconfig
> +++ b/drivers/input/mouse/Kconfig
> @@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
>         If unsure, say N.
>  +config MOUSE_PS2_FOCALTECH
> +    bool "FocalTech PS/2 mouse protocol extension" if EXPERT
> +    default y
> +    depends on MOUSE_PS2
> +    help
> +      Say Y here if you have a FocalTech PS/2 TouchPad connected to
> +      your system.
> +
> +      If unsure, say Y.
> +
>  config MOUSE_SERIAL
>      tristate "Serial mouse"
>      select SERIO
> diff --git a/drivers/input/mouse/focaltech.c
> b/drivers/input/mouse/focaltech.c
> index f4d657e..ada97cc 100644
> --- a/drivers/input/mouse/focaltech.c
> +++ b/drivers/input/mouse/focaltech.c
> @@ -2,6 +2,7 @@
>   * Focaltech TouchPad PS/2 mouse driver
>   *
>   * Copyright (c) 2014 Red Hat Inc.
> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -13,15 +14,14 @@
>   * Hans de Goede <hdegoede@redhat.com>
>   */
>  -/*
> - * The Focaltech PS/2 touchpad protocol is unknown. This drivers
deals with
> - * detection only, to avoid further detection attempts confusing the
> touchpad
> - * this way it at least works in PS/2 mouse compatibility mode.
> - */
>   #include <linux/device.h>
>  #include <linux/libps2.h>
> +#include <linux/input/mt.h>
> +#include <linux/serio.h>
> +#include <linux/slab.h>
>  #include "psmouse.h"
> +#include "focaltech.h"
>   static const char * const focaltech_pnp_ids[] = {
>      "FLT0101",
> @@ -30,7 +30,14 @@ static const char * const focaltech_pnp_ids[] = {
>      NULL
>  };
>  -int focaltech_detect(struct psmouse *psmouse, bool set_properties)
> +/*
> + * Even if the kernel is built without support for Focaltech PS/2
> touchpads (or
> + * when the real driver fails to recognize the device), we still have
> to detect
> + * them in order to avoid further detection attempts confusing the
> touchpad.
> + * This way it at least works in PS/2 mouse compatibility mode.
> + */
> +
> +int focaltech_detect_fallback(struct psmouse *psmouse, bool
set_properties)
>  {
>      if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
>          return -ENODEV;
> @@ -43,10 +50,315 @@ int focaltech_detect(struct psmouse *psmouse, bool
> set_properties)
>      return 0;
>  }
>  -int focaltech_init(struct psmouse *psmouse)
> +int focaltech_init_fallback(struct psmouse *psmouse)
> +{
> +    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
> +    psmouse_reset(psmouse);
> +
> +    return 0;
> +}
> +
> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
> +
> +static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
> +        unsigned char *param)
> +{
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
> +        return -1;
> +    param[0] = 0;
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +        return -1;
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +        return -1;
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +        return -1;
> +    param[0] = reg;
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +        return -1;
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
> +        return -1;
> +    return 0;
> +}
> +
> +int focaltech_detect(struct psmouse *psmouse, bool set_properties)
> +{
> +    struct ps2dev *ps2dev = &psmouse->ps2dev;
> +    unsigned char param[3];
> +
> +    if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
> +        return -ENODEV;
> +
> +    /*
> +     * We don't know how to identify the device, so we just check the
> +     * content of all registers.
> +     */
> +    if (focaltech_read_register(ps2dev, 0, param))
> +        return -EIO;
> +    if (param[0] != 0x69 || param[1] != 0x80 || param[2] != 0x80)
> +        return -ENODEV;
> +    if (focaltech_read_register(ps2dev, 1, param))
> +        return -EIO;
> +    if (param[0] != 0x36 || param[1] != 0x53 || param[2] != 0x03)
> +        return -ENODEV;
> +    if (focaltech_read_register(ps2dev, 2, param))
> +        return -EIO;
> +    /* 0x13 and 0x0d might be the touchpad size? */
> +    if (param[0] != 0x00 || param[1] != 0x13 || param[2] != 0x0d)
> +        return -ENODEV;
> +    if (focaltech_read_register(ps2dev, 5, param))
> +        return -EIO;
> +    if (param[0] != 0x0b || param[1] != 0x03 || param[2] != 0x00)
> +        return -ENODEV;
> +    if (focaltech_read_register(ps2dev, 6, param))
> +        return -EIO;
> +    if (param[0] != 0x23 || param[1] != 0xbd || param[2] != 0xf8)
> +        return -ENODEV;
> +
> +    if (set_properties) {
> +        psmouse->vendor = "FocalTech";
> +        psmouse->name = "FocalTech Touchpad";
> +    }
> +
> +    return 0;
> +}
> +
> +static void focaltech_report_state(struct psmouse *psmouse)
> +{
> +    int i;
> +    struct focaltech_data *priv = psmouse->private;
> +    struct focaltech_hw_state *state = &priv->state;
> +    struct input_dev *dev = psmouse->dev;
> +    int finger_count = 0;
> +
> +    for (i = 0; i < FOC_MAX_FINGERS; i++) {
> +        struct focaltech_finger_state *finger = &state->fingers[i];
> +        int active = finger->active && finger->valid;
> +        input_mt_slot(dev, i);
> +        input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
> +        if (active) {
> +            finger_count++;
> +            input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
> +            input_report_abs(dev, ABS_MT_POSITION_Y,
> +                    focaltech_invert_y(finger->y));
> +        }
> +    }
> +    input_mt_report_pointer_emulation(dev, false);
> +    input_mt_report_finger_count(dev, finger_count);
> +
> +    input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
> +    input_sync(psmouse->dev);
> +}
> +
> +static void process_touch_packet(struct focaltech_hw_state *state,
> +        unsigned char *packet)
> +{
> +    int i;
> +    unsigned char fingers = packet[1];
> +
> +    state->pressed = (packet[0] >> 4) & 1;
> +    /* the second byte contains a bitmap of all fingers touching the
pad */
> +    for (i = 0; i < FOC_MAX_FINGERS; i++) {
> +        if ((fingers & 0x1) && !state->fingers[i].active) {
> +            /* we do not have a valid position for the finger yet */
> +            state->fingers[i].valid = 0;
> +        }
> +        state->fingers[i].active = fingers & 0x1;
> +        fingers >>= 1;
> +    }
> +}
> +
> +static void process_abs_packet(struct focaltech_hw_state *state,
> +        unsigned char *packet)
> +{
> +    unsigned int finger = (packet[1] >> 4) - 1;
> +
> +    state->pressed = (packet[0] >> 4) & 1;
> +    if (finger >= FOC_MAX_FINGERS)
> +        return;
> +    /*
> +     * packet[5] contains some kind of tool size in the most significant
> +     * nibble. 0xff is a special value (latching) that signals a large
> +     * contact area.
> +     */
> +    if (packet[5] == 0xff)
> +        return;
> +    state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
> +    state->fingers[finger].y = (packet[3] << 8) | packet[4];
> +    state->fingers[finger].valid = 1;
> +}
> +static void process_rel_packet(struct focaltech_hw_state *state,
> +        unsigned char *packet)
> +{
> +    int finger1 = ((packet[0] >> 4) & 0x7) - 1;
> +    int finger2 = ((packet[3] >> 4) & 0x7) - 1;
> +
> +    state->pressed = packet[0] >> 7;
> +    if (finger1 < FOC_MAX_FINGERS) {
> +        state->fingers[finger1].x += (char)packet[1];
> +        state->fingers[finger1].y += (char)packet[2];
> +    }
> +    /*
> +     * If there is an odd number of fingers, the last relative packet
only
> +     * contains one finger. In this case, the second finger index in the
> +     * packet is 0 (we subtract 1 in the lines above to create array
> +     * indices).
> +     */
> +    if (finger2 != -1 && finger2 < FOC_MAX_FINGERS) {
> +        state->fingers[finger2].x += (char)packet[4];
> +        state->fingers[finger2].y += (char)packet[5];
> +    }
> +}
> +
> +static void focaltech_process_packet(struct psmouse *psmouse)
> +{
> +    struct focaltech_data *priv = psmouse->private;
> +    unsigned char *packet = psmouse->packet;
> +
> +    switch (packet[0] & 0xf) {
> +    case FOC_TOUCH:
> +        process_touch_packet(&priv->state, packet);
> +        break;
> +    case FOC_ABS:
> +        process_abs_packet(&priv->state, packet);
> +        break;
> +    case FOC_REL:
> +        process_rel_packet(&priv->state, packet);
> +        break;
> +    default:
> +        psmouse_err(psmouse, "Unknown packet type: %02x", packet[0]);
> +        break;
> +    }
> +
> +    focaltech_report_state(psmouse);
> +}
> +
> +static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
>  {
> +    if (psmouse->pktcnt >= 6) { /* Full packet received */
> +        focaltech_process_packet(psmouse);
> +        return PSMOUSE_FULL_PACKET;
> +    }
> +    /*
> +     * we might want to do some validation of the data here, but we
do not
> +     * know the protocoll well enough
> +     */
> +    return PSMOUSE_GOOD_DATA;
> +}
> +
> +static void focaltech_reset(struct psmouse *psmouse)
> +{
> +    psmouse_info(psmouse, "focaltech_reset");
>      ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
>      psmouse_reset(psmouse);
> +}
> +
> +static int focaltech_switch_protocol(struct psmouse *psmouse)
> +{
> +    struct ps2dev *ps2dev = &psmouse->ps2dev;
> +    unsigned char param[3];
> +
> +    param[0] = 0;
> +    if (ps2_command(ps2dev, param, 0x10f8))
> +        return -EIO;
> +    if (ps2_command(ps2dev, param, 0x10f8))
> +        return -EIO;
> +    if (ps2_command(ps2dev, param, 0x10f8))
> +        return -EIO;
> +    param[0] = 1;
> +    if (ps2_command(ps2dev, param, 0x10f8))
> +        return -EIO;
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
> +        return -EIO;
> +
> +    if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
> +        return -EIO;
>       return 0;
>  }
> +
> +static void focaltech_disconnect(struct psmouse *psmouse)
> +{
> +    focaltech_reset(psmouse);
> +    kfree(psmouse->private);
> +    psmouse->private = NULL;
> +}
> +
> +static int focaltech_reconnect(struct psmouse *psmouse)
> +{
> +    focaltech_reset(psmouse);
> +    if (focaltech_switch_protocol(psmouse)) {
> +        psmouse_err(psmouse,
> +                "Unable to initialize the device.");
> +        return -1;
> +    }
> +    return 0;
> +}
> +
> +static void set_input_params(struct psmouse *psmouse)
> +{
> +    struct input_dev *dev = psmouse->dev;
> +
> +    __set_bit(EV_KEY, dev->evbit);
> +    __set_bit(EV_ABS, dev->evbit);
> +    input_set_abs_params(dev, ABS_X, 0, FOC_MAX_X, 0, 0);
> +    input_set_abs_params(dev, ABS_Y, 0, FOC_MAX_Y, 0, 0);
> +    input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
> +    input_set_abs_params(dev, ABS_MT_POSITION_X, 0, FOC_MAX_X, 0, 0);
> +    input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, FOC_MAX_Y, 0, 0);
> +    __set_bit(BTN_TOUCH, dev->keybit);
> +    __set_bit(BTN_TOOL_FINGER, dev->keybit);
> +    __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
> +    __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
> +    __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
> +    __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
> +    __clear_bit(EV_REL, dev->evbit);
> +    __clear_bit(REL_X, dev->relbit);
> +    __clear_bit(REL_Y, dev->relbit);
> +    __set_bit(BTN_LEFT, dev->keybit);
> +    __clear_bit(BTN_RIGHT, dev->keybit);
> +    __clear_bit(BTN_MIDDLE, dev->keybit);
> +    __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
> +}
> +
> +int focaltech_init(struct psmouse *psmouse)
> +{
> +    struct focaltech_data *priv;
> +
> +    focaltech_reset(psmouse);
> +    if (focaltech_switch_protocol(psmouse)) {
> +        focaltech_reset(psmouse);
> +        psmouse_info(psmouse,
> +                 "Unable to initialize the device.");
> +        return -ENOSYS;
> +    }
> +
> +    psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
> GFP_KERNEL);
> +    if (!priv)
> +        return -ENOMEM;
> +
> +    set_input_params(psmouse);
> +
> +    psmouse->protocol_handler = focaltech_process_byte;
> +    psmouse->pktsize = 6;
> +    psmouse->disconnect = focaltech_disconnect;
> +    psmouse->reconnect = focaltech_reconnect;
> +    psmouse->cleanup = focaltech_reset;
> +    /* resync is not supported yet */
> +    psmouse->resync_time = 0;
> +
> +    return 0;
> +}
> +
> +#else /* CONFIG_MOUSE_PS2_FOCALTECH */
> +
> +int focaltech_detect(struct psmouse *psmouse, bool set_properties)
> +{
> +    return -ENOSYS;
> +}
> +
> +int focaltech_init(struct psmouse *psmouse)
> +{
> +    return -ENOSYS;
> +}
> +
> +#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
> diff --git a/drivers/input/mouse/focaltech.h
> b/drivers/input/mouse/focaltech.h
> index 498650c..3c553b2 100644
> --- a/drivers/input/mouse/focaltech.h
> +++ b/drivers/input/mouse/focaltech.h
> @@ -2,6 +2,7 @@
>   * Focaltech TouchPad PS/2 mouse driver
>   *
>   * Copyright (c) 2014 Red Hat Inc.
> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -16,7 +17,67 @@
>  #ifndef _FOCALTECH_H
>  #define _FOCALTECH_H
>  +/*
> + * Packet types - the numbers are not consecutive, so we might be missing
> + * something here.
> + */
> +#define FOC_TOUCH 0x3 /* bitmap of active fingers */
> +#define FOC_ABS 0x6 /* absolute position of one finger */
> +#define FOC_REL 0x9 /* relative position of 1-2 fingers */
> +
> +#define FOC_MAX_FINGERS 5
> +
> +#define FOC_MAX_X 2431
> +#define FOC_MAX_Y 1663
> +
> +static inline int focaltech_invert_y(int y)
> +{
> +    return FOC_MAX_Y - y;
> +}
> +
> +/*
> + * Current state of a single finger on the touchpad.
> + */
> +struct focaltech_finger_state {
> +    /* the touchpad has generated a touch event for the finger */
> +    bool active;
> +    /*
> +     * The touchpad has sent position data for the finger. Touch event
> +     * packages reset this flag for new fingers, and there is a time
> +     * between the first touch event and the following absolute position
> +     * packet for the finger where the touchpad has declared the
finger to
> +     * be valid, but we do not have any valid position yet.
> +     */
> +    bool valid;
> +    /* absolute position (from the bottom left corner) of the finger */
> +    unsigned int x;
> +    unsigned int y;
> +};
> +
> +/*
> + * Description of the current state of the touchpad hardware.
> + */
> +struct focaltech_hw_state {
> +    /*
> +     * The touchpad tracks the positions of the fingers for us, the array
> +     * indices correspond to the finger indices returned in the report
> +     * packages.
> +     */
> +    struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
> +    /*
> +     * True if the clickpad has been pressed.
> +     */
> +    bool pressed;
> +};
> +
> +struct focaltech_data {
> +    struct focaltech_hw_state state;
> +};
> +
>  int focaltech_detect(struct psmouse *psmouse, bool set_properties);
>  int focaltech_init(struct psmouse *psmouse);
>  +int focaltech_detect_fallback(struct psmouse *psmouse, bool
> set_properties);
> +int focaltech_init_fallback(struct psmouse *psmouse);
> +
>  #endif
> diff --git a/drivers/input/mouse/psmouse-base.c
> b/drivers/input/mouse/psmouse-base.c
> index 26994f6..2e2b98d 100644
> --- a/drivers/input/mouse/psmouse-base.c
> +++ b/drivers/input/mouse/psmouse-base.c
> @@ -726,6 +726,12 @@ static int psmouse_extensions(struct psmouse
*psmouse,
>  /* Always check for focaltech, this is safe as it uses pnp-id matching */
>      if (psmouse_do_detect(focaltech_detect, psmouse, set_properties)
== 0) {
>          if (!set_properties || focaltech_init(psmouse) == 0) {
> +            return PSMOUSE_FOCALTECH;
> +        }
> +    }
> +    if (psmouse_do_detect(focaltech_detect_fallback,
> +                  psmouse, set_properties) == 0) {
> +        if (!set_properties || focaltech_init_fallback(psmouse) == 0) {
>              /*
>               * Not supported yet, use bare protocol.
>               * Note that we need to also restrict
> @@ -1063,6 +1069,15 @@ static const struct psmouse_protocol
> psmouse_protocols[] = {
>          .alias        = "cortps",
>          .detect        = cortron_detect,
>      },
> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
> +    {
> +        .type        = PSMOUSE_FOCALTECH,
> +        .name        = "FocalTechPS/2",
> +        .alias        = "focaltech",
> +        .detect        = focaltech_detect,
> +        .init        = focaltech_init,
> +    },
> +#endif
>      {
>          .type        = PSMOUSE_AUTO,
>          .name        = "auto",
> diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
> index f4cf664..c2ff137 100644
> --- a/drivers/input/mouse/psmouse.h
> +++ b/drivers/input/mouse/psmouse.h
> @@ -96,6 +96,7 @@ enum psmouse_type {
>      PSMOUSE_FSP,
>      PSMOUSE_SYNAPTICS_RELATIVE,
>      PSMOUSE_CYPRESS,
> +    PSMOUSE_FOCALTECH,
>      PSMOUSE_AUTO        /* This one should always be last */
>  };
>  -- 1.9.1
>



^ permalink raw reply

* [PATCH 00/11] treewide: mask then shift defects and style updates
From: Joe Perches @ 2014-10-27  5:24 UTC (permalink / raw)
  To: linux-kernel, linux-nvme, dri-devel, ivtv-devel, linux-media,
	patches, netdev, linux-usb, linux-parisc
  Cc: alsa-devel, linux-wireless, linux-input

logical mask has lower precedence than shift but should be
done before the shift so parentheses are generally required.

And when masking with a fixed value after a shift, normal kernel
style has the shift on the left, then the shift on the right so
convert a few non-conforming uses.

Joe Perches (11):
  block: nvme-scsi: Fix probable mask then right shift defects
  radeon: evergreen: Fix probable mask then right shift defects
  aiptek: Fix probable mask then right shift defects
  dvb-net: Fix probable mask then right shift defects
  cx25840/cx18: Use standard ordering of mask and shift
  wm8350-core: Fix probable mask then right shift defect
  iwlwifi: dvm: Fix probable mask then right shift defect
  ssb: driver_chip_comon_pmu: Fix probable mask then right shift defect
  tty: ipwireless: Fix probable mask then right shift defects
  hwa-hc: Fix probable mask then right shift defect
  sound: ad1889: Fix probable mask then right shift defects

 drivers/block/nvme-scsi.c                | 12 ++++++------
 drivers/gpu/drm/radeon/evergreen.c       |  3 ++-
 drivers/input/tablet/aiptek.c            |  6 +++---
 drivers/media/dvb-core/dvb_net.c         |  4 +++-
 drivers/media/i2c/cx25840/cx25840-core.c | 12 ++++++------
 drivers/media/pci/cx18/cx18-av-core.c    | 16 ++++++++--------
 drivers/mfd/wm8350-core.c                |  2 +-
 drivers/net/wireless/iwlwifi/dvm/lib.c   |  4 ++--
 drivers/ssb/driver_chipcommon_pmu.c      |  4 ++--
 drivers/tty/ipwireless/hardware.c        | 12 ++++++------
 drivers/usb/host/hwa-hc.c                |  2 +-
 sound/pci/ad1889.c                       |  8 ++++----
 12 files changed, 44 insertions(+), 41 deletions(-)

-- 
2.1.2

^ permalink raw reply

* [PATCH 03/11] aiptek: Fix probable mask then right shift defects
From: Joe Perches @ 2014-10-27  5:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dmitry Torokhov, linux-input
In-Reply-To: <cover.1414387334.git.joe@perches.com>

Precedence of & and >> is not the same and is not left to right.
shift has higher precedence and should be done after the mask.

Here the shifts are unnecessary and a non-zero value can be used
as the test to set 1 or zero.

Signed-off-by: Joe Perches <joe@perches.com>
---
 drivers/input/tablet/aiptek.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
index e7f966d..dee2bb9 100644
--- a/drivers/input/tablet/aiptek.c
+++ b/drivers/input/tablet/aiptek.c
@@ -489,9 +489,9 @@ static void aiptek_irq(struct urb *urb)
 			 */
 			jitterable = data[1] & 0x07;
 
-			left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0;
-			right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0;
-			middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0;
+			left = data[1] & aiptek->curSetting.mouseButtonLeft ? 1 : 0;
+			right = data[1] & aiptek->curSetting.mouseButtonRight ? 1 : 0;
+			middle = data[1] & aiptek->curSetting.mouseButtonMiddle ? 1 : 0;
 
 			input_report_key(inputdev, BTN_LEFT, left);
 			input_report_key(inputdev, BTN_MIDDLE, middle);
-- 
2.1.2

^ permalink raw reply related

* Re: [PATCH] psmouse: Add some support for the FocalTech PS/2 protocol extensions.
From: Hans de Goede @ 2014-10-27  9:40 UTC (permalink / raw)
  To: Mathias Gottschlag, Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <544B910A.6070304@gmail.com>

Hi Matthias,

On 10/25/2014 02:01 PM, Mathias Gottschlag wrote:
> Most of the protocol for these touchpads has been reverse engineered. This
> commit adds a basic multitouch-capable driver.
> 
> A lot of the protocol is still unknown. Especially, we don't know how to
> identify the device yet apart from the PNP ID. The code currently compares
> known good values with all data from the touchpad which could be usable for
> identification, so by design even minor variations will cause the driver
> to fail to detect the device and fall back to compatibility mode.
> 
> The previous workaround for these devices has been left in place in case
> the driver is not compiled into the kernel or in case some other device
> with the same PNP ID is not recognized by the driver yet still has the same
> problems with the device probing code.
> ---
> 
> Hi,
> 
> this patch is related to the following bugs:
> https://bugzilla.redhat.com/show_bug.cgi?id=1110011
> https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1372609
> 
> It has been tested on the Asus UX303LN, UX303LA and K750L laptops. The
> latter is affected by bug 1110011 above and used to require additional
> flags before the touchpad would work. This behaviour should have been
> fixed by the previous FocalTech patch, but is still present after this
> patch. I have not yet gotten any feedback yet, but my theory is that the
> original patch failed to fix the kernel on that device. I have to admit
> though that I haven't 100% understood that patch.
> 
> When reviewing, keep in mind that this is my first linux patch. I am
> using the code for quite some time now without issues, but I might have
> overlooked quite some problems.

First of all many thanks for working on this.

First one overall comment on the patch, I don't really like the new
detect function you are adding, and I don't see a need for it, pnp-id
detection is what windows uses, and it should be 100% reliable (famous last
words). So I would prefer to stick with with only the pnp-id detection.

I do agree that keeping the pnp-id detection (and falling back to bare) even
when focaltech support is not compiled in is something which we should do.

Note that likewise specifying psmouse.proto=bare should runtime disable
your new code if compiled in so the detection blurb in psmouse-base.c should
look something like this:

/* Always check for focaltech, this is safe as it uses pnp-id matching */
        if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
		if (max_proto > PSMOUSE_IMEX) {
	                if (!set_properties || focaltech_init(psmouse) == 0) {
				if (focaltech_supported())
					return PSMOUSE_FOCALTECH;
	                        /*
        	                 * Note that we need to also restrict
                	         * psmouse_max_proto so that psmouse_initialize()
                        	 * does not try to reset rate and resolution,
	                         * because even that upsets the device.
        	                 */
                	        psmouse_max_proto = PSMOUSE_PS2;
                        	return PSMOUSE_PS2;
			}
                }
        }

Where focaltech_supported() is a simple:

#ifdef CONFIG_MOUSE_PS2_FOCALTECH
bool focaltech_supported(void) {
	return true;
}
#else
bool focaltech_supported(void) {
	return false;
}
#endif

in focaltech.c (see synaptics.c, which defines synaptics_supported() in the
same way.





> 
> Regards,
> Mathias
> 
>  drivers/input/mouse/Kconfig        |  10 ++
>  drivers/input/mouse/focaltech.c    | 326
> ++++++++++++++++++++++++++++++++++++-
>  drivers/input/mouse/focaltech.h    |  61 +++++++
>  drivers/input/mouse/psmouse-base.c |  15 ++
>  drivers/input/mouse/psmouse.h      |   1 +
>  5 files changed, 406 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
> index 366fc7a..db973e5 100644
> --- a/drivers/input/mouse/Kconfig
> +++ b/drivers/input/mouse/Kconfig
> @@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
>   	  If unsure, say N.
>  +config MOUSE_PS2_FOCALTECH
> +	bool "FocalTech PS/2 mouse protocol extension" if EXPERT
> +	default y
> +	depends on MOUSE_PS2
> +	help
> +	  Say Y here if you have a FocalTech PS/2 TouchPad connected to
> +	  your system.
> +
> +	  If unsure, say Y.
> +
>  config MOUSE_SERIAL
>  	tristate "Serial mouse"
>  	select SERIO
> diff --git a/drivers/input/mouse/focaltech.c
> b/drivers/input/mouse/focaltech.c
> index f4d657e..ada97cc 100644
> --- a/drivers/input/mouse/focaltech.c
> +++ b/drivers/input/mouse/focaltech.c
> @@ -2,6 +2,7 @@
>   * Focaltech TouchPad PS/2 mouse driver
>   *
>   * Copyright (c) 2014 Red Hat Inc.
> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -13,15 +14,14 @@
>   * Hans de Goede <hdegoede@redhat.com>
>   */
>  -/*
> - * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
> - * detection only, to avoid further detection attempts confusing the
> touchpad
> - * this way it at least works in PS/2 mouse compatibility mode.
> - */
>   #include <linux/device.h>
>  #include <linux/libps2.h>
> +#include <linux/input/mt.h>
> +#include <linux/serio.h>
> +#include <linux/slab.h>
>  #include "psmouse.h"
> +#include "focaltech.h"
>   static const char * const focaltech_pnp_ids[] = {
>  	"FLT0101",
> @@ -30,7 +30,14 @@ static const char * const focaltech_pnp_ids[] = {
>  	NULL
>  };
>  -int focaltech_detect(struct psmouse *psmouse, bool set_properties)
> +/*
> + * Even if the kernel is built without support for Focaltech PS/2
> touchpads (or
> + * when the real driver fails to recognize the device), we still have
> to detect
> + * them in order to avoid further detection attempts confusing the
> touchpad.
> + * This way it at least works in PS/2 mouse compatibility mode.
> + */
> +
> +int focaltech_detect_fallback(struct psmouse *psmouse, bool set_properties)
>  {
>  	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
>  		return -ENODEV;
> @@ -43,10 +50,315 @@ int focaltech_detect(struct psmouse *psmouse, bool
> set_properties)
>  	return 0;
>  }
>  -int focaltech_init(struct psmouse *psmouse)
> +int focaltech_init_fallback(struct psmouse *psmouse)
> +{
> +	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
> +	psmouse_reset(psmouse);
> +
> +	return 0;
> +}
> +

As discussed please keep this named focaltech_init, and make this version
#ifndef CONFIG_MOUSE_PS2_FOCALTECH (or put it in the #else block.

Also you may want to make this use the newly defined focaltech_reset()
function (which would then be outside of #ifdef CONFIG_MOUSE_PS2_FOCALTECH)

> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
> +
> +static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
> +		unsigned char *param)
> +{
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
> +		return -1;
> +	param[0] = 0;
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +		return -1;
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +		return -1;
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +		return -1;
> +	param[0] = reg;
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
> +		return -1;
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
> +		return -1;
> +	return 0;
> +}
> +

If you drop the register contents check, you can drop this as well,
may be useful for future reference though, so may make it #if 0 ... #endif.
Dmitry ?


> +int focaltech_detect(struct psmouse *psmouse, bool set_properties)
> +{
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	unsigned char param[3];
> +
> +	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
> +		return -ENODEV;
> +
> +	/*
> +	 * We don't know how to identify the device, so we just check the
> +	 * content of all registers.
> +	 */
> +	if (focaltech_read_register(ps2dev, 0, param))
> +		return -EIO;
> +	if (param[0] != 0x69 || param[1] != 0x80 || param[2] != 0x80)
> +		return -ENODEV;
> +	if (focaltech_read_register(ps2dev, 1, param))
> +		return -EIO;
> +	if (param[0] != 0x36 || param[1] != 0x53 || param[2] != 0x03)
> +		return -ENODEV;
> +	if (focaltech_read_register(ps2dev, 2, param))
> +		return -EIO;
> +	/* 0x13 and 0x0d might be the touchpad size? */
> +	if (param[0] != 0x00 || param[1] != 0x13 || param[2] != 0x0d)
> +		return -ENODEV;
> +	if (focaltech_read_register(ps2dev, 5, param))
> +		return -EIO;
> +	if (param[0] != 0x0b || param[1] != 0x03 || param[2] != 0x00)
> +		return -ENODEV;
> +	if (focaltech_read_register(ps2dev, 6, param))
> +		return -EIO;
> +	if (param[0] != 0x23 || param[1] != 0xbd || param[2] != 0xf8)
> +		return -ENODEV;
> +
> +	if (set_properties) {
> +		psmouse->vendor = "FocalTech";
> +		psmouse->name = "FocalTech Touchpad";
> +	}
> +
> +	return 0;
> +}
> +
> +static void focaltech_report_state(struct psmouse *psmouse)
> +{
> +	int i;
> +	struct focaltech_data *priv = psmouse->private;
> +	struct focaltech_hw_state *state = &priv->state;
> +	struct input_dev *dev = psmouse->dev;
> +	int finger_count = 0;
> +
> +	for (i = 0; i < FOC_MAX_FINGERS; i++) {
> +		struct focaltech_finger_state *finger = &state->fingers[i];
> +		int active = finger->active && finger->valid;
> +		input_mt_slot(dev, i);
> +		input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
> +		if (active) {
> +			finger_count++;
> +			input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
> +			input_report_abs(dev, ABS_MT_POSITION_Y,
> +					focaltech_invert_y(finger->y));
> +		}
> +	}
> +	input_mt_report_pointer_emulation(dev, false);
> +	input_mt_report_finger_count(dev, finger_count);

These 2 lines should be replace with:
	input_mt_report_pointer_emulation(dev, finger_count);

So that BTN_TOUCH properly gets set when at least one finger is down (this is
mandatory when not reporting pressure).

Also I see that you are not using input_mt_assign_slots, so I take it that
if put 2 fingers down and then lift the first finger down, the second finger
stays in slot->fingers[1] ? Because if it does not then you need to use
input_mt_assign_slots.

> +
> +	input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
> +	input_sync(psmouse->dev);
> +}
> +
> +static void process_touch_packet(struct focaltech_hw_state *state,
> +		unsigned char *packet)
> +{
> +	int i;
> +	unsigned char fingers = packet[1];
> +
> +	state->pressed = (packet[0] >> 4) & 1;
> +	/* the second byte contains a bitmap of all fingers touching the pad */
> +	for (i = 0; i < FOC_MAX_FINGERS; i++) {
> +		if ((fingers & 0x1) && !state->fingers[i].active) {
> +			/* we do not have a valid position for the finger yet */
> +			state->fingers[i].valid = 0;
> +		}
> +		state->fingers[i].active = fingers & 0x1;
> +		fingers >>= 1;
> +	}
> +}
> +
> +static void process_abs_packet(struct focaltech_hw_state *state,
> +		unsigned char *packet)
> +{
> +	unsigned int finger = (packet[1] >> 4) - 1;
> +
> +	state->pressed = (packet[0] >> 4) & 1;
> +	if (finger >= FOC_MAX_FINGERS)
> +		return;
> +	/*
> +	 * packet[5] contains some kind of tool size in the most significant
> +	 * nibble. 0xff is a special value (latching) that signals a large
> +	 * contact area.
> +	 */
> +	if (packet[5] == 0xff)
> +		return;

Maybe set state->fingers[finger].valid = 0 here ?

> +	state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
> +	state->fingers[finger].y = (packet[3] << 8) | packet[4];
> +	state->fingers[finger].valid = 1;
> +}
> +static void process_rel_packet(struct focaltech_hw_state *state,
> +		unsigned char *packet)
> +{
> +	int finger1 = ((packet[0] >> 4) & 0x7) - 1;
> +	int finger2 = ((packet[3] >> 4) & 0x7) - 1;
> +
> +	state->pressed = packet[0] >> 7;
> +	if (finger1 < FOC_MAX_FINGERS) {
> +		state->fingers[finger1].x += (char)packet[1];
> +		state->fingers[finger1].y += (char)packet[2];
> +	}
> +	/*
> +	 * If there is an odd number of fingers, the last relative packet only
> +	 * contains one finger. In this case, the second finger index in the
> +	 * packet is 0 (we subtract 1 in the lines above to create array
> +	 * indices).
> +	 */
> +	if (finger2 != -1 && finger2 < FOC_MAX_FINGERS) {
> +		state->fingers[finger2].x += (char)packet[4];
> +		state->fingers[finger2].y += (char)packet[5];
> +	}
> +}
> +
> +static void focaltech_process_packet(struct psmouse *psmouse)
> +{
> +	struct focaltech_data *priv = psmouse->private;
> +	unsigned char *packet = psmouse->packet;
> +
> +	switch (packet[0] & 0xf) {
> +	case FOC_TOUCH:
> +		process_touch_packet(&priv->state, packet);
> +		break;
> +	case FOC_ABS:
> +		process_abs_packet(&priv->state, packet);
> +		break;
> +	case FOC_REL:
> +		process_rel_packet(&priv->state, packet);
> +		break;
> +	default:
> +		psmouse_err(psmouse, "Unknown packet type: %02x", packet[0]);
> +		break;
> +	}
> +
> +	focaltech_report_state(psmouse);
> +}
> +
> +static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
>  {
> +	if (psmouse->pktcnt >= 6) { /* Full packet received */
> +		focaltech_process_packet(psmouse);
> +		return PSMOUSE_FULL_PACKET;
> +	}
> +	/*
> +	 * we might want to do some validation of the data here, but we do not
> +	 * know the protocoll well enough
> +	 */
> +	return PSMOUSE_GOOD_DATA;
> +}
> +
> +static void focaltech_reset(struct psmouse *psmouse)
> +{
> +	psmouse_info(psmouse, "focaltech_reset");
>  	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
>  	psmouse_reset(psmouse);
> +}
> +
> +static int focaltech_switch_protocol(struct psmouse *psmouse)
> +{
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	unsigned char param[3];
> +
> +	param[0] = 0;
> +	if (ps2_command(ps2dev, param, 0x10f8))
> +		return -EIO;
> +	if (ps2_command(ps2dev, param, 0x10f8))
> +		return -EIO;
> +	if (ps2_command(ps2dev, param, 0x10f8))
> +		return -EIO;
> +	param[0] = 1;
> +	if (ps2_command(ps2dev, param, 0x10f8))
> +		return -EIO;
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
> +		return -EIO;
> +
> +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
> +		return -EIO;
>   	return 0;
>  }
> +
> +static void focaltech_disconnect(struct psmouse *psmouse)
> +{
> +	focaltech_reset(psmouse);
> +	kfree(psmouse->private);
> +	psmouse->private = NULL;
> +}
> +
> +static int focaltech_reconnect(struct psmouse *psmouse)
> +{
> +	focaltech_reset(psmouse);
> +	if (focaltech_switch_protocol(psmouse)) {
> +		psmouse_err(psmouse,
> +			    "Unable to initialize the device.");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void set_input_params(struct psmouse *psmouse)
> +{
> +	struct input_dev *dev = psmouse->dev;
> +
> +	__set_bit(EV_KEY, dev->evbit);
> +	__set_bit(EV_ABS, dev->evbit);
> +	input_set_abs_params(dev, ABS_X, 0, FOC_MAX_X, 0, 0);
> +	input_set_abs_params(dev, ABS_Y, 0, FOC_MAX_Y, 0, 0);
> +	input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
> +	input_set_abs_params(dev, ABS_MT_POSITION_X, 0, FOC_MAX_X, 0, 0);
> +	input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, FOC_MAX_Y, 0, 0);
> +	__set_bit(BTN_TOUCH, dev->keybit);
> +	__set_bit(BTN_TOOL_FINGER, dev->keybit);
> +	__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
> +	__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
> +	__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
> +	__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
> +	__clear_bit(EV_REL, dev->evbit);
> +	__clear_bit(REL_X, dev->relbit);
> +	__clear_bit(REL_Y, dev->relbit);
> +	__set_bit(BTN_LEFT, dev->keybit);
> +	__clear_bit(BTN_RIGHT, dev->keybit);
> +	__clear_bit(BTN_MIDDLE, dev->keybit);
> +	__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
> +}
> +
> +int focaltech_init(struct psmouse *psmouse)
> +{
> +	struct focaltech_data *priv;
> +
> +	focaltech_reset(psmouse);
> +	if (focaltech_switch_protocol(psmouse)) {
> +		focaltech_reset(psmouse);
> +		psmouse_info(psmouse,
> +			     "Unable to initialize the device.");
> +		return -ENOSYS;
> +	}
> +
> +	psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
> GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	set_input_params(psmouse);
> +
> +	psmouse->protocol_handler = focaltech_process_byte;
> +	psmouse->pktsize = 6;
> +	psmouse->disconnect = focaltech_disconnect;
> +	psmouse->reconnect = focaltech_reconnect;
> +	psmouse->cleanup = focaltech_reset;
> +	/* resync is not supported yet */
> +	psmouse->resync_time = 0;
> +
> +	return 0;
> +}
> +
> +#else /* CONFIG_MOUSE_PS2_FOCALTECH */
> +
> +int focaltech_detect(struct psmouse *psmouse, bool set_properties)
> +{
> +	return -ENOSYS;
> +}
> +
> +int focaltech_init(struct psmouse *psmouse)
> +{
> +	return -ENOSYS;
> +}
> +
> +#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
> diff --git a/drivers/input/mouse/focaltech.h
> b/drivers/input/mouse/focaltech.h
> index 498650c..3c553b2 100644
> --- a/drivers/input/mouse/focaltech.h
> +++ b/drivers/input/mouse/focaltech.h
> @@ -2,6 +2,7 @@
>   * Focaltech TouchPad PS/2 mouse driver
>   *
>   * Copyright (c) 2014 Red Hat Inc.
> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -16,7 +17,67 @@
>  #ifndef _FOCALTECH_H
>  #define _FOCALTECH_H
>  +/*
> + * Packet types - the numbers are not consecutive, so we might be missing
> + * something here.
> + */
> +#define FOC_TOUCH 0x3 /* bitmap of active fingers */
> +#define FOC_ABS 0x6 /* absolute position of one finger */
> +#define FOC_REL 0x9 /* relative position of 1-2 fingers */
> +
> +#define FOC_MAX_FINGERS 5
> +
> +#define FOC_MAX_X 2431
> +#define FOC_MAX_Y 1663
> +
> +static inline int focaltech_invert_y(int y)
> +{
> +	return FOC_MAX_Y - y;
> +}
> +
> +/*
> + * Current state of a single finger on the touchpad.
> + */
> +struct focaltech_finger_state {
> +	/* the touchpad has generated a touch event for the finger */
> +	bool active;
> +	/*
> +	 * The touchpad has sent position data for the finger. Touch event
> +	 * packages reset this flag for new fingers, and there is a time
> +	 * between the first touch event and the following absolute position
> +	 * packet for the finger where the touchpad has declared the finger to
> +	 * be valid, but we do not have any valid position yet.
> +	 */
> +	bool valid;
> +	/* absolute position (from the bottom left corner) of the finger */
> +	unsigned int x;
> +	unsigned int y;
> +};
> +
> +/*
> + * Description of the current state of the touchpad hardware.
> + */
> +struct focaltech_hw_state {
> +	/*
> +	 * The touchpad tracks the positions of the fingers for us, the array
> +	 * indices correspond to the finger indices returned in the report
> +	 * packages.
> +	 */
> +	struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
> +	/*
> +	 * True if the clickpad has been pressed.
> +	 */
> +	bool pressed;
> +};
> +
> +struct focaltech_data {
> +	struct focaltech_hw_state state;
> +};
> +
>  int focaltech_detect(struct psmouse *psmouse, bool set_properties);
>  int focaltech_init(struct psmouse *psmouse);
>  +int focaltech_detect_fallback(struct psmouse *psmouse, bool
> set_properties);
> +int focaltech_init_fallback(struct psmouse *psmouse);
> +
>  #endif
> diff --git a/drivers/input/mouse/psmouse-base.c
> b/drivers/input/mouse/psmouse-base.c
> index 26994f6..2e2b98d 100644
> --- a/drivers/input/mouse/psmouse-base.c
> +++ b/drivers/input/mouse/psmouse-base.c
> @@ -726,6 +726,12 @@ static int psmouse_extensions(struct psmouse *psmouse,
>  /* Always check for focaltech, this is safe as it uses pnp-id matching */
>  	if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
>  		if (!set_properties || focaltech_init(psmouse) == 0) {
> +			return PSMOUSE_FOCALTECH;
> +		}
> +	}
> +	if (psmouse_do_detect(focaltech_detect_fallback,
> +			      psmouse, set_properties) == 0) {
> +		if (!set_properties || focaltech_init_fallback(psmouse) == 0) {
>  			/*
>  			 * Not supported yet, use bare protocol.
>  			 * Note that we need to also restrict
> @@ -1063,6 +1069,15 @@ static const struct psmouse_protocol
> psmouse_protocols[] = {
>  		.alias		= "cortps",
>  		.detect		= cortron_detect,
>  	},
> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
> +	{
> +		.type		= PSMOUSE_FOCALTECH,
> +		.name		= "FocalTechPS/2",
> +		.alias		= "focaltech",
> +		.detect		= focaltech_detect,
> +		.init		= focaltech_init,
> +	},
> +#endif
>  	{
>  		.type		= PSMOUSE_AUTO,
>  		.name		= "auto",
> diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
> index f4cf664..c2ff137 100644
> --- a/drivers/input/mouse/psmouse.h
> +++ b/drivers/input/mouse/psmouse.h
> @@ -96,6 +96,7 @@ enum psmouse_type {
>  	PSMOUSE_FSP,
>  	PSMOUSE_SYNAPTICS_RELATIVE,
>  	PSMOUSE_CYPRESS,
> +	PSMOUSE_FOCALTECH,
>  	PSMOUSE_AUTO		/* This one should always be last */
>  };

Overall this looks very good, many thanks for your work on this!

Regards,

Hans


^ permalink raw reply

* [PATCH v2] input: fix BTN_TOUCH reporting in input_mt_report_pointer_emulation
From: Chung-yih Wang @ 2014-10-27 10:08 UTC (permalink / raw)
  To: linux-input
  Cc: linux-kernel, Henrik Rydberg, Dmitry Torokhov, benjamin.tissoires,
	Chung-yih Wang
In-Reply-To: <1412861506-21732-1-git-send-email-cywang@chromium.org>

  From the definition of BTN_TOUCH, BTN_TOOL_<name> and BTN_TOUCH codes
are orthogonal. BTN_TOUCH should be zero if there is no physical contact
happened on device. With ABS_MT_DISTANCE information, the patch uses
touch_count and finger_count to get the final reporting code for
BTN_TOUCH and BTN_TOOL_<name>, respectively.

Signed-off-by: Chung-yih Wang <cywang@chromium.org>
---
 drivers/input/input-mt.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index fbe29fc..a2ab8e3 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -192,18 +192,21 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 {
 	struct input_mt *mt = dev->mt;
 	struct input_mt_slot *oldest;
-	int oldid, count, i;
+	int oldid, i;
+	int touch_count, finger_count;
 
 	if (!mt)
 		return;
 
 	oldest = NULL;
 	oldid = mt->trkid;
-	count = 0;
+	touch_count = 0;
+	finger_count = 0;
 
 	for (i = 0; i < mt->num_slots; ++i) {
 		struct input_mt_slot *ps = &mt->slots[i];
 		int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
+		int distance = input_mt_get_value(ps, ABS_MT_DISTANCE);
 
 		if (id < 0)
 			continue;
@@ -211,12 +214,14 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 			oldest = ps;
 			oldid = id;
 		}
-		count++;
+		finger_count++;
+		if (distance == 0)
+			touch_count++;
 	}
 
-	input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
+	input_event(dev, EV_KEY, BTN_TOUCH, touch_count > 0);
 	if (use_count)
-		input_mt_report_finger_count(dev, count);
+		input_mt_report_finger_count(dev, finger_count);
 
 	if (oldest) {
 		int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
-- 
2.1.2


^ permalink raw reply related

* RE: [PATCH] Alps source code update
From: Masaki Ota @ 2014-10-27 10:28 UTC (permalink / raw)
  To: Niels de Vos, Masaki Ota
  Cc: dmitry.torokhov@gmail.com, cernekee@gmail.com,
	dturvene@dahetral.com, linux-input@vger.kernel.org,
	jclift@redhat.com
In-Reply-To: <20141024071852.GA10616@ndevos-x240.usersys.redhat.com>

Hi, Niels,

Thank you for reply.
I will split this modification and post it again.

Best Regards,
================================
Masaki Ota
Alps Electric Co., Ltd.
Engineering Dept.M8
Tel:+86-21-5081-7575 ext:522
Fax:+86-21-5081-5252
E-mail  masaki.ota@jp.alps.com
================================

________________________________________
差出人: Niels de Vos <ndevos@redhat.com>
送信日時: 2014年10月24日 16:18
宛先: Masaki Ota
CC: dmitry.torokhov@gmail.com; cernekee@gmail.com; dturvene@dahetral.com; linux-input@vger.kernel.org; jclift@redhat.com; 太田 真喜 Masaki Ota
件名: Re: [PATCH] Alps source code update

On Fri, Oct 24, 2014 at 06:21:23AM +0800, Masaki Ota wrote:
> Signed-off-by: Masaki Ota <masaki.ota@jp.alps.com>
> - Support Alps Button-less Touchpad device(Rushmore and SS4). New device type and a data decode logic were added.

Thanks Masaki. I can't say much about the change to the alps driver
itself, but you should probably re-post this patch with a more suitable
subject and description of the change. The format would be something
like this (this text may not be correct though, it's just an example):


    [PATCH v2] input/alps: support for button-less touchpads

    Add support for Alps button-less touchpads (Rushmore and SS4). These
    devices use a different protocol, so additional decoding routines
    are added.

    These devices are found in <some laptop vendor/model(s)>.

    Signed-off-by: ...
    Reviewed-by: ... (maybe one of your colleagues?)
    Tested-by: ... (maybe one of your colleagues?)

    ---
    v2 changes:
    - reformat commit message

    <patch follows below>


Now, this is quite a big patch. It will be easier for others to review
and check your change if you can split it up in multiple logical
patches. For example, add the v8 protocol and the supported device(s) in
the 1st patch, and the v9 protocol in a 2nd one.

If you can include some laptop vendor/model(s) in the message for the
commit, we can see if there are any bugs filed in the Red Hat Bugzilla
for these. Maybe there are some users that want to do some testing and
can offer feedback.

Thanks,
Niels


> ---
>  drivers/input/mouse/alps.c | 1680 +++++++++++++++++++++++++++++++++++---------
>  drivers/input/mouse/alps.h |  263 ++++++-
>  2 files changed, 1574 insertions(+), 369 deletions(-)
>
> diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
> index a59a1a6..d4fc568 100644
> --- a/drivers/input/mouse/alps.c
> +++ b/drivers/input/mouse/alps.c
> @@ -20,6 +20,7 @@
>  #include <linux/input/mt.h>
>  #include <linux/serio.h>
>  #include <linux/libps2.h>
> +#include <linux/kernel.h>
>
>  #include "psmouse.h"
>  #include "alps.h"
> @@ -32,6 +33,8 @@
>  #define ALPS_REG_BASE_RUSHMORE       0xc2c0
>  #define ALPS_REG_BASE_PINNACLE       0x0000
>
> +#define V7_LARGE_MOVEMENT            130
> +
>  static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
>       { PSMOUSE_CMD_SETPOLL,          0x00 }, /* 0 */
>       { PSMOUSE_CMD_RESET_DIS,        0x00 }, /* 1 */
> @@ -99,8 +102,10 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
>  #define ALPS_FOUR_BUTTONS    0x40    /* 4 direction button present */
>  #define ALPS_PS2_INTERLEAVED 0x80    /* 3-byte PS/2 packet interleaved with
>                                          6-byte ALPS packet */
> -#define ALPS_IS_RUSHMORE     0x100   /* device is a rushmore */
> -#define ALPS_BUTTONPAD               0x200   /* device is a clickpad */
> +#define ALPS_BTNLESS                 0x100   /* ALPS ClickPad flag */
> +
> +#define      DOL_IS_APDATA(_BY)                      ((_BY[0]&0x01) == 0x01)
> +#define      DOL_IS_PROFDATA(_BY)            ((_BY[0]&0x20) == 0x20)
>
>  static const struct alps_model_info alps_model_data[] = {
>       { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },  /* Toshiba Salellite Pro M10 */
> @@ -142,6 +147,20 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
>   * isn't valid per PS/2 spec.
>   */
>
> +static unsigned int alps_pt_distance(struct alps_abs_data *pt0,
> +                                 struct alps_abs_data *pt1)
> +{
> +     int vect_x, vect_y;
> +
> +     if (!pt0 || !pt1)
> +             return 0;
> +
> +     vect_x = pt0->x - pt1->x;
> +     vect_y = pt0->y - pt1->y;
> +
> +     return int_sqrt(vect_x * vect_x + vect_y * vect_y);
> +}
> +
>  /* Packet formats are described in Documentation/input/alps.txt */
>
>  static bool alps_is_valid_first_byte(struct alps_data *priv,
> @@ -283,10 +302,11 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
>   *
>   * The bitmaps don't have enough data to track fingers, so this function
>   * only generates points representing a bounding box of at most two contacts.
> - * These two points are returned in fields->mt.
> + * These two points are returned in x1, y1, x2, and y2.
>   */
>  static void alps_process_bitmap_dolphin(struct alps_data *priv,
> -                                     struct alps_fields *fields)
> +                                     struct alps_fields *fields,
> +                                     int *x1, int *y1, int *x2, int *y2)
>  {
>       int box_middle_x, box_middle_y;
>       unsigned int x_map, y_map;
> @@ -309,6 +329,8 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
>       if (x_msb > priv->x_bits || y_msb > priv->y_bits)
>               return;
>
> +     *x1 = *y1 = *x2 = *y2 = 0;
> +
>       if (fields->fingers > 1) {
>               start_bit = priv->x_bits - x_msb;
>               end_bit = priv->x_bits - x_lsb;
> @@ -319,35 +341,10 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
>               end_bit = y_msb - 1;
>               box_middle_y = (priv->y_max * (start_bit + end_bit)) /
>                               (2 * (priv->y_bits - 1));
> -             fields->mt[0] = fields->st;
> -             fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x;
> -             fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y;
> -     }
> -}
> -
> -static void alps_get_bitmap_points(unsigned int map,
> -                                struct alps_bitmap_point *low,
> -                                struct alps_bitmap_point *high,
> -                                int *fingers)
> -{
> -     struct alps_bitmap_point *point;
> -     int i, bit, prev_bit = 0;
> -
> -     point = low;
> -     for (i = 0; map != 0; i++, map >>= 1) {
> -             bit = map & 1;
> -             if (bit) {
> -                     if (!prev_bit) {
> -                             point->start_bit = i;
> -                             point->num_bits = 0;
> -                             (*fingers)++;
> -                     }
> -                     point->num_bits++;
> -             } else {
> -                     if (prev_bit)
> -                             point = high;
> -             }
> -             prev_bit = bit;
> +             *x1 = fields->pt.x;
> +             *y1 = fields->pt.y;
> +             *x2 = 2 * box_middle_x - *x1;
> +             *y2 = 2 * box_middle_y - *y1;
>       }
>  }
>
> @@ -358,21 +355,71 @@ static void alps_get_bitmap_points(unsigned int map,
>   *
>   * The bitmaps don't have enough data to track fingers, so this function
>   * only generates points representing a bounding box of all contacts.
> - * These points are returned in fields->mt when the return value
> + * These points are returned in x1, y1, x2, and y2 when the return value
>   * is greater than 0.
>   */
>  static int alps_process_bitmap(struct alps_data *priv,
> -                            struct alps_fields *fields)
> +                            unsigned int x_map, unsigned int y_map,
> +                            int *x1, int *y1, int *x2, int *y2)
>  {
> -     int i, fingers_x = 0, fingers_y = 0, fingers;
> +     struct alps_bitmap_point {
> +             int start_bit;
> +             int num_bits;
> +     };
> +
> +     int fingers_x = 0, fingers_y = 0, fingers;
> +     int i, bit, prev_bit;
>       struct alps_bitmap_point x_low = {0,}, x_high = {0,};
>       struct alps_bitmap_point y_low = {0,}, y_high = {0,};
> +     struct alps_bitmap_point *point;
>
> -     if (!fields->x_map || !fields->y_map)
> +     if (!x_map || !y_map)
>               return 0;
>
> -     alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x);
> -     alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y);
> +     *x1 = *y1 = *x2 = *y2 = 0;
> +
> +     prev_bit = 0;
> +     point = &x_low;
> +     for (i = 0; x_map != 0; i++, x_map >>= 1) {
> +             bit = x_map & 1;
> +             if (bit) {
> +                     if (!prev_bit) {
> +                             point->start_bit = i;
> +                             fingers_x++;
> +                     }
> +                     point->num_bits++;
> +             } else {
> +                     if (prev_bit)
> +                             point = &x_high;
> +                     else
> +                             point->num_bits = 0;
> +             }
> +             prev_bit = bit;
> +     }
> +
> +     /*
> +      * y bitmap is reversed for what we need (lower positions are in
> +      * higher bits), so we process from the top end.
> +      */
> +     y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits);
> +     prev_bit = 0;
> +     point = &y_low;
> +     for (i = 0; y_map != 0; i++, y_map <<= 1) {
> +             bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1));
> +             if (bit) {
> +                     if (!prev_bit) {
> +                             point->start_bit = i;
> +                             fingers_y++;
> +                     }
> +                     point->num_bits++;
> +             } else {
> +                     if (prev_bit)
> +                             point = &y_high;
> +                     else
> +                             point->num_bits = 0;
> +             }
> +             prev_bit = bit;
> +     }
>
>       /*
>        * Fingers can overlap, so we use the maximum count of fingers
> @@ -381,89 +428,103 @@ static int alps_process_bitmap(struct alps_data *priv,
>       fingers = max(fingers_x, fingers_y);
>
>       /*
> -      * If an axis reports only a single contact, we have overlapping or
> -      * adjacent fingers. Divide the single contact between the two points.
> +      * If total fingers is > 1 but either axis reports only a single
> +      * contact, we have overlapping or adjacent fingers. For the
> +      * purposes of creating a bounding box, divide the single contact
> +      * (roughly) equally between the two points.
>        */
> -     if (fingers_x == 1) {
> -             i = (x_low.num_bits - 1) / 2;
> -             x_low.num_bits = x_low.num_bits - i;
> -             x_high.start_bit = x_low.start_bit + i;
> -             x_high.num_bits = max(i, 1);
> -     }
> -     if (fingers_y == 1) {
> -             i = (y_low.num_bits - 1) / 2;
> -             y_low.num_bits = y_low.num_bits - i;
> -             y_high.start_bit = y_low.start_bit + i;
> -             y_high.num_bits = max(i, 1);
> -     }
> -
> -     fields->mt[0].x =
> -             (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
> -             (2 * (priv->x_bits - 1));
> -     fields->mt[0].y =
> -             (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
> -             (2 * (priv->y_bits - 1));
> -
> -     fields->mt[1].x =
> -             (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
> -             (2 * (priv->x_bits - 1));
> -     fields->mt[1].y =
> -             (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
> -             (2 * (priv->y_bits - 1));
> -
> -     /* y-bitmap order is reversed, except on rushmore */
> -     if (!(priv->flags & ALPS_IS_RUSHMORE)) {
> -             fields->mt[0].y = priv->y_max - fields->mt[0].y;
> -             fields->mt[1].y = priv->y_max - fields->mt[1].y;
> +     if (fingers > 1) {
> +             if (fingers_x == 1) {
> +                     i = x_low.num_bits / 2;
> +                     x_low.num_bits = x_low.num_bits - i;
> +                     x_high.start_bit = x_low.start_bit + i;
> +                     x_high.num_bits = max(i, 1);
> +             } else if (fingers_y == 1) {
> +                     i = y_low.num_bits / 2;
> +                     y_low.num_bits = y_low.num_bits - i;
> +                     y_high.start_bit = y_low.start_bit + i;
> +                     y_high.num_bits = max(i, 1);
> +             }
> +     }
> +
> +     *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
> +           (2 * (priv->x_bits - 1));
> +     *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
> +           (2 * (priv->y_bits - 1));
> +
> +     if (fingers > 1) {
> +             *x2 = (priv->x_max *
> +                    (2 * x_high.start_bit + x_high.num_bits - 1)) /
> +                   (2 * (priv->x_bits - 1));
> +             *y2 = (priv->y_max *
> +                    (2 * y_high.start_bit + y_high.num_bits - 1)) /
> +                   (2 * (priv->y_bits - 1));
>       }
>
>       return fingers;
>  }
>
> -static void alps_set_slot(struct input_dev *dev, int slot, int x, int y)
> +static void alps_set_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, true);
> -     input_report_abs(dev, ABS_MT_POSITION_X, x);
> -     input_report_abs(dev, ABS_MT_POSITION_Y, 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, y);
> +     }
>  }
>
> -static void alps_report_mt_data(struct psmouse *psmouse, int n)
> +static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
> +                                  int x1, int y1, int x2, int y2)
>  {
> -     struct alps_data *priv = psmouse->private;
> -     struct input_dev *dev = psmouse->dev;
> -     struct alps_fields *f = &priv->f;
> -     int i, slot[MAX_TOUCHES];
> +     alps_set_slot(dev, 0, num_fingers != 0, x1, y1);
> +     alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
> +}
>
> -     input_mt_assign_slots(dev, slot, f->mt, n);
> -     for (i = 0; i < n; i++)
> -             alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
> +static void alps_report_semi_mt_data_ex(struct input_dev *dev, int num_fingers,
> +                                  struct alps_abs_data coord[])
> +{
> +     unsigned char i;
>
> -     input_mt_sync_frame(dev);
> +     for (i = 0; i < num_fingers; i++) {
> +             if (!coord[i].x || !coord[i].y || !coord[i].z)
> +                     alps_set_slot(dev, i, 0, coord[i].x, coord[i].y);
> +             else
> +                     alps_set_slot(dev, i, 1, coord[i].x, coord[i].y);
> +     }
>  }
>
> -static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
> +static void alps_report_coord_and_btn(struct psmouse *psmouse,
> +                                   struct alps_fields *f)
>  {
> -     struct alps_data *priv = psmouse->private;
> -     struct input_dev *dev = psmouse->dev;
> -     struct alps_fields *f = &priv->f;
> +     struct input_dev *dev;
>
> -     /* Use st data when we don't have mt data */
> -     if (fingers < 2) {
> -             f->mt[0].x = f->st.x;
> -             f->mt[0].y = f->st.y;
> -             fingers = f->pressure > 0 ? 1 : 0;
> -     }
> +     if (!psmouse || !f)
> +             return;
>
> -     alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2);
> +     dev = psmouse->dev;
>
> -     input_mt_report_finger_count(dev, fingers);
> +     if (f->fingers) {
> +             input_report_key(dev, BTN_TOUCH, 1);
> +
> +             alps_report_semi_mt_data(dev, f->fingers,
> +                     f->pt_img[0].x, f->pt_img[0].y,
> +                     f->pt_img[1].x, f->pt_img[1].y);
> +             input_mt_report_finger_count(dev, f->fingers);
>
> -     input_report_key(dev, BTN_LEFT, f->left);
> -     input_report_key(dev, BTN_RIGHT, f->right);
> -     input_report_key(dev, BTN_MIDDLE, f->middle);
> +             input_report_abs(dev, ABS_X, f->pt_img[0].x);
> +             input_report_abs(dev, ABS_Y, f->pt_img[0].y);
> +             input_report_abs(dev, ABS_PRESSURE, f->pt_img[0].z);
> +     } else {
> +             input_report_key(dev, BTN_TOUCH, 0);
> +             input_mt_report_finger_count(dev, 0);
> +             input_report_abs(dev, ABS_PRESSURE, 0);
> +     }
>
> -     input_report_abs(dev, ABS_PRESSURE, f->pressure);
> +     input_report_key(dev, BTN_LEFT, f->btn.left);
> +     input_report_key(dev, BTN_RIGHT, f->btn.right);
> +     input_report_key(dev, BTN_MIDDLE, f->btn.middle);
>
>       input_sync(dev);
>  }
> @@ -530,16 +591,25 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
>
>  static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
>  {
> -     f->left = !!(p[3] & 0x01);
> -     f->right = !!(p[3] & 0x02);
> -     f->middle = !!(p[3] & 0x04);
> +     f->btn.left = !!(p[3] & 0x01);
> +     f->btn.right = !!(p[3] & 0x02);
> +     f->btn.middle = !!(p[3] & 0x04);
>
> -     f->ts_left = !!(p[3] & 0x10);
> -     f->ts_right = !!(p[3] & 0x20);
> -     f->ts_middle = !!(p[3] & 0x40);
> +     f->btn.ts_left = !!(p[3] & 0x10);
> +     f->btn.ts_right = !!(p[3] & 0x20);
> +     f->btn.ts_middle = !!(p[3] & 0x40);
>  }
>
> -static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
> +/* proto_v9 */
> +static void alps_decode_button_ss3(struct alps_fields *f, unsigned char *p,
> +                             struct alps_data *priv)
> +{
> +     if (f->dol_packet_type == DOL_GPDATA ||
> +             f->dol_packet_type == DOL_APDATA)
> +             f->btn.left = !!(p[3]&0x01);
> +}
> +
> +static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
>                                struct psmouse *psmouse)
>  {
>       f->first_mp = !!(p[4] & 0x40);
> @@ -553,17 +623,15 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
>                  ((p[2] & 0x7f) << 1) |
>                  (p[4] & 0x01);
>
> -     f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
> +     f->pt.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
>              ((p[0] & 0x30) >> 4);
> -     f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
> -     f->pressure = p[5] & 0x7f;
> +     f->pt.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
> +     f->pt.z = p[5] & 0x7f;
>
>       alps_decode_buttons_v3(f, p);
> -
> -     return 0;
>  }
>
> -static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
> +static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
>                                struct psmouse *psmouse)
>  {
>       alps_decode_pinnacle(f, p, psmouse);
> @@ -573,25 +641,20 @@ static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
>       f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
>       f->x_map |= (p[5] & 0x10) << 11;
>       f->y_map |= (p[5] & 0x20) << 6;
> -
> -     return 0;
>  }
>
> -static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
> +static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
>                               struct psmouse *psmouse)
>  {
>       u64 palm_data = 0;
>       struct alps_data *priv = psmouse->private;
>
> -     f->first_mp = !!(p[0] & 0x02);
> -     f->is_mp = !!(p[0] & 0x20);
> +     f->is_mp = 0;
> +     f->first_mp = 0;
>
> -     if (!f->is_mp) {
> -             f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
> -             f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
> -             f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f;
> -             alps_decode_buttons_v3(f, p);
> -     } else {
> +     if (DOL_IS_PROFDATA(p)) {
> +             f->is_mp = 1;
> +             f->dol_packet_type = DOL_PROFDATA;
>               f->fingers = ((p[0] & 0x6) >> 1 |
>                    (p[0] & 0x10) >> 2);
>
> @@ -609,22 +672,415 @@ static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
>               /* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */
>               f->x_map = (palm_data >> priv->y_bits) &
>                          (BIT(priv->x_bits) - 1);
> +     } else {
> +             if (DOL_IS_APDATA(p)) {
> +                     f->dol_packet_type = DOL_APDATA;
> +                     f->fingers = 2;
> +                     f->pt_img[0].x = p[1]<<3;
> +                     f->pt_img[0].y = p[2]<<2;
> +                     f->pt_img[0].z = 64;
> +                     f->pt_img[1].x = p[4]<<3;
> +                     f->pt_img[1].y = p[5]<<2;
> +                     f->pt_img[1].z = 64;
> +             } else {/* is gp data */
> +                     f->dol_packet_type = DOL_GPDATA;
> +                     f->first_mp = !!(p[0]&0x02);
> +                     f->pt.x = f->pt_img[0].x =
> +                             ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
> +                     f->pt.y = f->pt_img[0].y =
> +                             ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
> +                     f->pt.z = f->pt_img[0].z =
> +                             min(((p[0] & 0x04) ? 0 : p[5] & 0x7f) * 2, 127);
> +
> +                     /*
> +                             Button number will be included in
> +                             the PROFILE data for a 3-f packet.
> +                             So do not change .fingers because
> +                             it will be updated in Profile data packet.
> +                     */
> +                     if (!f->first_mp)
> +                             f->fingers = (f->pt_img[0].x &&
> +                                     f->pt_img[0].y && f->pt_img[0].z)?1:0;
> +             }
> +
> +             if (priv->proto_version == ALPS_PROTO_V9)
> +                     alps_decode_button_ss3(f, p, priv);
> +             else
> +                     alps_decode_buttons_v3(f, p);
>       }
> +}
>
> -     return 0;
> +unsigned char alps_get_pkt_id_ss4_v1(char *byte)
> +{
> +     unsigned char pkt_id = SS4_PACKET_ID_IDLE;
> +
> +     if (((byte[0] & 0xFF) == 0x08) && ((byte[1] & 0xFF) == 0x10) &&
> +          ((byte[2] & 0xFF) == 0x00) && ((byte[3] & 0x8F) == 0x08) &&
> +          ((byte[4] & 0xFF) == 0x01) && ((byte[5] & 0xFF) == 0x00)) {
> +             pkt_id = SS4_PACKET_ID_IDLE;
> +     } else if (((byte[0] & 0x08) == 0x08) && ((byte[1] & 0x10) == 0x10) &&
> +             ((byte[3] & 0x8E) == 0x08) && ((byte[4] & 0x81) == 0x01)) {
> +             pkt_id = SS4_PACKET_ID_ONE;
> +     } else if (((byte[0] & 0x08) == 0x08) && ((byte[3] & 0x08) == 0x08) &&
> +               ((byte[4] & 0x01) == 0x01)) {
> +             if (((byte[5] & 0x01) == 0x01))
> +                     pkt_id = SS4_PACKET_ID_TWO;
> +             else
> +                     pkt_id = SS4_PACKET_ID_MULTI;
> +     }
> +
> +     return pkt_id;
> +}
> +
> +unsigned char alps_get_pkt_id_ss4_v2(char *byte)
> +{
> +     unsigned char pkt_id = SS4_PACKET_ID_IDLE;
> +
> +     if (((byte[0] & 0xFF) == 0x18) && ((byte[1] & 0xFF) == 0x10) &&
> +          ((byte[2] & 0xFF) == 0x00) && ((byte[3] & 0x88) == 0x08) &&
> +          ((byte[4] & 0xFF) == 0x10) && ((byte[5] & 0xFF) == 0x00)) {
> +             pkt_id = SS4_PACKET_ID_IDLE;
> +     } else if (!(byte[3] & 0x10)) {
> +             pkt_id = SS4_PACKET_ID_ONE;
> +     } else {
> +             if (!(byte[3] & 0x20))
> +                     pkt_id = SS4_PACKET_ID_TWO;
> +             else
> +                     pkt_id = SS4_PACKET_ID_MULTI;
> +     }
> +
> +     return pkt_id;
> +}
> +
> +static void alps_process_btnless_click(struct psmouse *psmouse,
> +                             struct alps_fields *f)
> +{
> +     struct alps_data *priv = psmouse->private;
> +
> +     if (!f->btn.left)
> +             return;
> +
> +     /* Clear button flag */
> +     f->btn.left = 0;
> +
> +     switch (f->fingers) {
> +     case 1:
> +             /* In Left Resting Area */
> +             if (PT_IN_LEFT_BTN_AREA(f->pt_img[0].x,
> +                             f->pt_img[0].y, priv->x_max, priv->y_max)) {
> +                     f->btn.left = 1;
> +             } else if (PT_IN_RIGHT_BTN_AREA(f->pt_img[0].x,
> +                             f->pt_img[0].y, priv->x_max, priv->y_max)) {
> +                     /* In Right Resting Area */
> +                     f->btn.right = 1;
> +             } else {
> +                     /* In Normal area */
> +                     f->btn.left = 1;
> +             }
> +             break;
> +
> +     case 2:
> +             /* Both two fingers are in Normal area */
> +             if (!PT_IN_BTN_AREA(f->pt_img[0].x,
> +                             f->pt_img[0].y, priv->x_max, priv->y_max) &&
> +                             !PT_IN_BTN_AREA(f->pt_img[1].x,
> +                             f->pt_img[1].y, priv->x_max, priv->y_max)) {
> +                     f->btn.right = 1;
> +             } else if (PT_IN_RIGHT_BTN_AREA(f->pt_img[0].x,
> +                             f->pt_img[0].y, priv->x_max, priv->y_max) ||
> +                             PT_IN_RIGHT_BTN_AREA(f->pt_img[1].x,
> +                             f->pt_img[1].y, priv->x_max, priv->y_max)) {
> +                     /* Either one finger is in Right Area */
> +                     f->btn.right = 1;
> +             } else {
> +                     f->btn.left = 1;
> +             }
> +             break;
> +
> +     case 3:
> +             f->btn.middle = 1;
> +             break;
> +
> +     case 0:
> +     default:
> +             break;
> +     }
> +}
> +
> +static void alps_process_resting_finger(struct psmouse *psmouse,
> +     struct alps_fields *f, struct alps_abs_data *output,
> +     unsigned char *p_fn)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     static struct alps_abs_data prev_pt[10];
> +     static struct alps_abs_data init_pt[10];
> +     static unsigned char is_moved[10];
> +     static unsigned char prev_fn;
> +     static unsigned char prev_coord_is_output[10];
> +     unsigned char cur_coord_is_output[10];
> +     unsigned char in_resting_area[10];
> +     unsigned char i, index;
> +     unsigned char output_fn = 0;
> +
> +     memset(in_resting_area, 0, sizeof(in_resting_area));
> +     memset(cur_coord_is_output, 0, sizeof(cur_coord_is_output));
> +
> +     /* Clear "is_moved" flag when finger number changed */
> +     if (f->fingers != prev_fn) {
> +             memset(is_moved, 0, sizeof(is_moved));
> +             memcpy(init_pt, f->pt_img, sizeof(f->pt_img));
> +     }
> +
> +     /* Calculate output finger */
> +     for (i = 0, index = 0; i < f->fingers; i++) {
> +             if (is_moved[i] == 0 &&
> +                     (abs(f->pt_img[i].x - init_pt[i].x)
> +                             > RESTING_FN_LARGE_MOVEMENT)) {
> +                     is_moved[i] = 1;
> +             }
> +
> +             /* Check if in resting area */
> +             if (PT_IN_BTN_AREA(f->pt_img[i].x,
> +                             f->pt_img[i].y, priv->x_max, priv->y_max))
> +                     in_resting_area[i] = 1;
> +
> +             if (!in_resting_area[i] ||
> +                     (in_resting_area[i] && is_moved[i])) {
> +                     memcpy(&output[index++], &f->pt_img[i],
> +                             sizeof(struct alps_abs_data));
> +                     cur_coord_is_output[i] = 1;
> +                     output_fn++;
> +             }
> +     }
> +
> +     /* A normal finger becomes a resting finger */
> +     for (i = 0; i < f->fingers; i++) {
> +             if (prev_coord_is_output[i] &&
> +                     !cur_coord_is_output[i] && f->pt_img[i].z) {
> +                     output_fn = 0;
> +                     memset(output, 0,
> +                             sizeof(struct alps_abs_data)*f->fingers);
> +             }
> +     }
> +
> +     memcpy(prev_pt, f->pt_img, sizeof(f->pt_img));
> +     memcpy(prev_coord_is_output, cur_coord_is_output,
> +             sizeof(cur_coord_is_output));
> +     prev_fn = f->fingers;
> +     *p_fn = output_fn;
> +}
> +
> +static void alps_decode_ss4_v1(struct alps_fields *f,
> +                     unsigned char *p, struct psmouse *psmouse)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     unsigned char pkt_id;
> +     unsigned int no_data_x, no_data_y;
> +
> +     if (!psmouse || !f || !p)
> +             return;
> +
> +     pkt_id = alps_get_pkt_id_ss4_v1(p);
> +
> +     /* Current packet is 1Finger coordinate packet */
> +     switch (pkt_id) {
> +     case SS4_PACKET_ID_ONE:
> +             f->pt_img[0].x = SS4_1F_X_V1(p);
> +             f->pt_img[0].y = SS4_1F_Y_V1(p);
> +             /* Keep Z-value in 0-127 */
> +             f->pt_img[0].z = min(((SS4_1F_Z_V1(p)) * 2), 127);
> +             f->large_fn |= SS4_1F_LFB(p) ? 0x01 : 0x00;
> +             f->fingers = 1;
> +             f->first_mp = 0;
> +             f->is_mp = 0;
> +             break;
> +
> +     case SS4_PACKET_ID_TWO:
> +             if (priv->flags & ALPS_BTNLESS) {
> +                     f->pt_img[0].x = SS4_BTL_MF_X_V1(p, 0);
> +                     f->pt_img[0].y = SS4_BTL_MF_Y_V1(p, 0);
> +                     f->pt_img[1].x = SS4_BTL_MF_X_V1(p, 1);
> +                     f->pt_img[1].y = SS4_BTL_MF_Y_V1(p, 1);
> +             } else {
> +                     f->pt_img[0].x = SS4_STD_MF_X_V1(p, 0);
> +                     f->pt_img[0].y = SS4_STD_MF_Y_V1(p, 0);
> +                     f->pt_img[1].x = SS4_STD_MF_X_V1(p, 1);
> +                     f->pt_img[1].y = SS4_STD_MF_Y_V1(p, 1);
> +             }
> +             f->pt_img[0].z = SS4_MF_Z_V1(p, 0) ? 0x30 : 0;
> +             f->pt_img[1].z = SS4_MF_Z_V1(p, 1) ? 0x30 : 0;
> +
> +             if (SS4_IS_MF_CONTINUE_V1(p)) {
> +                     f->first_mp = 1;
> +             } else {
> +                     f->fingers = 2;
> +                     f->first_mp = 0;
> +             }
> +             f->is_mp = 0;
> +
> +             break;
> +
> +     case SS4_PACKET_ID_MULTI:
> +             if (priv->flags & ALPS_BTNLESS) {
> +                     f->pt_img[2].x = SS4_BTL_MF_X_V1(p, 0);
> +                     f->pt_img[2].y = SS4_BTL_MF_Y_V1(p, 0);
> +                     f->pt_img[3].x = SS4_BTL_MF_X_V1(p, 1);
> +                     f->pt_img[3].y = SS4_BTL_MF_Y_V1(p, 1);
> +                     no_data_x = SS4_MFPACKET_NO_AX_BL;
> +                     no_data_y = SS4_MFPACKET_NO_AY_BL;
> +             } else {
> +                     f->pt_img[2].x = SS4_STD_MF_X_V1(p, 0);
> +                     f->pt_img[2].y = SS4_STD_MF_Y_V1(p, 0);
> +                     f->pt_img[3].x = SS4_STD_MF_X_V1(p, 1);
> +                     f->pt_img[3].y = SS4_STD_MF_Y_V1(p, 1);
> +                     no_data_x = SS4_MFPACKET_NO_AX;
> +                     no_data_y = SS4_MFPACKET_NO_AY;
> +             }
> +             f->pt_img[2].z = SS4_MF_Z_V1(p, 0) ? 0x30 : 0;
> +             f->pt_img[3].z = SS4_MF_Z_V1(p, 1) ? 0x30 : 0;
> +
> +             f->first_mp = 0;
> +             f->is_mp = 1;
> +
> +             if (SS4_IS_5F_DETECTED_V1(p)) {
> +                     f->fingers = 5;
> +             } else if (f->pt_img[3].x == no_data_x &&
> +                          f->pt_img[3].y == no_data_y) {
> +                     f->fingers = 3;
> +                     f->pt_img[3].x = 0;
> +                     f->pt_img[3].y = 0;
> +                     f->pt_img[3].z = 0;
> +             } else {
> +                     f->fingers = 4;
> +             }
> +             break;
> +
> +     case SS4_PACKET_ID_IDLE:
> +     default:
> +             memset(f, 0, sizeof(struct alps_fields));
> +             break;
> +     }
> +
> +     f->btn.left = !!(SS4_BTN_V1(p) & 0x01);
> +     if (!(priv->flags & ALPS_BTNLESS)) {
> +             f->btn.right = !!(SS4_BTN_V1(p) & 0x02);
> +             f->btn.middle = !!(SS4_BTN_V1(p) & 0x04);
> +     }
> +}
> +
> +static void alps_decode_ss4_v2(struct alps_fields *f,
> +                     unsigned char *p, struct psmouse *psmouse)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     unsigned char pkt_id;
> +     unsigned int no_data_x, no_data_y;
> +
> +     if (!psmouse || !f || !p)
> +             return;
> +
> +     pkt_id = alps_get_pkt_id_ss4_v2(p);
> +
> +     /* Current packet is 1Finger coordinate packet */
> +     switch (pkt_id) {
> +     case SS4_PACKET_ID_ONE:
> +             f->pt_img[0].x = SS4_1F_X_V2(p);
> +             f->pt_img[0].y = SS4_1F_Y_V2(p);
> +             /* Keep Z-value in 0-127 */
> +             f->pt_img[0].z = min(((SS4_1F_Z_V2(p)) * 2), 127);
> +             f->large_fn |= SS4_1F_LFB_V2(p) ? 0x01 : 0x00;
> +             f->fingers = 1;
> +             f->first_mp = 0;
> +             f->is_mp = 0;
> +             break;
> +
> +     case SS4_PACKET_ID_TWO:
> +             if (priv->flags & ALPS_BTNLESS) {
> +                     f->pt_img[0].x = SS4_BTL_MF_X_V2(p, 0);
> +                     f->pt_img[0].y = SS4_BTL_MF_Y_V2(p, 0);
> +                     f->pt_img[1].x = SS4_BTL_MF_X_V2(p, 1);
> +                     f->pt_img[1].y = SS4_BTL_MF_Y_V2(p, 1);
> +             } else {
> +                     f->pt_img[0].x = SS4_STD_MF_X_V2(p, 0);
> +                     f->pt_img[0].y = SS4_STD_MF_Y_V2(p, 0);
> +                     f->pt_img[1].x = SS4_STD_MF_X_V2(p, 1);
> +                     f->pt_img[1].y = SS4_STD_MF_Y_V2(p, 1);
> +             }
> +             f->pt_img[0].z = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
> +             f->pt_img[1].z = SS4_MF_Z_V2(p, 1) ? 0x30 : 0;
> +
> +             f->large_fn |= SS4_MF_LF_V2(p, 0) ? 0x01 : 0;
> +             f->large_fn |= SS4_MF_LF_V2(p, 1) ? 0x02 : 0;
> +
> +             if (SS4_IS_MF_CONTINUE_V2(p)) {
> +                     f->first_mp = 1;
> +             } else {
> +                     f->fingers = 2;
> +                     f->first_mp = 0;
> +             }
> +             f->is_mp = 0;
> +             break;
> +
> +     case SS4_PACKET_ID_MULTI:
> +             if (priv->flags & ALPS_BTNLESS) {
> +                     f->pt_img[2].x = SS4_BTL_MF_X_V2(p, 0);
> +                     f->pt_img[2].y = SS4_BTL_MF_Y_V2(p, 0);
> +                     f->pt_img[3].x = SS4_BTL_MF_X_V2(p, 1);
> +                     f->pt_img[3].y = SS4_BTL_MF_Y_V2(p, 1);
> +                     no_data_x = SS4_MFPACKET_NO_AX_BL;
> +                     no_data_y = SS4_MFPACKET_NO_AY_BL;
> +             } else {
> +                     f->pt_img[2].x = SS4_STD_MF_X_V2(p, 0);
> +                     f->pt_img[2].y = SS4_STD_MF_Y_V2(p, 0);
> +                     f->pt_img[3].x = SS4_STD_MF_X_V2(p, 1);
> +                     f->pt_img[3].y = SS4_STD_MF_Y_V2(p, 1);
> +                     no_data_x = SS4_MFPACKET_NO_AX;
> +                     no_data_y = SS4_MFPACKET_NO_AY;
> +             }
> +             f->pt_img[2].z = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
> +             f->pt_img[3].z = SS4_MF_Z_V2(p, 1) ? 0x30 : 0;
> +
> +             f->large_fn |= SS4_MF_LF_V2(p, 0) ? 0x04 : 0;
> +             f->large_fn |= SS4_MF_LF_V2(p, 1) ? 0x08 : 0;
> +             f->first_mp = 0;
> +             f->is_mp = 1;
> +
> +             if (SS4_IS_5F_DETECTED_V2(p)) {
> +                     f->fingers = 5;
> +             } else if (f->pt_img[3].x == no_data_x &&
> +                          f->pt_img[3].y == no_data_y) {
> +                     f->fingers = 3;
> +                     f->pt_img[3].x = 0;
> +                     f->pt_img[3].y = 0;
> +                     f->pt_img[3].z = 0;
> +             } else {
> +                     f->fingers = 4;
> +             }
> +             break;
> +
> +     case SS4_PACKET_ID_IDLE:
> +     default:
> +             memset(f, 0, sizeof(struct alps_fields));
> +             break;
> +     }
> +
> +     f->btn.left = !!(SS4_BTN_V2(p) & 0x01);
> +     if (!(priv->flags & ALPS_BTNLESS)) {
> +             f->btn.right = !!(SS4_BTN_V2(p) & 0x02);
> +             f->btn.middle = !!(SS4_BTN_V2(p) & 0x04);
> +     }
>  }
>
>  static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
>  {
>       struct alps_data *priv = psmouse->private;
>       unsigned char *packet = psmouse->packet;
> +     struct input_dev *dev = psmouse->dev;
>       struct input_dev *dev2 = priv->dev2;
> -     struct alps_fields *f = &priv->f;
> -     int fingers = 0;
> +     int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
> +     int fingers = 0, bmap_fn;
> +     struct alps_fields f = {0};
>
> -     memset(f, 0, sizeof(*f));
> -
> -     priv->decode_fields(f, packet, psmouse);
> +     priv->decode_fields(&f, packet, psmouse);
>
>       /*
>        * There's no single feature of touchpad position and bitmap packets
> @@ -639,14 +1095,22 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
>                * packet. Check for this, and when it happens process the
>                * position packet as usual.
>                */
> -             if (f->is_mp) {
> -                     fingers = f->fingers;
> +             if (f.is_mp) {
> +                     fingers = f.fingers;
>                       if (priv->proto_version == ALPS_PROTO_V3) {
> -                             if (alps_process_bitmap(priv, f) == 0)
> -                                     fingers = 0; /* Use st data */
> +                             bmap_fn = alps_process_bitmap(priv, f.x_map,
> +                                                           f.y_map, &x1, &y1,
> +                                                           &x2, &y2);
> +
> +                             /*
> +                              * We shouldn't report more than one finger if
> +                              * we don't have two coordinates.
> +                              */
> +                             if (fingers > 1 && bmap_fn < 2)
> +                                     fingers = bmap_fn;
>
>                               /* Now process position packet */
> -                             priv->decode_fields(f, priv->multi_data,
> +                             priv->decode_fields(&f, priv->multi_data,
>                                                   psmouse);
>                       } else {
>                               /*
> @@ -655,14 +1119,15 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
>                                * calculate Pt2, so we need to do position
>                                * packet decode first.
>                                */
> -                             priv->decode_fields(f, priv->multi_data,
> +                             priv->decode_fields(&f, priv->multi_data,
>                                                   psmouse);
>
>                               /*
>                                * Since Dolphin's finger number is reliable,
>                                * there is no need to compare with bmap_fn.
>                                */
> -                             alps_process_bitmap_dolphin(priv, f);
> +                             alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
> +                                     &x2, &y2);
>                       }
>               } else {
>                       priv->multi_packet = 0;
> @@ -677,10 +1142,10 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
>        * out misidentified bitmap packets, we reject anything with this
>        * bit set.
>        */
> -     if (f->is_mp)
> +     if (f.is_mp)
>               return;
>
> -     if (!priv->multi_packet && f->first_mp) {
> +     if (!priv->multi_packet && f.first_mp) {
>               priv->multi_packet = 1;
>               memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
>               return;
> @@ -694,15 +1159,44 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
>        * with x, y, and z all zero, so these seem to be flukes.
>        * Ignore them.
>        */
> -     if (f->st.x && f->st.y && !f->pressure)
> +     if (f.pt.x && f.pt.y && !f.pt.z)
>               return;
>
> -     alps_report_semi_mt_data(psmouse, fingers);
> +     /*
> +      * If we don't have MT data or the bitmaps were empty, we have
> +      * to rely on ST data.
> +      */
> +     if (!fingers) {
> +             x1 = f.pt.x;
> +             y1 = f.pt.y;
> +             fingers = f.pt.z > 0 ? 1 : 0;
> +     }
> +
> +     if (f.pt.z > 0)
> +             input_report_key(dev, BTN_TOUCH, 1);
> +     else
> +             input_report_key(dev, BTN_TOUCH, 0);
> +
> +     alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
> +
> +     input_mt_report_finger_count(dev, fingers);
> +
> +     input_report_key(dev, BTN_LEFT, f.btn.left);
> +     input_report_key(dev, BTN_RIGHT, f.btn.right);
> +     input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> +
> +     if (f.pt.z > 0) {
> +             input_report_abs(dev, ABS_X, f.pt.x);
> +             input_report_abs(dev, ABS_Y, f.pt.y);
> +     }
> +     input_report_abs(dev, ABS_PRESSURE, f.pt.z);
> +
> +     input_sync(dev);
>
>       if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
> -             input_report_key(dev2, BTN_LEFT, f->ts_left);
> -             input_report_key(dev2, BTN_RIGHT, f->ts_right);
> -             input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
> +             input_report_key(dev2, BTN_LEFT, f.btn.ts_left);
> +             input_report_key(dev2, BTN_RIGHT, f.btn.ts_right);
> +             input_report_key(dev2, BTN_MIDDLE, f.btn.ts_middle);
>               input_sync(dev2);
>       }
>  }
> @@ -727,6 +1221,64 @@ static void alps_process_packet_v3(struct psmouse *psmouse)
>       alps_process_touchpad_packet_v3_v5(psmouse);
>  }
>
> +/* proto_v9 */
> +static void alps_process_touchpad_packet_ss3(struct psmouse *psmouse)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     unsigned char *packet = psmouse->packet;
> +     struct input_dev *dev = psmouse->dev;
> +     int fingers = 0;
> +     struct alps_fields f = {0};
> +     struct alps_abs_data    pt_output[2] = { {0, 0, 0}, {0, 0, 0} };
> +     unsigned char output_fn_num = 0;
> +
> +     priv->decode_fields(&f, packet, psmouse);
> +
> +     if ((!!priv->multi_packet) != (!!f.is_mp)) {
> +             priv->multi_packet = 0;
> +             return;
> +     }
> +
> +     /* When f.first_mp is 1, next packet should be a
> +      *      bitmap packet(when there is no error).
> +      */
> +     priv->multi_packet = f.first_mp;
> +
> +     /* Don't process any 3-f data */
> +     if (f.first_mp || f.is_mp)
> +             return;
> +
> +     /*
> +      * If we don't have MT data or the bitmaps were empty, we have
> +      * to rely on ST data.
> +      */
> +     fingers = f.fingers;
> +
> +     alps_process_resting_finger(psmouse, &f, pt_output, &output_fn_num);
> +     alps_process_btnless_click(psmouse, &f);
> +
> +     if (pt_output[0].z || pt_output[1].z)
> +             input_report_key(dev, BTN_TOUCH, 1);
> +     else
> +             input_report_key(dev, BTN_TOUCH, 0);
> +
> +     alps_report_semi_mt_data_ex(dev, 2, pt_output);
> +
> +     input_mt_report_finger_count(dev, output_fn_num);
> +
> +     input_report_key(dev, BTN_LEFT, f.btn.left);
> +     input_report_key(dev, BTN_RIGHT, f.btn.right);
> +     input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> +
> +     if (pt_output[0].z > 0) {
> +             input_report_abs(dev, ABS_X, pt_output[0].x);
> +             input_report_abs(dev, ABS_Y, pt_output[0].y);
> +     }
> +     input_report_abs(dev, ABS_PRESSURE, pt_output[0].z ? 30 : 0);
> +
> +     input_sync(dev);
> +}
> +
>  static void alps_process_packet_v6(struct psmouse *psmouse)
>  {
>       struct alps_data *priv = psmouse->private;
> @@ -801,8 +1353,13 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
>  {
>       struct alps_data *priv = psmouse->private;
>       unsigned char *packet = psmouse->packet;
> -     struct alps_fields *f = &priv->f;
> +     struct input_dev *dev = psmouse->dev;
>       int offset;
> +     int x, y, z;
> +     int left, right;
> +     int x1, y1, x2, y2;
> +     int fingers = 0;
> +     unsigned int x_bitmap, y_bitmap;
>
>       /*
>        * v4 has a 6-byte encoding for bitmap data, but this data is
> @@ -824,41 +1381,96 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
>       if (++priv->multi_packet > 2) {
>               priv->multi_packet = 0;
>
> -             f->x_map = ((priv->multi_data[2] & 0x1f) << 10) |
> +             x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) |
>                          ((priv->multi_data[3] & 0x60) << 3) |
>                          ((priv->multi_data[0] & 0x3f) << 2) |
>                          ((priv->multi_data[1] & 0x60) >> 5);
> -             f->y_map = ((priv->multi_data[5] & 0x01) << 10) |
> +             y_bitmap = ((priv->multi_data[5] & 0x01) << 10) |
>                          ((priv->multi_data[3] & 0x1f) << 5) |
>                           (priv->multi_data[1] & 0x1f);
>
> -             f->fingers = alps_process_bitmap(priv, f);
> +             fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap,
> +                                           &x1, &y1, &x2, &y2);
> +
> +             /* Store MT data.*/
> +             priv->fingers = fingers;
> +             priv->x1 = x1;
> +             priv->x2 = x2;
> +             priv->y1 = y1;
> +             priv->y2 = y2;
>       }
>
> -     f->left = packet[4] & 0x01;
> -     f->right = packet[4] & 0x02;
> +     left = packet[4] & 0x01;
> +     right = packet[4] & 0x02;
> +
> +     x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
> +         ((packet[0] & 0x30) >> 4);
> +     y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
> +     z = packet[5] & 0x7f;
> +
> +     /*
> +      * If there were no contacts in the bitmap, use ST
> +      * points in MT reports.
> +      * If there were two contacts or more, report MT data.
> +      */
> +     if (priv->fingers < 2) {
> +             x1 = x;
> +             y1 = y;
> +             fingers = z > 0 ? 1 : 0;
> +     } else {
> +             fingers = priv->fingers;
> +             x1 = priv->x1;
> +             x2 = priv->x2;
> +             y1 = priv->y1;
> +             y2 = priv->y2;
> +     }
> +
> +     if (z >= 64)
> +             input_report_key(dev, BTN_TOUCH, 1);
> +     else
> +             input_report_key(dev, BTN_TOUCH, 0);
> +
> +     alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
> +
> +     input_mt_report_finger_count(dev, fingers);
> +
> +     input_report_key(dev, BTN_LEFT, left);
> +     input_report_key(dev, BTN_RIGHT, right);
>
> -     f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
> -               ((packet[0] & 0x30) >> 4);
> -     f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
> -     f->pressure = packet[5] & 0x7f;
> +     if (z > 0) {
> +             input_report_abs(dev, ABS_X, x);
> +             input_report_abs(dev, ABS_Y, y);
> +     }
> +     input_report_abs(dev, ABS_PRESSURE, z);
>
> -     alps_report_semi_mt_data(psmouse, f->fingers);
> +     input_sync(dev);
>  }
>
>  static bool alps_is_valid_package_v7(struct psmouse *psmouse)
>  {
> -     switch (psmouse->pktcnt) {
> -     case 3:
> -             return (psmouse->packet[2] & 0x40) == 0x40;
> -     case 4:
> -             return (psmouse->packet[3] & 0x48) == 0x48;
> -     case 6:
> -             return (psmouse->packet[5] & 0x40) == 0x00;
> -     }
> +     if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40))
> +             return false;
> +     if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48))
> +             return false;
> +     if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0))
> +             return false;
>       return true;
>  }
>
> +static int alps_drop_unsupported_packet_v7(struct psmouse *psmouse)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     int drop = 1;
> +
> +     if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW ||
> +         priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
> +         priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
> +         priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
> +             drop = 0;
> +
> +     return drop;
> +}
> +
>  static unsigned char alps_get_packet_id_v7(char *byte)
>  {
>       unsigned char packet_id;
> @@ -874,85 +1486,251 @@ static unsigned char alps_get_packet_id_v7(char *byte)
>       else
>               packet_id = V7_PACKET_ID_UNKNOWN;
>
> -     return packet_id;
> +     return packet_id;
> +}
> +
> +static void alps_get_finger_coordinate_v7(struct alps_abs_data *pt,
> +                                       unsigned char *pkt,
> +                                       unsigned char pkt_id)
> +{
> +     if ((pkt_id == V7_PACKET_ID_TWO) ||
> +        (pkt_id == V7_PACKET_ID_MULTI) ||
> +        (pkt_id == V7_PACKET_ID_NEW)) {
> +             pt[0].x = ((pkt[2] & 0x80) << 4);
> +             pt[0].x |= ((pkt[2] & 0x3F) << 5);
> +             pt[0].x |= ((pkt[3] & 0x30) >> 1);
> +             pt[0].x |= (pkt[3] & 0x07);
> +             pt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
> +
> +             pt[1].x = ((pkt[3] & 0x80) << 4);
> +             pt[1].x |= ((pkt[4] & 0x80) << 3);
> +             pt[1].x |= ((pkt[4] & 0x3F) << 4);
> +             pt[1].y = ((pkt[5] & 0x80) << 3);
> +             pt[1].y |= ((pkt[5] & 0x3F) << 4);
> +
> +             if (pkt_id == V7_PACKET_ID_TWO) {
> +                     pt[1].x &= ~0x000F;
> +                     pt[1].y |= 0x000F;
> +             } else if (pkt_id == V7_PACKET_ID_MULTI) {
> +                     pt[1].x &= ~0x003F;
> +                     pt[1].y &= ~0x0020;
> +                     pt[1].y |= ((pkt[4] & 0x02) << 4);
> +                     pt[1].y |= 0x001F;
> +             } else if (pkt_id == V7_PACKET_ID_NEW) {
> +                     pt[1].x &= ~0x003F;
> +                     pt[1].x |= (pkt[0] & 0x20);
> +                     pt[1].y |= 0x000F;
> +             }
> +
> +             pt[0].y = 0x7FF - pt[0].y;
> +             pt[1].y = 0x7FF - pt[1].y;
> +
> +             pt[0].z = (pt[0].x && pt[0].y) ? 62 : 0;
> +             pt[1].z = (pt[1].x && pt[1].y) ? 62 : 0;
> +     }
> +}
> +
> +static void alps_decode_packet_v7(struct alps_fields *f,
> +                               unsigned char *p,
> +                               struct psmouse *psmouse)
> +{
> +     struct alps_data *priv = psmouse->private;
> +
> +     priv->r.v7.pkt_id = alps_get_packet_id_v7(p);
> +
> +     alps_get_finger_coordinate_v7(f->pt_img, p, priv->r.v7.pkt_id);
> +
> +     priv->r.v7.rest_left = 0;
> +     priv->r.v7.rest_right = 0;
> +     priv->r.v7.additional_fingers = 0;
> +     memset(&f->btn, 0, sizeof(struct alps_btn));
> +
> +     if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
> +         priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) {
> +
> +             if (priv->flags & ALPS_BTNLESS) {
> +                     priv->r.v7.rest_left = (p[0] & 0x10) >> 4;
> +                     priv->r.v7.rest_right = (p[0] & 0x20) >> 5;
> +                     f->btn.middle = (p[0] & 0x80) >> 7;
> +             } else {
> +                     f->btn.left = (p[0] & 0x80) >> 7;
> +                     f->btn.right = (p[0] & 0x20) >> 5;
> +                     f->btn.middle = (p[0] & 0x10) >> 4;
> +             }
> +     }
> +
> +     if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
> +             priv->r.v7.additional_fingers = p[5] & 0x03;
>  }
>
> -static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
> -                                       unsigned char *pkt,
> -                                       unsigned char pkt_id)
> +static void alps_set_each_pt_attr_v7(struct psmouse *psmouse,
> +                                  struct alps_abs_data *pt,
> +                                  struct alps_bl_pt_attr *pt_attr)
>  {
> -     mt[0].x = ((pkt[2] & 0x80) << 4);
> -     mt[0].x |= ((pkt[2] & 0x3F) << 5);
> -     mt[0].x |= ((pkt[3] & 0x30) >> 1);
> -     mt[0].x |= (pkt[3] & 0x07);
> -     mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
> +     struct alps_data *priv = psmouse->private;
> +     unsigned int dist;
>
> -     mt[1].x = ((pkt[3] & 0x80) << 4);
> -     mt[1].x |= ((pkt[4] & 0x80) << 3);
> -     mt[1].x |= ((pkt[4] & 0x3F) << 4);
> -     mt[1].y = ((pkt[5] & 0x80) << 3);
> -     mt[1].y |= ((pkt[5] & 0x3F) << 4);
> +     if (!pt_attr->is_init_pt_got && pt->z != 0) {
> +             pt_attr->is_init_pt_got = 1;
> +             pt_attr->is_counted = 0;
> +             memcpy(&pt_attr->init_pt, pt, sizeof(pt_attr->init_pt));
> +     }
>
> -     switch (pkt_id) {
> -     case V7_PACKET_ID_TWO:
> -             mt[1].x &= ~0x000F;
> -             mt[1].y |= 0x000F;
> -             break;
> +     if (pt->z != 0) {
> +             if (pt->y < priv->resting_zone_y_min) {
> +                     /* A finger is recognized as
> +                     a non-resting finger if it's
> +                     position is outside the resting finger zone.*/
> +                     pt_attr->zone = ZONE_NORMAL;
> +                     pt_attr->is_counted = 1;
> +             } else {
> +                     /* A finger is recognized as
> +                     a resting finger if it's position
> +                     is inside the resting finger zone and
> +                     there's no large movement
> +                     from it's touch down position.*/
> +                     pt_attr->zone = ZONE_RESTING;
> +
> +                     if (pt->x > priv->x_max / 2)
> +                             pt_attr->zone |= ZONE_RIGHT_BTN;
> +                     else
> +                             pt_attr->zone |= ZONE_LEFT_BTN;
> +
> +                     /* A resting finger will turn to
> +                     be a non-resting finger if it
> +                     has made large movement from it's touch down position. A
> +                     non-resting finger will never turn to
> +                     a resting finger before
> +                     it leaves the touchpad surface.*/
> +                     if (pt_attr->is_init_pt_got) {
> +                             dist = alps_pt_distance(pt, &pt_attr->init_pt);
> +
> +                             if (dist > V7_LARGE_MOVEMENT)
> +                                     pt_attr->is_counted = 1;
> +                     }
> +             }
> +     }
> +}
>
> -     case V7_PACKET_ID_MULTI:
> -             mt[1].x &= ~0x003F;
> -             mt[1].y &= ~0x0020;
> -             mt[1].y |= ((pkt[4] & 0x02) << 4);
> -             mt[1].y |= 0x001F;
> -             break;
> +static void alps_set_pt_attr_v7(struct psmouse *psmouse,
> +                                    struct alps_fields *f)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     int i;
>
> -     case V7_PACKET_ID_NEW:
> -             mt[1].x &= ~0x003F;
> -             mt[1].x |= (pkt[0] & 0x20);
> -             mt[1].y |= 0x000F;
> +     switch (priv->r.v7.pkt_id) {
> +     case  V7_PACKET_ID_TWO:
> +     case  V7_PACKET_ID_MULTI:
> +             for (i = 0; i < V7_IMG_PT_NUM; i++)
> +                     alps_set_each_pt_attr_v7(psmouse,
> +                                              &f->pt_img[i],
> +                                              &priv->pt_attr[i]);
> +             break;
> +     default:
> +             /*All finger attributes are cleared
> +             when packet ID is 'IDLE', 'New'or
> +             other unknown IDs. An 'IDLE' packet indicates
> +             that there's no finger and no button activity.
> +             A 'NEW' packet indicates the finger position
> +             in packet is not continues from previous packet.
> +             Such as the condition there's finger placed or lifted.
> +             In these cases, finger attributes will be reset.*/
> +
> +             memset(priv->pt_attr, 0,
> +                     sizeof(priv->pt_attr[0]) * V7_IMG_PT_NUM);
>               break;
>       }
> -
> -     mt[0].y = 0x7FF - mt[0].y;
> -     mt[1].y = 0x7FF - mt[1].y;
>  }
>
> -static int alps_get_mt_count(struct input_mt_pos *mt)
> +static void alps_cal_output_finger_num_v7(struct psmouse *psmouse,
> +                                     struct alps_fields *f)
>  {
> +     struct alps_data *priv = psmouse->private;
> +     unsigned int fn = 0;
>       int i;
>
> -     for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++)
> -             /* empty */;
> +     switch (priv->r.v7.pkt_id) {
> +     case V7_PACKET_ID_IDLE:
> +     case V7_PACKET_ID_NEW:
> +             /*No finger is reported when packet ID is
> +             'IDLE' or 'New'. An 'IDLE' packet indicates
> +             that there's no finger on touchpad.
> +             A 'NEW' packet indicates there's finger placed
> +             or lifted. Finger position of 'New' packet is
> +             not continues from the previous packet.*/
> +             fn = 0;
> +             break;
> +     case V7_PACKET_ID_TWO:
> +             if (f->pt_img[0].z == 0) {
> +                     /*The first finger slot is zero when
> +                     a non-resting finger lifted and remaining
> +                     only one resting finger on touchpad.
> +                     Hardware report the remaining resting finger
> +                     in second slot.This resting is ignored*/
> +                     fn = 0;
> +             } else if (f->pt_img[1].z == 0) {
> +                     /* The second finger slot is zero
> +                     if there's only one finger*/
> +                     fn = 1;
> +             } else {
> +                     /*All non-resting fingers will be counted to report*/
> +                     fn = 0;
> +                     for (i = 0; i < V7_IMG_PT_NUM; i++) {
> +                             if (priv->pt_attr[i].is_counted)
> +                                     fn++;
> +                     }
> +
> +                     /*In the case that both fingers are
> +                     resting fingers, report the first one*/
> +                     if (!priv->pt_attr[0].is_counted &&
> +                         !priv->pt_attr[1].is_counted) {
> +                             fn = 1;
> +                     }
> +             }
> +             break;
> +     case V7_PACKET_ID_MULTI:
> +             /*A packet ID 'MULTI' indicats that at least 3 non-resting
> +             finger exist.*/
> +             fn = 3 + priv->r.v7.additional_fingers;
> +             break;
> +     }
>
> -     return i;
> +     f->fingers = fn;
>  }
>
> -static int alps_decode_packet_v7(struct alps_fields *f,
> -                               unsigned char *p,
> -                               struct psmouse *psmouse)
> +static void alps_assign_buttons_v7(struct psmouse *psmouse,
> +                                struct alps_fields *f)
>  {
> -     unsigned char pkt_id;
> +     struct alps_data *priv = psmouse->private;
>
> -     pkt_id = alps_get_packet_id_v7(p);
> -     if (pkt_id == V7_PACKET_ID_IDLE)
> -             return 0;
> -     if (pkt_id == V7_PACKET_ID_UNKNOWN)
> -             return -1;
> +     /* It's ClickPad */
> +     if (priv->flags & ALPS_BTNLESS) {
> +             if (!f->btn.middle)
> +                     goto exit;
> +
> +             f->btn.middle = 0;
> +             if (priv->prev_btn.left || priv->prev_btn.middle ||
> +                     priv->prev_btn.right) {
> +                     memcpy(&f->btn, &priv->prev_btn,
> +                             sizeof(struct alps_btn));
> +                     goto exit;
> +             }
>
> -     alps_get_finger_coordinate_v7(f->mt, p, pkt_id);
> +             if (priv->r.v7.rest_right ||
> +                 priv->pt_attr[0].zone & ZONE_RIGHT_BTN ||
> +                 priv->pt_attr[1].zone & ZONE_RIGHT_BTN) {
> +                     f->btn.right = 1;
> +             } else {
> +                     f->btn.left = 1;
> +             }
>
> -     if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) {
> -             f->left = (p[0] & 0x80) >> 7;
> -             f->right = (p[0] & 0x20) >> 5;
> -             f->middle = (p[0] & 0x10) >> 4;
> +             goto exit;
>       }
>
> -     if (pkt_id == V7_PACKET_ID_TWO)
> -             f->fingers = alps_get_mt_count(f->mt);
> -     else if (pkt_id == V7_PACKET_ID_MULTI)
> -             f->fingers = 3 + (p[5] & 0x03);
> +     /* It's Standard, do nothing */
>
> -     return 0;
> +exit:
> +     memcpy(&priv->prev_btn, &f->btn, sizeof(struct alps_btn));
>  }
>
>  static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
> @@ -962,21 +1740,9 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
>       struct input_dev *dev2 = priv->dev2;
>       int x, y, z, left, right, middle;
>
> -     /*
> -      *        b7 b6 b5 b4 b3 b2 b1 b0
> -      * Byte0   0  1  0  0  1  0  0  0
> -      * Byte1   1  1  *  *  1  M  R  L
> -      * Byte2  X7  1 X5 X4 X3 X2 X1 X0
> -      * Byte3  Z6  1 Y6 X6  1 Y2 Y1 Y0
> -      * Byte4  Y7  0 Y5 Y4 Y3  1  1  0
> -      * Byte5 T&P  0 Z5 Z4 Z3 Z2 Z1 Z0
> -      * M / R / L: Middle / Right / Left button
> -      */
> -
> -     x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2);
> -     y = (packet[3] & 0x07) | (packet[4] & 0xb8) |
> -         ((packet[3] & 0x20) << 1);
> -     z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
> +     x = ((packet[2] & 0xBF)) | ((packet[3] & 0x10) << 2);
> +     y = (packet[3] & 0x03) | (packet[4] & 0xB8) | ((packet[3] & 0x20) << 1);
> +     z = (packet[5] & 0x3F) | ((packet[3] & 0x80) >> 1);
>
>       left = (packet[1] & 0x01);
>       right = (packet[1] & 0x02) >> 1;
> @@ -996,35 +1762,115 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
>  static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
>  {
>       struct alps_data *priv = psmouse->private;
> -     struct input_dev *dev = psmouse->dev;
> -     struct alps_fields *f = &priv->f;
> +     struct alps_fields f = {0};
> +     unsigned char *packet = psmouse->packet;
>
> -     memset(f, 0, sizeof(*f));
> +     priv->decode_fields(&f, packet, psmouse);
>
> -     if (priv->decode_fields(f, psmouse->packet, psmouse))
> +     if (alps_drop_unsupported_packet_v7(psmouse))
>               return;
>
> -     alps_report_mt_data(psmouse, alps_get_mt_count(f->mt));
> +     alps_set_pt_attr_v7(psmouse, &f);
>
> -     input_mt_report_finger_count(dev, f->fingers);
> +     alps_cal_output_finger_num_v7(psmouse, &f);
>
> -     input_report_key(dev, BTN_LEFT, f->left);
> -     input_report_key(dev, BTN_RIGHT, f->right);
> -     input_report_key(dev, BTN_MIDDLE, f->middle);
> +     alps_assign_buttons_v7(psmouse, &f);
>
> -     input_sync(dev);
> +     alps_report_coord_and_btn(psmouse, &f);
>  }
>
>  static void alps_process_packet_v7(struct psmouse *psmouse)
>  {
>       unsigned char *packet = psmouse->packet;
>
> -     if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06)
> +     if ((packet[0] == 0x48) && ((packet[4] & 0x47) == 0x06))
>               alps_process_trackstick_packet_v7(psmouse);
>       else
>               alps_process_touchpad_packet_v7(psmouse);
>  }
>
> +static void alps_process_packet_ss4(struct psmouse *psmouse)
> +{
> +     struct alps_data *priv = psmouse->private;
> +     unsigned char *packet = psmouse->packet;
> +     struct input_dev *dev = psmouse->dev;
> +     struct alps_fields f;
> +     struct alps_abs_data output_data[5];
> +     unsigned char output_fn;
> +
> +     memset(&f, 0, sizeof(struct alps_fields));
> +     priv->decode_fields(&f, packet, psmouse);
> +
> +     if (priv->multi_packet) {
> +             /*
> +              * Sometimes the first packet will indicate a multi-packet
> +              * sequence, but sometimes the next multi-packet would not come.
> +              * Check for this, and when it happens process the
> +              * position packet as usual.
> +              */
> +             if (f.is_mp) {
> +                     /* Now process the 1st packet */
> +                     priv->decode_fields(&f, priv->multi_data, psmouse);
> +             } else {
> +                     priv->multi_packet = 0;
> +             }
> +     }
> +
> +     /*
> +      * "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
> +      * When it is set, it means 2nd packet comes without 1st packet come.
> +      */
> +     if (f.is_mp)
> +             return;
> +
> +     /* Save the first packet */
> +     if (!priv->multi_packet && f.first_mp) {
> +             priv->multi_packet = 1;
> +             memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
> +             return;
> +     }
> +
> +     priv->multi_packet = 0;
> +
> +     /* Set "output_data" and "output_fn" */
> +     memset(&output_data[0], 0, sizeof(output_data));
> +     if (priv->flags & ALPS_BTNLESS) {
> +             alps_process_resting_finger(psmouse, &f,
> +                     output_data, &output_fn);
> +             alps_process_btnless_click(psmouse, &f);
> +     } else {
> +             memcpy(&output_data[0], &f.pt_img[0],
> +                     sizeof(struct alps_abs_data) * f.fingers);
> +             output_fn = f.fingers;
> +     }
> +
> +     f.pt.x = output_data[0].x;
> +     f.pt.y = output_data[0].y;
> +     f.pt.z = output_data[0].z;
> +
> +     if (output_data[0].z || output_data[1].z ||
> +             output_data[2].z || output_data[3].z)
> +             input_report_key(dev, BTN_TOUCH, 1);
> +     else
> +             input_report_key(dev, BTN_TOUCH, 0);
> +
> +     alps_report_semi_mt_data_ex(dev, 4, output_data);
> +
> +     input_mt_report_finger_count(dev, output_fn);
> +
> +     input_report_key(dev, BTN_LEFT, f.btn.left);
> +     input_report_key(dev, BTN_RIGHT, f.btn.right);
> +     input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> +
> +     if (f.pt.z > 0) {
> +             input_report_abs(dev, ABS_X, f.pt.x);
> +             input_report_abs(dev, ABS_Y, f.pt.y);
> +     }
> +     input_report_abs(dev, ABS_PRESSURE, f.pt.z);
> +
> +     input_sync(dev);
> +}
> +
>  static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
>                                       unsigned char packet[],
>                                       bool report_buttons)
> @@ -1152,11 +1998,22 @@ static void alps_flush_packet(unsigned long data)
>       serio_continue_rx(psmouse->ps2dev.serio);
>  }
>
> +static bool alps_is_valid_package_ss4(struct psmouse *psmouse)
> +{
> +     if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
> +             return false;
> +     if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
> +             return false;
> +     return true;
> +}
> +
>  static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
>  {
>       struct alps_data *priv = psmouse->private;
>
> -     if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
> +     /* Can not distinguish V8's first byte from PS/2 packet's */
> +     if ((psmouse->packet[0] & 0xc8) == 0x08 &&
> +                     priv->proto_version != ALPS_PROTO_V8) {
>               if (psmouse->pktcnt == 3) {
>                       alps_report_bare_ps2_packet(psmouse, psmouse->packet,
>                                                   true);
> @@ -1189,8 +2046,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
>               return PSMOUSE_BAD_DATA;
>       }
>
> -     if (priv->proto_version == ALPS_PROTO_V7 &&
> -         !alps_is_valid_package_v7(psmouse)) {
> +     if ((priv->proto_version == ALPS_PROTO_V7 &&
> +             !alps_is_valid_package_v7(psmouse)) ||
> +             (priv->proto_version == ALPS_PROTO_V8 &&
> +             !alps_is_valid_package_ss4(psmouse))) {
>               psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
>                           psmouse->pktcnt - 1,
>                           psmouse->packet[psmouse->pktcnt - 1]);
> @@ -1309,20 +2168,20 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
>       return 0;
>  }
>
> -static bool alps_check_valid_firmware_id(unsigned char id[])
> +static int alps_check_valid_firmware_id(unsigned char id[])
>  {
> -     if (id[0] == 0x73)
> -             return true;
> +     int valid = 1;
>
> -     if (id[0] == 0x88 &&
> -         (id[1] == 0x07 ||
> -          id[1] == 0x08 ||
> -          (id[1] & 0xf0) == 0xb0 ||
> -          (id[1] & 0xf0) == 0xc0)) {
> -             return true;
> +     if (id[0] == 0x73)
> +             valid = 1;
> +     else if (id[0] == 0x88) {
> +             if ((id[1] == 0x07) ||
> +                 (id[1] == 0x08) ||
> +                 ((id[1] & 0xf0) == 0xB0))
> +                     valid = 1;
>       }
>
> -     return false;
> +     return valid;
>  }
>
>  static int alps_enter_command_mode(struct psmouse *psmouse)
> @@ -1792,45 +2651,6 @@ error:
>       return -1;
>  }
>
> -static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
> -{
> -     int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys;
> -     struct alps_data *priv = psmouse->private;
> -
> -     reg = alps_command_mode_read_reg(psmouse, reg_pitch);
> -     if (reg < 0)
> -             return reg;
> -
> -     x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
> -     x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
> -
> -     y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */
> -     y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
> -
> -     reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1);
> -     if (reg < 0)
> -             return reg;
> -
> -     x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
> -     x_electrode = 17 + x_electrode;
> -
> -     y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */
> -     y_electrode = 13 + y_electrode;
> -
> -     x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */
> -     y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */
> -
> -     priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
> -     priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
> -
> -     psmouse_dbg(psmouse,
> -                 "pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n",
> -                 x_pitch, y_pitch, x_electrode, y_electrode,
> -                 x_phys / 10, y_phys / 10, priv->x_res, priv->y_res);
> -
> -     return 0;
> -}
> -
>  static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
>  {
>       struct alps_data *priv = psmouse->private;
> @@ -1851,9 +2671,6 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
>           alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
>               goto error;
>
> -     if (alps_get_v3_v7_resolution(psmouse, 0xc2da))
> -             goto error;
> -
>       reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
>       if (reg_val == -1)
>               goto error;
> @@ -1878,6 +2695,32 @@ error:
>       return ret;
>  }
>
> +static int alps_hw_init_v7(struct psmouse *psmouse)
> +{
> +     struct ps2dev *ps2dev = &psmouse->ps2dev;
> +     int reg_val, ret = -1;
> +
> +     if (alps_enter_command_mode(psmouse) ||
> +         alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
> +             goto error;
> +
> +     if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
> +             goto error;
> +
> +     reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
> +     if (reg_val == -1)
> +             goto error;
> +     if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
> +             goto error;
> +
> +     alps_exit_command_mode(psmouse);
> +     return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
> +
> +error:
> +     alps_exit_command_mode(psmouse);
> +     return ret;
> +}
> +
>  /* Must be in command mode when calling this function */
>  static int alps_absolute_mode_v4(struct psmouse *psmouse)
>  {
> @@ -1967,6 +2810,102 @@ error:
>       return -1;
>  }
>
> +static int alps_get_otp_values_ss4(struct psmouse *psmouse,
> +     unsigned char index, unsigned char otp[])
> +{
> +     struct ps2dev *ps2dev = &psmouse->ps2dev;
> +
> +     switch (index) {
> +     case 0:
> +             if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
> +                 ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
> +                 ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
> +                     return -1;
> +
> +             break;
> +
> +     case 1:
> +             if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
> +                 ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
> +                 ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
> +                     return -1;
> +
> +             break;
> +     }
> +
> +     return 0;
> +}
> +
> +int alps_update_device_area_ss4_v1(
> +     unsigned char otp[][4], struct alps_data *priv)
> +{
> +     int     num_x_electrode;
> +     int     num_y_electrode;
> +
> +     num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0]            & 0x0F);
> +     num_y_electrode = ((otp[1][0] >> 4)     & 0x0F);
> +
> +     priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +     priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +
> +     return 0;
> +}
> +
> +int alps_update_device_area_ss4_v2(
> +     unsigned char otp[][4], struct alps_data *priv)
> +{
> +     int     num_x_electrode;
> +     int     num_y_electrode;
> +
> +     num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
> +     if ((priv->fw_ver[1] > 2) ||
> +             (priv->fw_ver[1] == 2 && priv->fw_ver[2] > 0x33)) {
> +             num_y_electrode =
> +                     SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x07);
> +     } else {
> +             num_y_electrode = ((otp[1][0] >> 4) & 0x0F);
> +     }
> +
> +     priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +     priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +
> +     return 0;
> +}
> +
> +int alps_update_btn_info_ss4(unsigned char otp[][4], struct alps_data *priv)
> +{
> +
> +     unsigned char is_btnless = 0;
> +
> +     is_btnless = (otp[1][1] >> 3) &  0x01;
> +
> +     if (is_btnless)
> +             priv->flags |= ALPS_BTNLESS;
> +
> +     return 0;
> +}
> +
> +static int alps_set_defaults_ss4(struct psmouse *psmouse,
> +                                     struct alps_data *priv)
> +{
> +     unsigned char otp[2][4];
> +
> +     memset(otp, 0, sizeof(otp));
> +
> +     if (alps_get_otp_values_ss4(psmouse, 0, &otp[0][0]) ||
> +         alps_get_otp_values_ss4(psmouse, 1, &otp[1][0]))
> +             return -1;
> +
> +     if (priv->fw_ver[1] >= 2)
> +             alps_update_device_area_ss4_v2(otp, priv);
> +     else
> +             alps_update_device_area_ss4_v1(otp, priv);
> +
> +     alps_update_btn_info_ss4(otp, priv);
> +
> +     return 0;
> +}
> +
>  static int alps_dolphin_get_device_area(struct psmouse *psmouse,
>                                       struct alps_data *priv)
>  {
> @@ -2030,28 +2969,54 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
>       return 0;
>  }
>
> -static int alps_hw_init_v7(struct psmouse *psmouse)
> +static int alps_hw_init_ss3(struct psmouse *psmouse)
>  {
>       struct ps2dev *ps2dev = &psmouse->ps2dev;
> -     int reg_val, ret = -1;
> +     unsigned char   f3param0 = 0x00,
> +                                     f3param1 = 0x00;
>
> -     if (alps_enter_command_mode(psmouse) ||
> -         alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
> +     if (alps_enter_command_mode(psmouse))
>               goto error;
>
> -     if (alps_get_v3_v7_resolution(psmouse, 0xc397))
> +     /* Set to 2 pt-mode */
> +     f3param0 = 0x50;
> +     f3param1 = 0x3c;
> +     if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +         ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +         ps2_command(ps2dev, &f3param0, PSMOUSE_CMD_SETRATE) ||
> +         ps2_command(ps2dev, &f3param1, PSMOUSE_CMD_SETRATE))
>               goto error;
>
> -     if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
> -             goto error;
> +     /* output APDATA when 1 finger is in resting area */
> +     f3param0 = 0xc8;
> +     f3param1 = 0x28;
> +     if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +         ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +         ps2_command(ps2dev, &f3param0, PSMOUSE_CMD_SETRATE) ||
> +         ps2_command(ps2dev, &f3param1, PSMOUSE_CMD_SETRATE))
> +                     goto error;
>
> -     reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
> -     if (reg_val == -1)
> -             goto error;
> -     if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
> +     return 0;
> +
> +error:
> +     return -1;
> +}
> +
> +
> +static int alps_hw_init_ss4(struct psmouse *psmouse)
> +{
> +     struct ps2dev *ps2dev = &psmouse->ps2dev;
> +     char param[2] = {0x64, 0x28};
> +     int ret = -1;
> +
> +     /* enter absolute mode */
> +     if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +         ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +         ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
> +         ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE)) {
>               goto error;
> +     }
>
> -     alps_exit_command_mode(psmouse);
>       return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
>
>  error:
> @@ -2078,6 +3043,7 @@ static void alps_set_defaults(struct alps_data *priv)
>               priv->set_abs_params = alps_set_abs_params_st;
>               priv->x_max = 1023;
>               priv->y_max = 767;
> +             priv->slot_number = 1;
>               break;
>       case ALPS_PROTO_V3:
>               priv->hw_init = alps_hw_init_v3;
> @@ -2086,6 +3052,7 @@ static void alps_set_defaults(struct alps_data *priv)
>               priv->decode_fields = alps_decode_pinnacle;
>               priv->nibble_commands = alps_v3_nibble_commands;
>               priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
> +             priv->slot_number = 2;
>               break;
>       case ALPS_PROTO_V4:
>               priv->hw_init = alps_hw_init_v4;
> @@ -2093,6 +3060,7 @@ static void alps_set_defaults(struct alps_data *priv)
>               priv->set_abs_params = alps_set_abs_params_mt;
>               priv->nibble_commands = alps_v4_nibble_commands;
>               priv->addr_command = PSMOUSE_CMD_DISABLE;
> +             priv->slot_number = 2;
>               break;
>       case ALPS_PROTO_V5:
>               priv->hw_init = alps_hw_init_dolphin_v1;
> @@ -2108,6 +3076,7 @@ static void alps_set_defaults(struct alps_data *priv)
>               priv->y_max = 660;
>               priv->x_bits = 23;
>               priv->y_bits = 12;
> +             priv->slot_number = 2;
>               break;
>       case ALPS_PROTO_V6:
>               priv->hw_init = alps_hw_init_v6;
> @@ -2116,6 +3085,7 @@ static void alps_set_defaults(struct alps_data *priv)
>               priv->nibble_commands = alps_v6_nibble_commands;
>               priv->x_max = 2047;
>               priv->y_max = 1535;
> +             priv->slot_number = 1;
>               break;
>       case ALPS_PROTO_V7:
>               priv->hw_init = alps_hw_init_v7;
> @@ -2129,8 +3099,44 @@ static void alps_set_defaults(struct alps_data *priv)
>               priv->byte0 = 0x48;
>               priv->mask0 = 0x48;
>
> -             if (priv->fw_ver[1] != 0xba)
> -                     priv->flags |= ALPS_BUTTONPAD;
> +             if (priv->fw_ver[1] == 0xBA) {
> +                     priv->flags = 0;
> +                     /* No resting finger area */
> +                     priv->resting_zone_y_min = priv->y_max;
> +             } else {
> +                     priv->flags = ALPS_BTNLESS;
> +                     priv->resting_zone_y_min = 0x654;
> +             }
> +
> +             priv->slot_number = 2;
> +             break;
> +     case ALPS_PROTO_V8:
> +             if (priv->fw_ver[1] >= 2)
> +                     priv->decode_fields = alps_decode_ss4_v2;
> +             else
> +                     priv->decode_fields = alps_decode_ss4_v1;
> +
> +             priv->hw_init = alps_hw_init_ss4;
> +             priv->process_packet = alps_process_packet_ss4;
> +             priv->set_abs_params = alps_set_abs_params_mt;
> +             priv->nibble_commands = alps_v3_nibble_commands;
> +             priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
> +             priv->x_max = 0xfff;
> +             priv->y_max = 0x7ff;
> +             priv->byte0 = 0x18;
> +             priv->mask0 = 0x18;
> +             priv->flags = 0;
> +             priv->slot_number = 4;
> +             break;
> +     case ALPS_PROTO_V9:
> +             priv->hw_init = alps_hw_init_ss3;
> +             priv->process_packet = alps_process_touchpad_packet_ss3;
> +             priv->decode_fields = alps_decode_dolphin;
> +             priv->set_abs_params = alps_set_abs_params_mt;
> +             priv->nibble_commands = alps_v3_nibble_commands;
> +             priv->byte0 = 0xc8;
> +             priv->mask0 = 0xc8;
> +             priv->slot_number = 2;
>               break;
>       }
>  }
> @@ -2204,7 +3210,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
>               else
>                       return 0;
>       } else if (ec[0] == 0x88 &&
> -                ((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
> +             ((ec[1] & 0xf0) == 0xB0 || (ec[1] & 0xf0) == 0xC0)) {
>               priv->proto_version = ALPS_PROTO_V7;
>               alps_set_defaults(priv);
>
> @@ -2217,7 +3223,6 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
>               priv->decode_fields = alps_decode_rushmore;
>               priv->x_bits = 16;
>               priv->y_bits = 12;
> -             priv->flags |= ALPS_IS_RUSHMORE;
>
>               /* hack to make addr_command, nibble_command available */
>               psmouse->private = priv;
> @@ -2232,6 +3237,22 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
>               alps_set_defaults(priv);
>
>               return 0;
> +     } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x14) {
> +             priv->proto_version = ALPS_PROTO_V8;
> +             alps_set_defaults(priv);
> +
> +             if (alps_set_defaults_ss4(psmouse, priv))
> +                     return -EIO;
> +
> +             return 0;
> +     } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8) {
> +             priv->proto_version = ALPS_PROTO_V9;
> +             alps_set_defaults(priv);
> +
> +             if (alps_dolphin_get_device_area(psmouse, priv))
> +                     return -EIO;
> +
> +             return 0;
>       }
>
>       psmouse_info(psmouse,
> @@ -2272,21 +3293,17 @@ static void alps_set_abs_params_st(struct alps_data *priv,
>  static void alps_set_abs_params_mt(struct alps_data *priv,
>                                  struct input_dev *dev1)
>  {
> +     set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
> +     input_mt_init_slots(dev1, priv->slot_number, 0);
>       input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
>       input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
>
> -     input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
> -     input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
> -
> -     input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
> -             INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
> -
> +     set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
>       set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
>       set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
>
> -     /* V7 is real multi-touch */
> -     if (priv->proto_version == ALPS_PROTO_V7)
> -             clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
> +     input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
> +     input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
>  }
>
>  int alps_init(struct psmouse *psmouse)
> @@ -2332,9 +3349,7 @@ int alps_init(struct psmouse *psmouse)
>       dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
>
>       priv->set_abs_params(priv, dev1);
> -     /* No pressure on V7 */
> -     if (priv->proto_version != ALPS_PROTO_V7)
> -             input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
> +     input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
>
>       if (priv->flags & ALPS_WHEEL) {
>               dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
> @@ -2351,14 +3366,12 @@ int alps_init(struct psmouse *psmouse)
>               dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
>               dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
>               dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
> -     } else if (priv->flags & ALPS_BUTTONPAD) {
> -             set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit);
> -             clear_bit(BTN_RIGHT, dev1->keybit);
>       } else {
>               dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
>       }
>
> -     snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
> +     snprintf(priv->phys, sizeof(priv->phys),
> +             "%s/input1", psmouse->ps2dev.serio->phys);
>       dev2->phys = priv->phys;
>       dev2->name = (priv->flags & ALPS_DUALPOINT) ?
>                    "DualPoint Stick" : "ALPS PS/2 Device";
> @@ -2382,7 +3395,8 @@ int alps_init(struct psmouse *psmouse)
>       psmouse->reconnect = alps_reconnect;
>       psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6;
>
> -     /* We are having trouble resyncing ALPS touchpads so disable it for now */
> +     /* We are having trouble resyncing ALPS touchpads
> +             so disable it for now */
>       psmouse->resync_time = 0;
>
>       return 0;
> diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
> index 66240b4..55f5193 100644
> --- a/drivers/input/mouse/alps.h
> +++ b/drivers/input/mouse/alps.h
> @@ -12,8 +12,6 @@
>  #ifndef _ALPS_H
>  #define _ALPS_H
>
> -#include <linux/input/mt.h>
> -
>  #define ALPS_PROTO_V1        1
>  #define ALPS_PROTO_V2        2
>  #define ALPS_PROTO_V3        3
> @@ -21,8 +19,17 @@
>  #define ALPS_PROTO_V5        5
>  #define ALPS_PROTO_V6        6
>  #define ALPS_PROTO_V7        7       /* t3btl t4s */
> +#define ALPS_PROTO_V8        8       /* ss4 */
> +#define ALPS_PROTO_V9        9       /* ss3btl */
> +
> +
> +#define MAX_IMG_PT_NUM               5
> +#define V7_IMG_PT_NUM                2
>
> -#define MAX_TOUCHES  2
> +#define ZONE_NORMAL                          0x01
> +#define ZONE_RESTING                 0x02
> +#define ZONE_LEFT_BTN                        0x04
> +#define ZONE_RIGHT_BTN                       0x08
>
>  #define DOLPHIN_COUNT_PER_ELECTRODE  64
>  #define DOLPHIN_PROFILE_XOFFSET              8       /* x-electrode offset */
> @@ -45,6 +52,124 @@ enum V7_PACKET_ID {
>        V7_PACKET_ID_UNKNOWN,
>  };
>
> +enum SS4_PACKET_ID {
> +     SS4_PACKET_ID_IDLE = 0,
> +     SS4_PACKET_ID_ONE,
> +     SS4_PACKET_ID_TWO,
> +     SS4_PACKET_ID_MULTI,
> +};
> +
> +#define SS4_COUNT_PER_ELECTRODE                      256
> +#define SS4_NUMSENSOR_XOFFSET                        7
> +#define SS4_NUMSENSOR_YOFFSET                        7
> +
> +/* SWC status( 0:SW OFF, 1: SW ON) */
> +#define SS4_BUTTONLESS_BUTTONS       0x01
> +
> +#define SS4_MASK_NORMAL_BUTTONS      0x07
> +
> +#define SS4_1F_X_V1(_b)      (((_b[0] << 5) & 0x1E00) | \
> +             ((_b[0] << 6) & 0x01C0) | \
> +             ((_b[1] >> 2) & 0x0038) | \
> +             ((_b[1] >> 1) & 0x0007) \
> +             )
> +
> +#define SS4_1F_Y_V1(_b)      (((_b[2] >> 1) & 0x007F) | \
> +             ((_b[4] << 5) & 0x0F80) \
> +             )
> +
> +#define SS4_1F_Z_V1(_b)              (_b[5] & 0xFF)
> +
> +#define      SS4_1F_LFB(_b)          ((_b[4] >> 1) & 0x01)
> +
> +#define SS4_BTN_V1(_b)               (_b[3] & SS4_BUTTONLESS_BUTTONS)
> +
> +#define SS4_STD_MF_X_V1(_b, _i)      (((_b[0 + _i * 3] >> 2) & 0x0020) | \
> +             ((_b[1 + _i * 3] << 5) & 0x1FC0) \
> +             )
> +
> +#define SS4_STD_MF_Y_V1(_b, _i)      (((_b[0 + _i * 3] >> 2) & 0x0010) | \
> +             ((_b[2 + _i * 3] << 4) & 0x0FE0) \
> +             )
> +
> +#define SS4_BTL_BTN_V1(_b)           (_b[3] & SS4_BUTTONLESS_BUTTONS)
> +
> +#define SS4_BTL_MF_X_V1(_b, _i) (SS4_STD_MF_X_V1(_b, _i) | \
> +             ((_b[0 + _i * 3] << 2) & 0x0010))
> +
> +#define SS4_BTL_MF_Y_V1(_b, _i) (SS4_STD_MF_Y_V1(_b, _i) | \
> +             ((_b[0 + _i * 3] << 2) & 0x0008))
> +
> +#define SS4_MF_Z_V1(_b, _i)          ((_b[0 + _i * 3] >> 4) & 0x0003)
> +
> +#define SS4_IS_MF_CONTINUE_V1(_b)    ((_b[0] & 0x01) == 0x01)
> +#define SS4_IS_5F_DETECTED_V1(_b)    ((_b[0] & 0x01) == 0x01)
> +
> +#define SS4_1F_X_V2(_b)      ((_b[0] & 0x0007) | \
> +             ((_b[1] << 3) & 0x0078) | \
> +             ((_b[1] << 2) & 0x0380) | \
> +             ((_b[2] << 5) & 0x1C00) \
> +             )
> +
> +#define SS4_1F_Y_V2(_b)      (((_b[2]) & 0x000F) | \
> +             ((_b[3] >> 2) & 0x0030) | \
> +             ((_b[4] << 6) & 0x03C0) | \
> +             ((_b[4] << 5) & 0x0C00) \
> +             )
> +
> +#define SS4_1F_Z_V2(_b)      (((_b[5]) & 0x0F) | \
> +             ((_b[5] >> 1) & 0x70) | \
> +             ((_b[4]) & 0x80) \
> +             )
> +
> +#define      SS4_1F_LFB_V2(_b)       (((_b[2] >> 4) & 0x01) == 0x01)
> +
> +#define      SS4_MF_LF_V2(_b, _i)    ((_b[1 + _i * 3] & 0x0004) == 0x0004)
> +
> +#define SS4_BTN_V2(_b)               ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS)
> +
> +#define SS4_STD_MF_X_V2(_b, _i)      (((_b[0 + _i * 3]  << 5) & 0x00E0) | \
> +             ((_b[1 + _i * 3]  << 5) & 0x1F00) \
> +             )
> +
> +#define SS4_STD_MF_Y_V2(_b, _i)      (((_b[1 + _i * 3] << 3) & 0x0010) | \
> +             ((_b[2 + _i * 3] << 5) & 0x01E0) | \
> +             ((_b[2 + _i * 3] << 4) & 0x0E00) \
> +             )
> +
> +#define SS4_BTL_MF_X_V2(_b, _i)      (SS4_STD_MF_X_V2(_b, _i) | \
> +             ((_b[0 + _i * 3] >> 3) & 0x0010))
> +
> +#define SS4_BTL_MF_Y_V2(_b, _i)      (SS4_STD_MF_Y_V2(_b, _i) | \
> +             ((_b[0 + _i * 3] >> 3) & 0x0008))
> +
> +#define SS4_MF_Z_V2(_b, _i)  (((_b[1 + _i * 3]) & 0x0001) | \
> +             ((_b[1 + _i * 3] >> 1) & 0x0002) \
> +             )
> +
> +#define SS4_IS_MF_CONTINUE_V2(_b)    ((_b[2] & 0x10) == 0x10)
> +#define SS4_IS_5F_DETECTED_V2(_b)    ((_b[2] & 0x10) == 0x10)
> +
> +
> +#define SS4_MFPACKET_NO_AX           8160    /* X-Coordinate value */
> +#define SS4_MFPACKET_NO_AY           4080    /* Y-Coordinate value */
> +#define SS4_MFPACKET_NO_AX_BL        8176    /* Buttonless X-Coordinate value */
> +#define SS4_MFPACKET_NO_AY_BL        4088    /* Buttonless Y-Coordinate value */
> +
> +/* Threshold for resting finger's large movement */
> +#define RESTING_FN_LARGE_MOVEMENT    50
> +
> +#define PT_IN_LEFT_BTN_AREA(_x, _y, _x_max, _y_max)
> +             (((_x) < (_x_max)/2) && ((_y) > (_y_max)*4/5))
> +
> +#define PT_IN_RIGHT_BTN_AREA(_x, _y, _x_max, _y_max)
> +             (((_x) >= (_x_max)/2) && ((_y) > (_y_max)*4/5))
> +
> +#define PT_IN_BTN_AREA(_x, _y, _x_max, _y_max)
> +             (PT_IN_LEFT_BTN_AREA(_x, _y, _x_max, _y_max) ||
> +             PT_IN_RIGHT_BTN_AREA(_x, _y, _x_max, _y_max)
> +             )
> +
>  /**
>   * struct alps_model_info - touchpad ID table
>   * @signature: E7 response string to match.
> @@ -68,7 +193,7 @@ struct alps_model_info {
>       unsigned char command_mode_resp;
>       unsigned char proto_version;
>       unsigned char byte0, mask0;
> -     int flags;
> +     unsigned int flags;
>  };
>
>  /**
> @@ -87,47 +212,95 @@ struct alps_nibble_commands {
>       unsigned char data;
>  };
>
> -struct alps_bitmap_point {
> -     int start_bit;
> -     int num_bits;
> +/**
> + * struct alps_btn - decoded version of the button status
> + * @left: Left touchpad button is active.
> + * @right: Right touchpad button is active.
> + * @middle: Middle touchpad button is active.
> + * @ts_left: Left trackstick button is active.
> + * @ts_right: Right trackstick button is active.
> + * @ts_middle: Middle trackstick button is active.
> + */
> +struct alps_btn {
> +     unsigned int left:1;
> +     unsigned int right:1;
> +     unsigned int middle:1;
> +
> +     unsigned int ts_left:1;
> +     unsigned int ts_right:1;
> +     unsigned int ts_middle:1;
> +};
> +
> +/**
> + * struct alps_btn - decoded version of the X Y Z postion for ST.
> + * @x: X position for ST.
> + * @y: Y position for ST.
> + * @z: Z position for ST.
> + */
> +struct alps_abs_data {
> +     unsigned int x;
> +     unsigned int y;
> +     unsigned int z;
> +};
> +
> +enum dol_Packet {
> +     DOL_UNKNOWN,
> +     DOL_GPDATA,
> +     DOL_PROFDATA,
> +     DOL_APDATA
>  };
>
>  /**
>   * struct alps_fields - decoded version of the report packet
> + * @fingers: Number of fingers for MT.
> + * @pt: X Y Z postion for ST.
> + * @pt: X Y Z postion for image MT.
>   * @x_map: Bitmap of active X positions for MT.
>   * @y_map: Bitmap of active Y positions for MT.
> - * @fingers: Number of fingers for MT.
> - * @pressure: Pressure.
> - * @st: position for ST.
> - * @mt: position for MT.
>   * @first_mp: Packet is the first of a multi-packet report.
>   * @is_mp: Packet is part of a multi-packet report.
> - * @left: Left touchpad button is active.
> - * @right: Right touchpad button is active.
> - * @middle: Middle touchpad button is active.
> - * @ts_left: Left trackstick button is active.
> - * @ts_right: Right trackstick button is active.
> - * @ts_middle: Middle trackstick button is active.
> + * @btn: Button activity status
>   */
>  struct alps_fields {
> +     unsigned int fingers;
> +     struct alps_abs_data pt;
> +     struct alps_abs_data pt_img[MAX_IMG_PT_NUM];
>       unsigned int x_map;
>       unsigned int y_map;
> -     unsigned int fingers;
> -
> -     int pressure;
> -     struct input_mt_pos st;
> -     struct input_mt_pos mt[MAX_TOUCHES];
> -
> +     unsigned int large_fn;
>       unsigned int first_mp:1;
>       unsigned int is_mp:1;
> +     enum dol_Packet dol_packet_type;
> +     struct alps_btn btn;
> +};
>
> -     unsigned int left:1;
> -     unsigned int right:1;
> -     unsigned int middle:1;
> +/**
> + * struct v7_raw - data decoded from raw packet for V7.
> + * @pkt_id: An id that specifies the type of packet.
> + * @additional_fingers: Number of additional finger that is neighter included
> + *  in pt slot nor reflected in rest_left and rest_right flag of data packet.
> + * @rest_left: There are fingers on left resting zone.
> + * @rest_right: There are fingers on right resting zone.
> + */
> +struct v7_raw {
> +     unsigned char pkt_id;
> +     unsigned int additional_fingers;
> +     unsigned char rest_left;
> +     unsigned char rest_right;
> +};
>
> -     unsigned int ts_left:1;
> -     unsigned int ts_right:1;
> -     unsigned int ts_middle:1;
> +/**
> + * struct alps_bl_pt_attr - generic attributes of touch points for buttonless device
> + * @zone: The part of touchpad that the touch point locates
> + * @is_counted: The touch point is not a resting finger.
> + * @is_init_pt_got: The touch down point is got.
> + * @init_pt: The X Y Z position of the touch down point.
> + */
> +struct alps_bl_pt_attr {
> +     unsigned char zone;
> +     unsigned char is_counted;
> +     unsigned char is_init_pt_got;
> +     struct alps_abs_data init_pt;
>  };
>
>  /**
> @@ -142,12 +315,13 @@ struct alps_fields {
>   *   known format for this model.  The first byte of the report, ANDed with
>   *   mask0, should match byte0.
>   * @mask0: The mask used to check the first byte of the report.
> - * @fw_ver: cached copy of firmware version (EC report)
>   * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
>   * @x_max: Largest possible X position value.
>   * @y_max: Largest possible Y position value.
> + * @resting_zone_y_min: Smallest Y postion value of the bottom resting zone.
>   * @x_bits: Number of X bits in the MT bitmap.
>   * @y_bits: Number of Y bits in the MT bitmap.
> + * @img_fingers: Number of image fingers.
>   * @hw_init: Protocol-specific hardware init function.
>   * @process_packet: Protocol-specific function to process a report packet.
>   * @decode_fields: Protocol-specific function to read packet bitfields.
> @@ -155,9 +329,18 @@ struct alps_fields {
>   * @prev_fin: Finger bit from previous packet.
>   * @multi_packet: Multi-packet data in progress.
>   * @multi_data: Saved multi-packet data.
> - * @f: Decoded packet data fields.
> + * @x1: First X coordinate from last MT report.
> + * @x2: Second X coordinate from last MT report.
> + * @y1: First Y coordinate from last MT report.
> + * @y2: Second Y coordinate from last MT report.
> + * @fingers: Number of fingers from last MT report.
>   * @quirks: Bitmap of ALPS_QUIRK_*.
>   * @timer: Timer for flushing out the final report packet in the stream.
> + * @v7: Data decoded from raw packet for V7
> + * @phy_btn: Physical button is active.
> + * @prev_phy_btn: Physical button of previous packet is active.
> + * @pressed_btn_bits: Pressed positon of button zone
> + * @pt_attr: Generic attributes of touch points for buttonless device.
>   */
>  struct alps_data {
>       struct input_dev *dev2;
> @@ -168,27 +351,35 @@ struct alps_data {
>       int addr_command;
>       unsigned char proto_version;
>       unsigned char byte0, mask0;
> +     unsigned int flags;
>       unsigned char fw_ver[3];
> -     int flags;
>       int x_max;
>       int y_max;
> +     int resting_zone_y_min;
>       int x_bits;
>       int y_bits;
> -     unsigned int x_res;
> -     unsigned int y_res;
> +     unsigned char slot_number;
>
>       int (*hw_init)(struct psmouse *psmouse);
>       void (*process_packet)(struct psmouse *psmouse);
> -     int (*decode_fields)(struct alps_fields *f, unsigned char *p,
> +     void (*decode_fields)(struct alps_fields *f, unsigned char *p,
>                             struct psmouse *psmouse);
>       void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
>
>       int prev_fin;
>       int multi_packet;
>       unsigned char multi_data[6];
> -     struct alps_fields f;
> +     int x1, x2, y1, y2;
> +     int fingers;
>       u8 quirks;
>       struct timer_list timer;
> +
> +     /* these are used for buttonless touchpad*/
> +     union {
> +             struct v7_raw v7;
> +     } r;
> +     struct alps_btn prev_btn;
> +     struct alps_bl_pt_attr pt_attr[MAX_IMG_PT_NUM];
>  };
>
>  #define ALPS_QUIRK_TRACKSTICK_BUTTONS        1 /* trakcstick buttons in trackstick packet */
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH 0/4] Touchscreen performance related fixes
From: Vignesh R @ 2014-10-27 11:08 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Benoit Cousson, Tony Lindgren, Russell King, Jonathan Cameron,
	Dmitry Torokhov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Samuel Ortiz,
	Lee Jones, Felipe Balbi, Sebastian Andrzej Siewior, Jan Kardell,
	Paul Gortmaker, Brad Griffis, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA, Vignesh R

This series of patches fix TSC defects related to lag in touchscreen
performance and cursor jump at touch release. The lag was result of
udelay in TSC interrupt handler. Cursor jump due to false pen-up event.
The patches implement Advisory 1.0.31 in silicon errata of am335x-evm
to avoid false pen-up events and remove udelay. The advisory says to use
steps 1 to 4 for ADC and 5 to 16 for TSC (assuming 4 wire TSC and 4 channel
ADC). Further the X co-ordinate must be the last one to be sampled just
before charge step. The first two patches implement the required changes. 

A DT parameter to configure the duration of tsc charge step. It represents
number of ADC clock cycles to wait between applying the step configuration
registers and going back to the IDLE state. The charge delay value can vary
across boards. Configuring correct value of charge delay is important to avoid
false pen-up events. Hence it is necessary to expose charge-delay value as
DT parameter. The pen-up detection happens immediately after the charge step
so this does in fact function as a hardware knob for adjusting the amount of
settling time.

After applying these changes false pen-up events have not be observed and
smooth circles can be drawn on touch screen. The performance is much better
in recognizing quick movement across the screen. No lag or cursor jump is
observed.

Brad Griffis (2):
  input: touchscreen: ti_am335x_tsc Interchange touchscreen and ADC
    steps
  input: touchscreen: ti_am335x_tsc: Remove udelay in interrupt handler

Vignesh R (2):
  arm: boot: dts: am335x-evm: Make charge delay a DT parameter for tsc
  input: touchscreen: ti_am335x_tsc: Use charge delay DT parameter

 .../bindings/input/touchscreen/ti-tsc-adc.txt      |  13 +++
 arch/arm/boot/dts/am335x-evm.dts                   |   1 +
 drivers/iio/adc/ti_am335x_adc.c                    |   2 +-
 drivers/input/touchscreen/ti_am335x_tsc.c          | 106 ++++++++++-----------
 drivers/mfd/ti_am335x_tscadc.c                     |   7 +-
 include/linux/mfd/ti_am335x_tscadc.h               |   4 +-
 6 files changed, 76 insertions(+), 57 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH 1/4] input: touchscreen: ti_am335x_tsc Interchange touchscreen and ADC steps
From: Vignesh R @ 2014-10-27 11:08 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Benoit Cousson, Tony Lindgren, Russell King, Jonathan Cameron,
	Dmitry Torokhov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Samuel Ortiz,
	Lee Jones, Felipe Balbi, Sebastian Andrzej Siewior, Jan Kardell,
	Paul Gortmaker, Brad Griffis, devicetree, linux-kernel,
	linux-omap, linux-arm-kernel, linux-iio, linux-input, Vignesh R
In-Reply-To: <1414408111-2631-1-git-send-email-vigneshr@ti.com>

From: Brad Griffis <bgriffis@ti.com>

This patch makes the initial changes required to workaround TSC-false
pen-up interrupts. It is required to implement these changes in order to
remove udelay in the TSC interrupt handler and false pen-up events.
The charge step is to be executed immediately after sampling X+. Hence
TSC is made to use higher numbered steps (steps 5 to 16 for 5 co-ordinate
readouts, 4 wire TSC configuration) and ADC to use lower ones. Further
X co-ordinate readouts must be the last to be sampled, thus co-ordinates
are sampled in the order Y-Z-X.

Signed-off-by: Brad Griffis <bgriffis@ti.com>
[vigneshr@ti.com: Ported the patch from v3.12 to v3.18rc2]
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 drivers/iio/adc/ti_am335x_adc.c           |  2 +-
 drivers/input/touchscreen/ti_am335x_tsc.c | 42 ++++++++++++++++++-------------
 2 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index b730864731e8..3f530ed6bd80 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -98,7 +98,7 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
 	 * needs to be given to ADC to digitalize data.
 	 */
 
-	steps = TOTAL_STEPS - adc_dev->channels;
+	steps = 0;
 	if (iio_buffer_enabled(indio_dev))
 		stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
 					| STEPCONFIG_MODE_SWCNT;
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 2ce649520fe0..1aeac9675fe7 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -121,7 +121,7 @@ static void titsc_step_config(struct titsc *ts_dev)
 {
 	unsigned int	config;
 	int i;
-	int end_step;
+	int end_step, first_step, tsc_steps;
 	u32 stepenable;
 
 	config = STEPCONFIG_MODE_HWSYNC |
@@ -140,9 +140,11 @@ static void titsc_step_config(struct titsc *ts_dev)
 		break;
 	}
 
-	/* 1 … coordinate_readouts is for X */
-	end_step = ts_dev->coordinate_readouts;
-	for (i = 0; i < end_step; i++) {
+	tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
+	first_step = TOTAL_STEPS - tsc_steps;
+	/* Steps 16 to 16-coordinate_readouts is for X */
+	end_step = first_step + tsc_steps;
+	for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) {
 		titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
 		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
 	}
@@ -164,9 +166,9 @@ static void titsc_step_config(struct titsc *ts_dev)
 		break;
 	}
 
-	/* coordinate_readouts … coordinate_readouts * 2 is for Y */
-	end_step = ts_dev->coordinate_readouts * 2;
-	for (i = ts_dev->coordinate_readouts; i < end_step; i++) {
+	/* 1 ... coordinate_readouts is for Y */
+	end_step = first_step + ts_dev->coordinate_readouts;
+	for (i = first_step; i < end_step; i++) {
 		titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
 		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
 	}
@@ -179,7 +181,7 @@ static void titsc_step_config(struct titsc *ts_dev)
 	titsc_writel(ts_dev, REG_CHARGECONFIG, config);
 	titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
 
-	/* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */
+	/* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
 	config = STEPCONFIG_MODE_HWSYNC |
 			STEPCONFIG_AVG_16 | ts_dev->bit_yp |
 			ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
@@ -194,8 +196,11 @@ static void titsc_step_config(struct titsc *ts_dev)
 	titsc_writel(ts_dev, REG_STEPDELAY(end_step),
 			STEPCONFIG_OPENDLY);
 
-	/* The steps1 … end and bit 0 for TS_Charge */
-	stepenable = (1 << (end_step + 2)) - 1;
+	/* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
+	stepenable = 1;
+	for (i = 0; i < tsc_steps; i++)
+		stepenable |= 1 << (first_step + i + 1);
+
 	ts_dev->step_mask = stepenable;
 	am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
 }
@@ -209,6 +214,7 @@ static void titsc_read_coordinates(struct titsc *ts_dev,
 	unsigned int read, diff;
 	unsigned int i, channel;
 	unsigned int creads = ts_dev->coordinate_readouts;
+	unsigned int first_step = TOTAL_STEPS - (creads * 2 + 2);
 
 	*z1 = *z2 = 0;
 	if (fifocount % (creads * 2 + 2))
@@ -226,7 +232,7 @@ static void titsc_read_coordinates(struct titsc *ts_dev,
 
 		channel = (read & 0xf0000) >> 16;
 		read &= 0xfff;
-		if (channel < creads) {
+		if (channel > first_step + creads + 2) {
 			diff = abs(read - prev_val_x);
 			if (diff < prev_diff_x) {
 				prev_diff_x = diff;
@@ -234,19 +240,19 @@ static void titsc_read_coordinates(struct titsc *ts_dev,
 			}
 			prev_val_x = read;
 
-		} else if (channel < creads * 2) {
+		} else if (channel == first_step + creads + 1) {
+			*z1 = read;
+
+		} else if (channel == first_step + creads + 2) {
+			*z2 = read;
+
+		} else if (channel > first_step) {
 			diff = abs(read - prev_val_y);
 			if (diff < prev_diff_y) {
 				prev_diff_y = diff;
 				*y = read;
 			}
 			prev_val_y = read;
-
-		} else if (channel < creads * 2 + 1) {
-			*z1 = read;
-
-		} else if (channel < creads * 2 + 2) {
-			*z2 = read;
 		}
 	}
 }
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/4] input: touchscreen: ti_am335x_tsc: Remove udelay in interrupt handler
From: Vignesh R @ 2014-10-27 11:08 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Benoit Cousson, Tony Lindgren, Russell King, Jonathan Cameron,
	Dmitry Torokhov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Samuel Ortiz,
	Lee Jones, Felipe Balbi, Sebastian Andrzej Siewior, Jan Kardell,
	Paul Gortmaker, Brad Griffis, devicetree, linux-kernel,
	linux-omap, linux-arm-kernel, linux-iio, linux-input, Vignesh R
In-Reply-To: <1414408111-2631-1-git-send-email-vigneshr@ti.com>

From: Brad Griffis <bgriffis@ti.com>

TSC interrupt handler had udelay to avoid reporting of false pen-up
interrupt to user space. This patch implements workaround suggesting in
Advisory 1.0.31 of silicon errata for am335x, thus eliminating udelay
and touchscreen lag. This also improves performance of touchscreen and
eliminates sudden jump of cursor at touch release.

IDLECONFIG and CHARGECONFIG registers are to be configured
with same values in order to eliminate false pen-up events. This
workaround may result in false pen-down to be detected, hence considerable
charge step delay needs to be added. The charge delay is set to 0xB000
(in terms of ADC clock cycles) by default.

TSC steps are disabled at the end of every sampling cycle and EOS bit is
set. Once the EOS bit is set, the TSC steps need to be re-enabled to begin
next sampling cycle.

In one shot mode, sequencer automatically disables all enabled steps at
the end of each cycle. (both ADC steps and TSC steps) Hence these steps
need not be saved in reg_se_cache for clearing these steps at a later
stage.

Signed-off-by: Brad Griffis <bgriffis@ti.com>
[vigneshr@ti.com: Ported patch from v3.12 to v3.18rc2]
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 drivers/input/touchscreen/ti_am335x_tsc.c | 56 ++++++++++++-------------------
 drivers/mfd/ti_am335x_tscadc.c            |  7 ++--
 include/linux/mfd/ti_am335x_tscadc.h      |  4 ++-
 3 files changed, 30 insertions(+), 37 deletions(-)

diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 1aeac9675fe7..483fd97c0e0c 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -173,11 +173,9 @@ static void titsc_step_config(struct titsc *ts_dev)
 		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
 	}
 
-	/* Charge step configuration */
-	config = ts_dev->bit_xp | ts_dev->bit_yn |
-			STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
-			STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp);
+	/* Make CHARGECONFIG same as IDLECONFIG */
 
+	config = titsc_readl(ts_dev, REG_IDLECONFIG);
 	titsc_writel(ts_dev, REG_CHARGECONFIG, config);
 	titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
 
@@ -264,9 +262,26 @@ static irqreturn_t titsc_irq(int irq, void *dev)
 	unsigned int status, irqclr = 0;
 	unsigned int x = 0, y = 0;
 	unsigned int z1, z2, z;
-	unsigned int fsm;
 
-	status = titsc_readl(ts_dev, REG_IRQSTATUS);
+	status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
+	if (status & IRQENB_HW_PEN) {
+		ts_dev->pen_down = true;
+		titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
+		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+		irqclr |= IRQENB_HW_PEN;
+	}
+
+	if (status & IRQENB_PENUP) {
+		ts_dev->pen_down = false;
+		input_report_key(input_dev, BTN_TOUCH, 0);
+		input_report_abs(input_dev, ABS_PRESSURE, 0);
+		input_sync(input_dev);
+		irqclr |= IRQENB_PENUP;
+	}
+
+	if (status & IRQENB_EOS)
+		irqclr |= IRQENB_EOS;
+
 	/*
 	 * ADC and touchscreen share the IRQ line.
 	 * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
@@ -297,34 +312,6 @@ static irqreturn_t titsc_irq(int irq, void *dev)
 		}
 		irqclr |= IRQENB_FIFO0THRES;
 	}
-
-	/*
-	 * Time for sequencer to settle, to read
-	 * correct state of the sequencer.
-	 */
-	udelay(SEQ_SETTLE);
-
-	status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
-	if (status & IRQENB_PENUP) {
-		/* Pen up event */
-		fsm = titsc_readl(ts_dev, REG_ADCFSM);
-		if (fsm == ADCFSM_STEPID) {
-			ts_dev->pen_down = false;
-			input_report_key(input_dev, BTN_TOUCH, 0);
-			input_report_abs(input_dev, ABS_PRESSURE, 0);
-			input_sync(input_dev);
-		} else {
-			ts_dev->pen_down = true;
-		}
-		irqclr |= IRQENB_PENUP;
-	}
-
-	if (status & IRQENB_HW_PEN) {
-
-		titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
-		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
-	}
-
 	if (irqclr) {
 		titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
 		am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
@@ -417,6 +404,7 @@ static int titsc_probe(struct platform_device *pdev)
 	}
 
 	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
+	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
 	err = titsc_config_wires(ts_dev);
 	if (err) {
 		dev_err(&pdev->dev, "wrong i/p wire configuration\n");
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index d877e777cce6..94ef8992f46b 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -86,8 +86,12 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
 		spin_lock_irq(&tsadc->reg_lock);
 		finish_wait(&tsadc->reg_se_wait, &wait);
 
+		/*
+		 * Sequencer should either be idle or
+		 * busy applying the charge step.
+		 */
 		reg = tscadc_readl(tsadc, REG_ADCFSM);
-		WARN_ON(reg & SEQ_STATUS);
+		WARN_ON(reg & SEQ_STATUS & (!CHARGE_STEP));
 		tsadc->adc_waiting = false;
 	}
 	tsadc->adc_in_use = true;
@@ -96,7 +100,6 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc)
 void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val)
 {
 	spin_lock_irq(&tsadc->reg_lock);
-	tsadc->reg_se_cache |= val;
 	am335x_tscadc_need_adc(tsadc);
 
 	tscadc_writel(tsadc, REG_SE, val);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e2e70053470e..fcce182e4a35 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -52,6 +52,7 @@
 
 /* IRQ enable */
 #define IRQENB_HW_PEN		BIT(0)
+#define IRQENB_EOS		BIT(1)
 #define IRQENB_FIFO0THRES	BIT(2)
 #define IRQENB_FIFO0OVRRUN	BIT(3)
 #define IRQENB_FIFO0UNDRFLW	BIT(4)
@@ -107,7 +108,7 @@
 /* Charge delay */
 #define CHARGEDLY_OPEN_MASK	(0x3FFFF << 0)
 #define CHARGEDLY_OPEN(val)	((val) << 0)
-#define CHARGEDLY_OPENDLY	CHARGEDLY_OPEN(1)
+#define CHARGEDLY_OPENDLY	CHARGEDLY_OPEN(0xB000)
 
 /* Control register */
 #define CNTRLREG_TSCSSENB	BIT(0)
@@ -127,6 +128,7 @@
 
 /* Sequencer Status */
 #define SEQ_STATUS BIT(5)
+#define CHARGE_STEP		0x11
 
 #define ADC_CLK			3000000
 #define TOTAL_STEPS		16
-- 
1.9.1

^ permalink raw reply related

* [PATCH 3/4] arm: boot: dts: am335x-evm: Make charge delay a DT parameter for tsc
From: Vignesh R @ 2014-10-27 11:08 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Benoit Cousson, Tony Lindgren, Russell King, Jonathan Cameron,
	Dmitry Torokhov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Samuel Ortiz,
	Lee Jones, Felipe Balbi, Sebastian Andrzej Siewior, Jan Kardell,
	Paul Gortmaker, Brad Griffis, devicetree, linux-kernel,
	linux-omap, linux-arm-kernel, linux-iio, linux-input, Vignesh R
In-Reply-To: <1414408111-2631-1-git-send-email-vigneshr@ti.com>

The charge delay value is by default 0xB000. But it can be set to lower
values on some boards as long as false pen-ups are avoided. Lowering the
value increases the sampling rate (though current sampling rate is
sufficient for tsc operation). Hence charge delay has been made a DT
parameter.

Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 .../devicetree/bindings/input/touchscreen/ti-tsc-adc.txt    | 13 +++++++++++++
 arch/arm/boot/dts/am335x-evm.dts                            |  1 +
 2 files changed, 14 insertions(+)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
index 878549ba814d..ac62769e70e4 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
@@ -28,6 +28,18 @@ Required properties:
 	ti,adc-channels: List of analog inputs available for ADC.
 			 AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7.
 
+Optional properties:
+- child "tsc"
+	ti,charge-delay: Length of touch screen charge delay step in terms of
+			 ADC clock cycles. Charge delay value should be large in order
+			 to avoid false pen-up events. This value affects the overall
+			 sampling speed hence need to be kept as low as possible while
+			 avoiding false pen-up event. Start from a lower value say 0x400
+			 and increase value until false pen-up events are avoided. The
+			 pen-up detection happens immediately after the charge step
+			 so this does in fact function as a hardware knob for adjusting
+			 the amount of "settling time".
+
 Example:
 	tscadc: tscadc@44e0d000 {
 		compatible = "ti,am3359-tscadc";
@@ -36,6 +48,7 @@ Example:
 			ti,x-plate-resistance = <200>;
 			ti,coordiante-readouts = <5>;
 			ti,wire-config = <0x00 0x11 0x22 0x33>;
+			ti,charge-delay = <0xB000>;
 		};
 
 		adc {
diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts
index e2156a583de7..80be0462298b 100644
--- a/arch/arm/boot/dts/am335x-evm.dts
+++ b/arch/arm/boot/dts/am335x-evm.dts
@@ -641,6 +641,7 @@
 		ti,x-plate-resistance = <200>;
 		ti,coordinate-readouts = <5>;
 		ti,wire-config = <0x00 0x11 0x22 0x33>;
+		ti,charge-delay = <0xB000>;
 	};
 
 	adc {
-- 
1.9.1

^ permalink raw reply related

* [PATCH 4/4] input: touchscreen: ti_am335x_tsc: Use charge delay DT parameter
From: Vignesh R @ 2014-10-27 11:08 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Benoit Cousson, Tony Lindgren, Russell King, Jonathan Cameron,
	Dmitry Torokhov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Samuel Ortiz,
	Lee Jones, Felipe Balbi, Sebastian Andrzej Siewior, Jan Kardell,
	Paul Gortmaker, Brad Griffis, devicetree, linux-kernel,
	linux-omap, linux-arm-kernel, linux-iio, linux-input, Vignesh R
In-Reply-To: <1414408111-2631-1-git-send-email-vigneshr@ti.com>

This patch reads charge delay from tsc DT node and writes to
REG_CHARGEDELAY register. If the charge delay is not specified in DT
then default value of 0xB000(CHARGEDLY_OPENDLY) is used.

Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 drivers/input/touchscreen/ti_am335x_tsc.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 483fd97c0e0c..20ce76b1b6e7 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -52,6 +52,7 @@ struct titsc {
 	u32			bit_xp, bit_xn, bit_yp, bit_yn;
 	u32			inp_xp, inp_xn, inp_yp, inp_yn;
 	u32			step_mask;
+	u32			charge_delay;
 };
 
 static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
@@ -177,7 +178,7 @@ static void titsc_step_config(struct titsc *ts_dev)
 
 	config = titsc_readl(ts_dev, REG_IDLECONFIG);
 	titsc_writel(ts_dev, REG_CHARGECONFIG, config);
-	titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
+	titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);
 
 	/* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
 	config = STEPCONFIG_MODE_HWSYNC |
@@ -361,6 +362,11 @@ static int titsc_parse_dt(struct platform_device *pdev,
 	if (err < 0)
 		return err;
 
+	err = of_property_read_u32(node, "ti,charge-delay",
+				   &ts_dev->charge_delay);
+	if (err < 0)
+		ts_dev->charge_delay = CHARGEDLY_OPENDLY;
+
 	return of_property_read_u32_array(node, "ti,wire-config",
 			ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp));
 }
-- 
1.9.1

^ permalink raw reply related

* [PATCH] Input: spear-keyboard - Add CONFIG_PM_SLEEP to suspend/resume functions
From: Jingoo Han @ 2014-10-27 12:32 UTC (permalink / raw)
  To: 'Dmitry Torokhov'
  Cc: linux-input, 'Jingoo Han', 'Viresh Kumar'

Add CONFIG_PM_SLEEP to suspend/resume functions

Add CONFIG_PM_SLEEP to suspend/resume functions instead of
CONFIG_PM to fix the following build warning when CONFIG_PM_SLEEP
is not selected and CONFIG_PM is selected. This is because sleep
PM callbacks defined by SIMPLE_DEV_PM_OPS are only used when the
CONFIG_PM_SLEEP is enabled.

drivers/input/keyboard/spear-keyboard.c:292:12: warning: 'spear_kbd_suspend' defined but not used [-Wunused-function]
drivers/input/keyboard/spear-keyboard.c:345:12: warning: 'spear_kbd_resume' defined but not used [-Wunused-function]

Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
 drivers/input/keyboard/spear-keyboard.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
index 258af10..fc93e79 100644
--- a/drivers/input/keyboard/spear-keyboard.c
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -288,7 +288,7 @@ static int spear_kbd_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int spear_kbd_suspend(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-- 
1.7.9.5



^ permalink raw reply related

* Re: [PATCH] psmouse: Add some support for the FocalTech PS/2 protocol extensions.
From: Benjamin Tissoires @ 2014-10-27 13:44 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Mathias Gottschlag, Dmitry Torokhov, linux-input
In-Reply-To: <544E1304.2060006@redhat.com>

Hi Mathias,

In addition to what Hans said, there are dfew thing you need to change
while initializing the device (see inlined comments).

On Mon, Oct 27, 2014 at 5:40 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> Hi Matthias,
>
> On 10/25/2014 02:01 PM, Mathias Gottschlag wrote:
>> Most of the protocol for these touchpads has been reverse engineered. This
>> commit adds a basic multitouch-capable driver.
>>
>> A lot of the protocol is still unknown. Especially, we don't know how to
>> identify the device yet apart from the PNP ID. The code currently compares
>> known good values with all data from the touchpad which could be usable for
>> identification, so by design even minor variations will cause the driver
>> to fail to detect the device and fall back to compatibility mode.
>>
>> The previous workaround for these devices has been left in place in case
>> the driver is not compiled into the kernel or in case some other device
>> with the same PNP ID is not recognized by the driver yet still has the same
>> problems with the device probing code.
>> ---
>>
>> Hi,
>>
>> this patch is related to the following bugs:
>> https://bugzilla.redhat.com/show_bug.cgi?id=1110011
>> https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1372609
>>
>> It has been tested on the Asus UX303LN, UX303LA and K750L laptops. The
>> latter is affected by bug 1110011 above and used to require additional
>> flags before the touchpad would work. This behaviour should have been
>> fixed by the previous FocalTech patch, but is still present after this
>> patch. I have not yet gotten any feedback yet, but my theory is that the
>> original patch failed to fix the kernel on that device. I have to admit
>> though that I haven't 100% understood that patch.
>>
>> When reviewing, keep in mind that this is my first linux patch. I am
>> using the code for quite some time now without issues, but I might have
>> overlooked quite some problems.
>
> First of all many thanks for working on this.
>
> First one overall comment on the patch, I don't really like the new
> detect function you are adding, and I don't see a need for it, pnp-id
> detection is what windows uses, and it should be 100% reliable (famous last
> words). So I would prefer to stick with with only the pnp-id detection.
>
> I do agree that keeping the pnp-id detection (and falling back to bare) even
> when focaltech support is not compiled in is something which we should do.
>
> Note that likewise specifying psmouse.proto=bare should runtime disable
> your new code if compiled in so the detection blurb in psmouse-base.c should
> look something like this:
>
> /* Always check for focaltech, this is safe as it uses pnp-id matching */
>         if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
>                 if (max_proto > PSMOUSE_IMEX) {
>                         if (!set_properties || focaltech_init(psmouse) == 0) {
>                                 if (focaltech_supported())
>                                         return PSMOUSE_FOCALTECH;
>                                 /*
>                                  * Note that we need to also restrict
>                                  * psmouse_max_proto so that psmouse_initialize()
>                                  * does not try to reset rate and resolution,
>                                  * because even that upsets the device.
>                                  */
>                                 psmouse_max_proto = PSMOUSE_PS2;
>                                 return PSMOUSE_PS2;
>                         }
>                 }
>         }
>
> Where focaltech_supported() is a simple:
>
> #ifdef CONFIG_MOUSE_PS2_FOCALTECH
> bool focaltech_supported(void) {
>         return true;
> }
> #else
> bool focaltech_supported(void) {
>         return false;
> }
> #endif
>
> in focaltech.c (see synaptics.c, which defines synaptics_supported() in the
> same way.
>
>
>
>
>
>>
>> Regards,
>> Mathias
>>
>>  drivers/input/mouse/Kconfig        |  10 ++
>>  drivers/input/mouse/focaltech.c    | 326
>> ++++++++++++++++++++++++++++++++++++-
>>  drivers/input/mouse/focaltech.h    |  61 +++++++
>>  drivers/input/mouse/psmouse-base.c |  15 ++
>>  drivers/input/mouse/psmouse.h      |   1 +
>>  5 files changed, 406 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
>> index 366fc7a..db973e5 100644
>> --- a/drivers/input/mouse/Kconfig
>> +++ b/drivers/input/mouse/Kconfig
>> @@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
>>         If unsure, say N.
>>  +config MOUSE_PS2_FOCALTECH
>> +     bool "FocalTech PS/2 mouse protocol extension" if EXPERT
>> +     default y
>> +     depends on MOUSE_PS2
>> +     help
>> +       Say Y here if you have a FocalTech PS/2 TouchPad connected to
>> +       your system.
>> +
>> +       If unsure, say Y.
>> +
>>  config MOUSE_SERIAL
>>       tristate "Serial mouse"
>>       select SERIO
>> diff --git a/drivers/input/mouse/focaltech.c
>> b/drivers/input/mouse/focaltech.c
>> index f4d657e..ada97cc 100644
>> --- a/drivers/input/mouse/focaltech.c
>> +++ b/drivers/input/mouse/focaltech.c
>> @@ -2,6 +2,7 @@
>>   * Focaltech TouchPad PS/2 mouse driver
>>   *
>>   * Copyright (c) 2014 Red Hat Inc.
>> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
>>   *
>>   * This program is free software; you can redistribute it and/or modify
>>   * it under the terms of the GNU General Public License as published by
>> @@ -13,15 +14,14 @@
>>   * Hans de Goede <hdegoede@redhat.com>
>>   */
>>  -/*
>> - * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
>> - * detection only, to avoid further detection attempts confusing the
>> touchpad
>> - * this way it at least works in PS/2 mouse compatibility mode.
>> - */
>>   #include <linux/device.h>
>>  #include <linux/libps2.h>
>> +#include <linux/input/mt.h>
>> +#include <linux/serio.h>
>> +#include <linux/slab.h>
>>  #include "psmouse.h"
>> +#include "focaltech.h"
>>   static const char * const focaltech_pnp_ids[] = {
>>       "FLT0101",
>> @@ -30,7 +30,14 @@ static const char * const focaltech_pnp_ids[] = {
>>       NULL
>>  };
>>  -int focaltech_detect(struct psmouse *psmouse, bool set_properties)
>> +/*
>> + * Even if the kernel is built without support for Focaltech PS/2
>> touchpads (or
>> + * when the real driver fails to recognize the device), we still have
>> to detect
>> + * them in order to avoid further detection attempts confusing the
>> touchpad.
>> + * This way it at least works in PS/2 mouse compatibility mode.
>> + */
>> +
>> +int focaltech_detect_fallback(struct psmouse *psmouse, bool set_properties)
>>  {
>>       if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
>>               return -ENODEV;
>> @@ -43,10 +50,315 @@ int focaltech_detect(struct psmouse *psmouse, bool
>> set_properties)
>>       return 0;
>>  }
>>  -int focaltech_init(struct psmouse *psmouse)
>> +int focaltech_init_fallback(struct psmouse *psmouse)
>> +{
>> +     ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
>> +     psmouse_reset(psmouse);
>> +
>> +     return 0;
>> +}
>> +
>
> As discussed please keep this named focaltech_init, and make this version
> #ifndef CONFIG_MOUSE_PS2_FOCALTECH (or put it in the #else block.
>
> Also you may want to make this use the newly defined focaltech_reset()
> function (which would then be outside of #ifdef CONFIG_MOUSE_PS2_FOCALTECH)
>
>> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
>> +
>> +static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
>> +             unsigned char *param)
>> +{
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
>> +             return -1;
>> +     param[0] = 0;
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>> +             return -1;
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>> +             return -1;
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>> +             return -1;
>> +     param[0] = reg;
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
>> +             return -1;
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
>> +             return -1;
>> +     return 0;
>> +}
>> +
>
> If you drop the register contents check, you can drop this as well,
> may be useful for future reference though, so may make it #if 0 ... #endif.
> Dmitry ?
>
>
>> +int focaltech_detect(struct psmouse *psmouse, bool set_properties)
>> +{
>> +     struct ps2dev *ps2dev = &psmouse->ps2dev;
>> +     unsigned char param[3];
>> +
>> +     if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
>> +             return -ENODEV;
>> +
>> +     /*
>> +      * We don't know how to identify the device, so we just check the
>> +      * content of all registers.
>> +      */
>> +     if (focaltech_read_register(ps2dev, 0, param))
>> +             return -EIO;
>> +     if (param[0] != 0x69 || param[1] != 0x80 || param[2] != 0x80)
>> +             return -ENODEV;
>> +     if (focaltech_read_register(ps2dev, 1, param))
>> +             return -EIO;
>> +     if (param[0] != 0x36 || param[1] != 0x53 || param[2] != 0x03)
>> +             return -ENODEV;
>> +     if (focaltech_read_register(ps2dev, 2, param))
>> +             return -EIO;
>> +     /* 0x13 and 0x0d might be the touchpad size? */
>> +     if (param[0] != 0x00 || param[1] != 0x13 || param[2] != 0x0d)
>> +             return -ENODEV;
>> +     if (focaltech_read_register(ps2dev, 5, param))
>> +             return -EIO;
>> +     if (param[0] != 0x0b || param[1] != 0x03 || param[2] != 0x00)
>> +             return -ENODEV;
>> +     if (focaltech_read_register(ps2dev, 6, param))
>> +             return -EIO;
>> +     if (param[0] != 0x23 || param[1] != 0xbd || param[2] != 0xf8)
>> +             return -ENODEV;
>> +
>> +     if (set_properties) {
>> +             psmouse->vendor = "FocalTech";
>> +             psmouse->name = "FocalTech Touchpad";
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void focaltech_report_state(struct psmouse *psmouse)
>> +{
>> +     int i;
>> +     struct focaltech_data *priv = psmouse->private;
>> +     struct focaltech_hw_state *state = &priv->state;
>> +     struct input_dev *dev = psmouse->dev;
>> +     int finger_count = 0;
>> +
>> +     for (i = 0; i < FOC_MAX_FINGERS; i++) {
>> +             struct focaltech_finger_state *finger = &state->fingers[i];
>> +             int active = finger->active && finger->valid;
>> +             input_mt_slot(dev, i);
>> +             input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
>> +             if (active) {
>> +                     finger_count++;
>> +                     input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
>> +                     input_report_abs(dev, ABS_MT_POSITION_Y,
>> +                                     focaltech_invert_y(finger->y));
>> +             }
>> +     }
>> +     input_mt_report_pointer_emulation(dev, false);
>> +     input_mt_report_finger_count(dev, finger_count);
>
> These 2 lines should be replace with:
>         input_mt_report_pointer_emulation(dev, finger_count);
>
> So that BTN_TOUCH properly gets set when at least one finger is down (this is
> mandatory when not reporting pressure).
>
> Also I see that you are not using input_mt_assign_slots, so I take it that
> if put 2 fingers down and then lift the first finger down, the second finger
> stays in slot->fingers[1] ? Because if it does not then you need to use
> input_mt_assign_slots.
>
>> +
>> +     input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
>> +     input_sync(psmouse->dev);
>> +}
>> +
>> +static void process_touch_packet(struct focaltech_hw_state *state,
>> +             unsigned char *packet)
>> +{
>> +     int i;
>> +     unsigned char fingers = packet[1];
>> +
>> +     state->pressed = (packet[0] >> 4) & 1;
>> +     /* the second byte contains a bitmap of all fingers touching the pad */
>> +     for (i = 0; i < FOC_MAX_FINGERS; i++) {
>> +             if ((fingers & 0x1) && !state->fingers[i].active) {
>> +                     /* we do not have a valid position for the finger yet */
>> +                     state->fingers[i].valid = 0;
>> +             }
>> +             state->fingers[i].active = fingers & 0x1;
>> +             fingers >>= 1;
>> +     }
>> +}
>> +
>> +static void process_abs_packet(struct focaltech_hw_state *state,
>> +             unsigned char *packet)
>> +{
>> +     unsigned int finger = (packet[1] >> 4) - 1;
>> +
>> +     state->pressed = (packet[0] >> 4) & 1;
>> +     if (finger >= FOC_MAX_FINGERS)
>> +             return;
>> +     /*
>> +      * packet[5] contains some kind of tool size in the most significant
>> +      * nibble. 0xff is a special value (latching) that signals a large
>> +      * contact area.
>> +      */
>> +     if (packet[5] == 0xff)
>> +             return;
>
> Maybe set state->fingers[finger].valid = 0 here ?
>
>> +     state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
>> +     state->fingers[finger].y = (packet[3] << 8) | packet[4];
>> +     state->fingers[finger].valid = 1;
>> +}
>> +static void process_rel_packet(struct focaltech_hw_state *state,
>> +             unsigned char *packet)
>> +{
>> +     int finger1 = ((packet[0] >> 4) & 0x7) - 1;
>> +     int finger2 = ((packet[3] >> 4) & 0x7) - 1;
>> +
>> +     state->pressed = packet[0] >> 7;
>> +     if (finger1 < FOC_MAX_FINGERS) {
>> +             state->fingers[finger1].x += (char)packet[1];
>> +             state->fingers[finger1].y += (char)packet[2];
>> +     }
>> +     /*
>> +      * If there is an odd number of fingers, the last relative packet only
>> +      * contains one finger. In this case, the second finger index in the
>> +      * packet is 0 (we subtract 1 in the lines above to create array
>> +      * indices).
>> +      */
>> +     if (finger2 != -1 && finger2 < FOC_MAX_FINGERS) {
>> +             state->fingers[finger2].x += (char)packet[4];
>> +             state->fingers[finger2].y += (char)packet[5];
>> +     }
>> +}
>> +
>> +static void focaltech_process_packet(struct psmouse *psmouse)
>> +{
>> +     struct focaltech_data *priv = psmouse->private;
>> +     unsigned char *packet = psmouse->packet;
>> +
>> +     switch (packet[0] & 0xf) {
>> +     case FOC_TOUCH:
>> +             process_touch_packet(&priv->state, packet);
>> +             break;
>> +     case FOC_ABS:
>> +             process_abs_packet(&priv->state, packet);
>> +             break;
>> +     case FOC_REL:
>> +             process_rel_packet(&priv->state, packet);
>> +             break;
>> +     default:
>> +             psmouse_err(psmouse, "Unknown packet type: %02x", packet[0]);
>> +             break;
>> +     }
>> +
>> +     focaltech_report_state(psmouse);
>> +}
>> +
>> +static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
>>  {
>> +     if (psmouse->pktcnt >= 6) { /* Full packet received */
>> +             focaltech_process_packet(psmouse);
>> +             return PSMOUSE_FULL_PACKET;
>> +     }
>> +     /*
>> +      * we might want to do some validation of the data here, but we do not
>> +      * know the protocoll well enough
>> +      */
>> +     return PSMOUSE_GOOD_DATA;
>> +}
>> +
>> +static void focaltech_reset(struct psmouse *psmouse)
>> +{
>> +     psmouse_info(psmouse, "focaltech_reset");
>>       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
>>       psmouse_reset(psmouse);
>> +}
>> +
>> +static int focaltech_switch_protocol(struct psmouse *psmouse)
>> +{
>> +     struct ps2dev *ps2dev = &psmouse->ps2dev;
>> +     unsigned char param[3];
>> +
>> +     param[0] = 0;
>> +     if (ps2_command(ps2dev, param, 0x10f8))
>> +             return -EIO;
>> +     if (ps2_command(ps2dev, param, 0x10f8))
>> +             return -EIO;
>> +     if (ps2_command(ps2dev, param, 0x10f8))
>> +             return -EIO;
>> +     param[0] = 1;
>> +     if (ps2_command(ps2dev, param, 0x10f8))
>> +             return -EIO;
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
>> +             return -EIO;
>> +
>> +     if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
>> +             return -EIO;
>>       return 0;
>>  }
>> +
>> +static void focaltech_disconnect(struct psmouse *psmouse)
>> +{
>> +     focaltech_reset(psmouse);
>> +     kfree(psmouse->private);
>> +     psmouse->private = NULL;
>> +}
>> +
>> +static int focaltech_reconnect(struct psmouse *psmouse)
>> +{
>> +     focaltech_reset(psmouse);
>> +     if (focaltech_switch_protocol(psmouse)) {
>> +             psmouse_err(psmouse,
>> +                         "Unable to initialize the device.");
>> +             return -1;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static void set_input_params(struct psmouse *psmouse)
>> +{
>> +     struct input_dev *dev = psmouse->dev;
>> +
>> +     __set_bit(EV_KEY, dev->evbit);
>> +     __set_bit(EV_ABS, dev->evbit);
>> +     input_set_abs_params(dev, ABS_X, 0, FOC_MAX_X, 0, 0);
>> +     input_set_abs_params(dev, ABS_Y, 0, FOC_MAX_Y, 0, 0);
>> +     input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
>> +     input_set_abs_params(dev, ABS_MT_POSITION_X, 0, FOC_MAX_X, 0, 0);
>> +     input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, FOC_MAX_Y, 0, 0);
>> +     __set_bit(BTN_TOUCH, dev->keybit);
>> +     __set_bit(BTN_TOOL_FINGER, dev->keybit);
>> +     __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
>> +     __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
>> +     __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
>> +     __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
>> +     __clear_bit(EV_REL, dev->evbit);
>> +     __clear_bit(REL_X, dev->relbit);
>> +     __clear_bit(REL_Y, dev->relbit);
>> +     __set_bit(BTN_LEFT, dev->keybit);
>> +     __clear_bit(BTN_RIGHT, dev->keybit);
>> +     __clear_bit(BTN_MIDDLE, dev->keybit);
>> +     __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);

Basically, there are too much things here :)

You need to initialize:
- __set_bit(EV_ABS, dev->evbit); (it looks like nobody else will set it for us)
- ABS_MT_POSITION_X and ABS_MT_POSITION_Y first
- then call input_mt_init_slots(dev, 5, INPUT_MT_POINTER); (if you
need to use int_me_assign_slot as said by Hans, you need to also add
INPUT_MT_TRACK in the flags).
- clear the various unrequired bits
- set INPUT_PROP_BUTTONPAD

The BTN_TOUCH, BTN_TOOL_***, ABS_X/Y, EV_KEY bits are all set by
input_mt_init_slot() if you call it after having set the
ABS_MT_POSITION_X/Y

Cheers,
Benjamin

>> +}
>> +
>> +int focaltech_init(struct psmouse *psmouse)
>> +{
>> +     struct focaltech_data *priv;
>> +
>> +     focaltech_reset(psmouse);
>> +     if (focaltech_switch_protocol(psmouse)) {
>> +             focaltech_reset(psmouse);
>> +             psmouse_info(psmouse,
>> +                          "Unable to initialize the device.");
>> +             return -ENOSYS;
>> +     }
>> +
>> +     psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
>> GFP_KERNEL);
>> +     if (!priv)
>> +             return -ENOMEM;
>> +
>> +     set_input_params(psmouse);
>> +
>> +     psmouse->protocol_handler = focaltech_process_byte;
>> +     psmouse->pktsize = 6;
>> +     psmouse->disconnect = focaltech_disconnect;
>> +     psmouse->reconnect = focaltech_reconnect;
>> +     psmouse->cleanup = focaltech_reset;
>> +     /* resync is not supported yet */
>> +     psmouse->resync_time = 0;
>> +
>> +     return 0;
>> +}
>> +
>> +#else /* CONFIG_MOUSE_PS2_FOCALTECH */
>> +
>> +int focaltech_detect(struct psmouse *psmouse, bool set_properties)
>> +{
>> +     return -ENOSYS;
>> +}
>> +
>> +int focaltech_init(struct psmouse *psmouse)
>> +{
>> +     return -ENOSYS;
>> +}
>> +
>> +#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
>> diff --git a/drivers/input/mouse/focaltech.h
>> b/drivers/input/mouse/focaltech.h
>> index 498650c..3c553b2 100644
>> --- a/drivers/input/mouse/focaltech.h
>> +++ b/drivers/input/mouse/focaltech.h
>> @@ -2,6 +2,7 @@
>>   * Focaltech TouchPad PS/2 mouse driver
>>   *
>>   * Copyright (c) 2014 Red Hat Inc.
>> + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
>>   *
>>   * This program is free software; you can redistribute it and/or modify
>>   * it under the terms of the GNU General Public License as published by
>> @@ -16,7 +17,67 @@
>>  #ifndef _FOCALTECH_H
>>  #define _FOCALTECH_H
>>  +/*
>> + * Packet types - the numbers are not consecutive, so we might be missing
>> + * something here.
>> + */
>> +#define FOC_TOUCH 0x3 /* bitmap of active fingers */
>> +#define FOC_ABS 0x6 /* absolute position of one finger */
>> +#define FOC_REL 0x9 /* relative position of 1-2 fingers */
>> +
>> +#define FOC_MAX_FINGERS 5
>> +
>> +#define FOC_MAX_X 2431
>> +#define FOC_MAX_Y 1663
>> +
>> +static inline int focaltech_invert_y(int y)
>> +{
>> +     return FOC_MAX_Y - y;
>> +}
>> +
>> +/*
>> + * Current state of a single finger on the touchpad.
>> + */
>> +struct focaltech_finger_state {
>> +     /* the touchpad has generated a touch event for the finger */
>> +     bool active;
>> +     /*
>> +      * The touchpad has sent position data for the finger. Touch event
>> +      * packages reset this flag for new fingers, and there is a time
>> +      * between the first touch event and the following absolute position
>> +      * packet for the finger where the touchpad has declared the finger to
>> +      * be valid, but we do not have any valid position yet.
>> +      */
>> +     bool valid;
>> +     /* absolute position (from the bottom left corner) of the finger */
>> +     unsigned int x;
>> +     unsigned int y;
>> +};
>> +
>> +/*
>> + * Description of the current state of the touchpad hardware.
>> + */
>> +struct focaltech_hw_state {
>> +     /*
>> +      * The touchpad tracks the positions of the fingers for us, the array
>> +      * indices correspond to the finger indices returned in the report
>> +      * packages.
>> +      */
>> +     struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
>> +     /*
>> +      * True if the clickpad has been pressed.
>> +      */
>> +     bool pressed;
>> +};
>> +
>> +struct focaltech_data {
>> +     struct focaltech_hw_state state;
>> +};
>> +
>>  int focaltech_detect(struct psmouse *psmouse, bool set_properties);
>>  int focaltech_init(struct psmouse *psmouse);
>>  +int focaltech_detect_fallback(struct psmouse *psmouse, bool
>> set_properties);
>> +int focaltech_init_fallback(struct psmouse *psmouse);
>> +
>>  #endif
>> diff --git a/drivers/input/mouse/psmouse-base.c
>> b/drivers/input/mouse/psmouse-base.c
>> index 26994f6..2e2b98d 100644
>> --- a/drivers/input/mouse/psmouse-base.c
>> +++ b/drivers/input/mouse/psmouse-base.c
>> @@ -726,6 +726,12 @@ static int psmouse_extensions(struct psmouse *psmouse,
>>  /* Always check for focaltech, this is safe as it uses pnp-id matching */
>>       if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
>>               if (!set_properties || focaltech_init(psmouse) == 0) {
>> +                     return PSMOUSE_FOCALTECH;
>> +             }
>> +     }
>> +     if (psmouse_do_detect(focaltech_detect_fallback,
>> +                           psmouse, set_properties) == 0) {
>> +             if (!set_properties || focaltech_init_fallback(psmouse) == 0) {
>>                       /*
>>                        * Not supported yet, use bare protocol.
>>                        * Note that we need to also restrict
>> @@ -1063,6 +1069,15 @@ static const struct psmouse_protocol
>> psmouse_protocols[] = {
>>               .alias          = "cortps",
>>               .detect         = cortron_detect,
>>       },
>> +#ifdef CONFIG_MOUSE_PS2_FOCALTECH
>> +     {
>> +             .type           = PSMOUSE_FOCALTECH,
>> +             .name           = "FocalTechPS/2",
>> +             .alias          = "focaltech",
>> +             .detect         = focaltech_detect,
>> +             .init           = focaltech_init,
>> +     },
>> +#endif
>>       {
>>               .type           = PSMOUSE_AUTO,
>>               .name           = "auto",
>> diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
>> index f4cf664..c2ff137 100644
>> --- a/drivers/input/mouse/psmouse.h
>> +++ b/drivers/input/mouse/psmouse.h
>> @@ -96,6 +96,7 @@ enum psmouse_type {
>>       PSMOUSE_FSP,
>>       PSMOUSE_SYNAPTICS_RELATIVE,
>>       PSMOUSE_CYPRESS,
>> +     PSMOUSE_FOCALTECH,
>>       PSMOUSE_AUTO            /* This one should always be last */
>>  };
>
> Overall this looks very good, many thanks for your work on this!
>
> Regards,
>
> Hans
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 03/11] aiptek: Fix probable mask then right shift defects
From: Dmitry Torokhov @ 2014-10-27 14:44 UTC (permalink / raw)
  To: Joe Perches; +Cc: linux-kernel, linux-input
In-Reply-To: <8c6687a4d568c853ab649ba03d2477ee24bc6ff6.1414387334.git.joe@perches.com>

Hi Joe,

On Sun, Oct 26, 2014 at 10:24:59PM -0700, Joe Perches wrote:
> Precedence of & and >> is not the same and is not left to right.
> shift has higher precedence and should be done after the mask.

Looking at the protocol description the current code is exactly right.
We want to "move" button bits first as in packet type 1 they are in a
different place than in other packets.

I'll take a patch that adds parenthesis around shifts to make clear it
is intended.

Thanks.

> 
> Here the shifts are unnecessary and a non-zero value can be used
> as the test to set 1 or zero.
> 
> Signed-off-by: Joe Perches <joe@perches.com>
> ---
>  drivers/input/tablet/aiptek.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
> index e7f966d..dee2bb9 100644
> --- a/drivers/input/tablet/aiptek.c
> +++ b/drivers/input/tablet/aiptek.c
> @@ -489,9 +489,9 @@ static void aiptek_irq(struct urb *urb)
>  			 */
>  			jitterable = data[1] & 0x07;
>  
> -			left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0;
> -			right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0;
> -			middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0;
> +			left = data[1] & aiptek->curSetting.mouseButtonLeft ? 1 : 0;
> +			right = data[1] & aiptek->curSetting.mouseButtonRight ? 1 : 0;
> +			middle = data[1] & aiptek->curSetting.mouseButtonMiddle ? 1 : 0;
>  
>  			input_report_key(inputdev, BTN_LEFT, left);
>  			input_report_key(inputdev, BTN_MIDDLE, middle);
> -- 
> 2.1.2
> 

-- 
Dmitry

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox