linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Cypress PS/2 Trackpad driver
@ 2012-11-29 21:57 Kamal Mostafa
  2012-11-29 21:57 ` [PATCH v3 1/4] input: increase struct ps2dev cmdbuf[] to 8 bytes Kamal Mostafa
                   ` (5 more replies)
  0 siblings, 6 replies; 17+ messages in thread
From: Kamal Mostafa @ 2012-11-29 21:57 UTC (permalink / raw)
  To: linux-input, linux-kernel, Dmitry Torokhov, Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, Dudley Du,
	Cypress Semiconductor Corporation, Kamal Mostafa, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski

This driver, submitted on behalf of Cypress Semiconductor Corporation and
additional contributors, provides support for the Cypress PS/2 Trackpad.

This [PATCH v3] version differs from my previous submitted version[1]:

  Patch #1 (cmdbuf to 8 bytes) and #3 (link in driver) are unchanged.

  Patch #2 (main driver), as recommended by Henrik Rydberg[2]:
  - use input_mt_assign_slots; drop cypress_cal_finger_id.
  - enable 2-finger-only SEMI_MT; drop cypress_simulate_fingers.
  - various code clean-ups.

  Henrik, does patch #2 appear to properly use assign_slots and SEMI_MT as
  you intended?  This SEMI_MT method does work (with 2 finger support only),
  but I'm not clear why we wouldn't want to handle >2 fingers also, so ...

  Patch #4 (new) reintroduces simulated multitouch for up to 5 fingers
  (#if CYPRESS_SIMULATE_MT), disabling SEMI_MT again.

  If that functionality (support for >2 fingers) can be acheived in some
  better way, please advise.


Remaining known problems (assistance or advice appreciated):

  - Multitouch >2 fingers does not work after rmmod/insmod, and stops
    working after suspend/resume.  Restarting X fixes it in both cases.

  - cypress_reconnect() never actually works, so I disabled it; just let
    psmouse re-init the driver instead, which works and is what was ending
    up happenning anyway.


 -Kamal Mostafa <kamal@canonical.com>

[0] PATCH v1: http://www.spinics.net/lists/linux-input/msg23690.html
[1] PATCH v2: http://www.spinics.net/lists/linux-input/msg23718.html
[2] Henrik's review: http://www.spinics.net/lists/linux-input/msg23747.html



Cypress Semiconductor Corporation (2):
  input: Cypress PS/2 Trackpad psmouse driver
  input: Cypress PS/2 Trackpad link into psmouse-base

Kamal Mostafa (2):
  input: increase struct ps2dev cmdbuf[] to 8 bytes
  input: Cypress PS/2 Trackpad simulated multitouch

 drivers/input/mouse/Kconfig        |   10 +
 drivers/input/mouse/Makefile       |    1 +
 drivers/input/mouse/cypress_ps2.c  |  846 ++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/cypress_ps2.h  |  231 ++++++++++
 drivers/input/mouse/psmouse-base.c |   32 ++
 drivers/input/mouse/psmouse.h      |    1 +
 include/linux/libps2.h             |    2 +-
 7 files changed, 1122 insertions(+), 1 deletion(-)
 create mode 100644 drivers/input/mouse/cypress_ps2.c
 create mode 100644 drivers/input/mouse/cypress_ps2.h

-- 
1.7.10.4


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v3 1/4] input: increase struct ps2dev cmdbuf[] to 8 bytes
  2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
@ 2012-11-29 21:57 ` Kamal Mostafa
  2012-11-29 21:57 ` [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver Kamal Mostafa
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 17+ messages in thread
From: Kamal Mostafa @ 2012-11-29 21:57 UTC (permalink / raw)
  To: linux-input, linux-kernel, Dmitry Torokhov, Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, Dudley Du,
	Cypress Semiconductor Corporation, Kamal Mostafa, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski

Cypress PS/2 Trackpad (drivers/input/mouse/cypress_ps2.c) needs
this larger cmdbuf[] to handle 8-byte packet responses.

Signed-off-by: Kamal Mostafa <kamal@canonical.com>
---
 include/linux/libps2.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/libps2.h b/include/linux/libps2.h
index 79603a6..4ad06e8 100644
--- a/include/linux/libps2.h
+++ b/include/linux/libps2.h
@@ -36,7 +36,7 @@ struct ps2dev {
 	wait_queue_head_t wait;
 
 	unsigned long flags;
-	unsigned char cmdbuf[6];
+	unsigned char cmdbuf[8];
 	unsigned char cmdcnt;
 	unsigned char nak;
 };
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
  2012-11-29 21:57 ` [PATCH v3 1/4] input: increase struct ps2dev cmdbuf[] to 8 bytes Kamal Mostafa
@ 2012-11-29 21:57 ` Kamal Mostafa
  2012-12-03  3:20   ` Dudley Du
                     ` (2 more replies)
  2012-11-29 21:58 ` [PATCH v3 3/4] input: Cypress PS/2 Trackpad link into psmouse-base Kamal Mostafa
                   ` (3 subsequent siblings)
  5 siblings, 3 replies; 17+ messages in thread
From: Kamal Mostafa @ 2012-11-29 21:57 UTC (permalink / raw)
  To: linux-input, linux-kernel, Dmitry Torokhov, Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, Dudley Du,
	Cypress Semiconductor Corporation, Kamal Mostafa, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski

From: Cypress Semiconductor Corporation <customercare@cypress.com>

Input/mouse driver for Cypress PS/2 Trackpad.

Original code contributed by Cypress Semiconductor Corporation,
modified by Kamal Mostafa and Kyle Fazzari.

BugLink: http://launchpad.net/bugs/978807

Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Kyle Fazzari <git@status.e4ward.com>
Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Acked-by: Herton Krzesinski <herton.krzesinski@canonical.com>
---
 drivers/input/mouse/cypress_ps2.c |  830 +++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/cypress_ps2.h |  219 ++++++++++
 2 files changed, 1049 insertions(+)
 create mode 100644 drivers/input/mouse/cypress_ps2.c
 create mode 100644 drivers/input/mouse/cypress_ps2.h

diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
new file mode 100644
index 0000000..472342a
--- /dev/null
+++ b/drivers/input/mouse/cypress_ps2.c
@@ -0,0 +1,830 @@
+/*
+ * Cypress Trackpad PS/2 mouse driver
+ *
+ * Copyright (c) 2012 Cypress Semiconductor Corporation.
+ *
+ * Additional contributors include:
+ *   Kamal Mostafa <kamal@canonical.com>
+ *   Kyle Fazzari <git@status.e4ward.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "cypress_ps2.h"
+
+#define CYTP_DBG_DUMP 0		/* set to 1 for more verbose debug dump */
+
+#define cytp_dbg(fmt, ...)  \
+	do {  \
+		if (cytp)  \
+			psmouse_dbg(psmouse, pr_fmt(fmt), ##__VA_ARGS__);  \
+	} while (0)
+
+#if CYTP_DBG_DUMP
+# define cytp_dbg_dump cytp_dbg
+#else
+# define cytp_dbg_dump(fmt, ...)
+#endif
+
+
+/* p is a pointer points to the buffer containing Cypress Keys. */
+#define IS_CYPRESS_KEY(p) ((p[0] == CYPRESS_KEY_1) && (p[1] == CYPRESS_KEY_2))
+#define CYTP_SET_PACKET_SIZE(n) { psmouse->pktsize = cytp->pkt_size = (n); }
+#define CYTP_SET_MODE_BIT(x)  \
+	do {  \
+		if ((x) & CYTP_BIT_ABS_REL_MASK)  \
+			cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK);  \
+		cytp->mode |= (x);  \
+	} while (0)
+#define CYTP_CLEAR_MODE_BIT(x)	{ cytp->mode &= ~(x); }
+
+#define CYTP_SUPPORT_ABS
+
+static unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
+static unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
+
+static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
+{
+	struct cytp_data *cytp = psmouse->private;
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
+		cytp_dbg("send command 0x%02x failed, resp 0x%02x\n",
+			 value & 0xff, ps2dev->nak);
+		if (ps2dev->nak == CYTP_PS2_RETRY)
+			return CYTP_PS2_RETRY;
+		else
+			return CYTP_PS2_ERROR;
+	}
+
+	cytp_dbg("send command 0x%02x success, resp 0xfa\n", value & 0xff);
+
+	return 0;
+}
+
+static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
+			       unsigned char data)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int tries = CYTP_PS2_CMD_TRIES;
+	int rc;
+
+	ps2_begin_command(ps2dev);
+
+	do {
+		/*
+		 * send extension command 0xE8 or 0xF3,
+		 * if send extension command failed,
+		 * try to send recovery command to make
+		 * trackpad device return to ready wait command state.
+		 * It alwasy success based on this recovery commands.
+		 */
+		rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
+		if (rc == CYTP_PS2_RETRY) {
+			rc = cypress_ps2_sendbyte(psmouse, 0x00);
+			if (rc == CYTP_PS2_RETRY)
+				rc = cypress_ps2_sendbyte(psmouse, 0x0a);
+		}
+		if (rc == CYTP_PS2_ERROR)
+			continue;
+
+		rc = cypress_ps2_sendbyte(psmouse, data);
+		if (rc == CYTP_PS2_RETRY)
+			rc = cypress_ps2_sendbyte(psmouse, data);
+		if (rc == CYTP_PS2_ERROR)
+			continue;
+		else
+			break;
+	} while (--tries > 0);
+
+	ps2_end_command(ps2dev);
+
+	return rc;
+}
+
+static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
+				       unsigned char cmd,
+				       unsigned char *param)
+{
+	int i;
+	int rc;
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	struct cytp_data *cytp = psmouse->private;
+	enum psmouse_state old_state;
+	unsigned char old_pktsize;
+
+	ps2_begin_command(&psmouse->ps2dev);
+
+	old_state = psmouse->state;
+	psmouse->state = PSMOUSE_CMD_MODE;
+	psmouse->pktcnt = 0;
+	old_pktsize = psmouse->pktsize;
+	psmouse->pktsize = 3;
+	if (cmd == CYTP_CMD_READ_VITAL_STATISTICS)
+		psmouse->pktsize = 8;
+	memset(param, 0, psmouse->pktsize);
+
+	rc = cypress_ps2_sendbyte(psmouse, 0xe9);
+	if (rc < 0)
+		goto out;
+
+	wait_event_timeout(ps2dev->wait,
+			(psmouse->pktcnt >= psmouse->pktsize),
+			msecs_to_jiffies(CYTP_CMD_TIMEOUT));
+
+	memcpy(param, psmouse->packet, psmouse->pktsize);
+
+	cytp_dbg("Command 0x%02x response data: (0x)", cmd);
+	for (i = 0; i < psmouse->pktsize; i++)
+		cytp_dbg(" %02x", param[i]);
+	cytp_dbg("\n");
+
+out:
+	psmouse->state = old_state;
+	psmouse->pktcnt = 0;
+	psmouse->pktsize = old_pktsize;
+
+	ps2_end_command(&psmouse->ps2dev);
+
+	return rc;
+}
+
+static int cypress_verify_cmd_state(struct psmouse *psmouse,
+				    unsigned char cmd, unsigned char *param)
+{
+	struct cytp_data *cytp = psmouse->private;
+	bool rate_match = 0;
+	bool resolution_match = 0;
+	int i;
+
+	/* callers will do further checking. */
+	if ((cmd == CYTP_CMD_READ_CYPRESS_ID) ||
+	    (cmd == CYTP_CMD_STANDARD_MODE) ||
+	    (cmd == CYTP_CMD_READ_VITAL_STATISTICS))
+		return 0;
+	if (((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID) &&
+	    ((param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE)) {
+		for (i = 0; i < sizeof(cytp_resolution); i++)
+			if (cytp_resolution[i] == param[1])
+				resolution_match =  1;
+
+		for (i = 0; i < sizeof(cytp_rate); i++)
+			if (cytp_rate[i] == param[2])
+				rate_match = 1;
+
+		if (resolution_match && rate_match)
+			return 0;
+	}
+
+	cytp_dbg("verify cmd state failed.\n");
+	return -1;
+}
+
+static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
+				unsigned char *param)
+{
+	struct cytp_data *cytp = psmouse->private;
+	int tries = CYTP_PS2_CMD_TRIES;
+	int rc;
+
+	cytp_dbg("send extension cmd 0x%02x, [%d %d %d %d]\n",
+		 cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
+		 DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
+	do {
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
+
+		rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
+		if ((rc == 0) &&
+		    (cypress_verify_cmd_state(psmouse, cmd, param) == 0))
+			return 0;
+	} while (--tries > 0);
+
+
+	if (tries <= 0)
+		return -1;
+
+	return 0;
+
+}
+
+int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+	unsigned char param[3];
+
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+		return -1;
+
+	if (!IS_CYPRESS_KEY(param))
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "Cypress";
+		psmouse->name = "Trackpad";
+	}
+
+	return 0;
+}
+
+static int cypress_read_fw_version(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+	unsigned char param[3];
+
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+		return -1;
+
+	if (!IS_CYPRESS_KEY(param))
+		return -ENODEV;
+
+	cytp->fw_version = param[2] & FW_VERSION_MASX;
+	cytp->vital_statics_supported = (param[2] & VITAL_STATICS_MASK) ? 1 : 0;
+
+	cytp_dbg("cytp->fw_version = %d\n", cytp->fw_version);
+	cytp_dbg("cytp->vital_statics_supported = %d\n",
+		 cytp->vital_statics_supported);
+	return 0;
+}
+
+static int cypress_read_vital_statistics(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+	unsigned char param[8];
+
+	/* set default values for vital statistics not supported trackpad. */
+	cytp->tp_width = CYTP_DEFAULT_WIDTH;
+	cytp->tp_high = CYTP_DEFAULT_HIGH;
+	cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
+	cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
+	cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
+	cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
+	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+	memset(param, 0, sizeof(param));
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_VITAL_STATISTICS, param) == 0) {
+		/* Update trackpad parameters. */
+		cytp->tp_max_abs_x = (param[1] << 8) | param[0];
+		cytp->tp_max_abs_y = (param[3] << 8) | param[2];
+		cytp->tp_min_pressure = param[4];
+		cytp->tp_max_pressure = param[5];
+
+		if (param[6] & VITAL_BIT_APA)
+			cytp->tp_type = CYTP_APA;
+		else if (param[6] & VITAL_BIT_MTG)
+			cytp->tp_type = CYTP_MTG;
+		else
+			cytp->tp_type = CYTP_STG;
+		cytp->tp_palm = (param[6] & VITAL_BIT_PALM) ? 1 : 0;
+		cytp->tp_stubborn = (param[6] & VITAL_BIT_STUBBORN) ? 1 : 0;
+		cytp->tp_2f_jitter = (param[6] & VITAL_BIT_2F_JITTER) >> 4;
+		cytp->tp_1f_jitter = (param[6] & VITAL_BIT_1F_JITTER) >> 2;
+		cytp->tp_abs_packet_format_set =
+			(param[7] & VITAL_BIT_ABS_PKT_FORMAT_SET) >> 4;
+		cytp->tp_2f_spike = (param[7] & VITAL_BIT_2F_SPIKE) >> 2;
+		cytp->tp_1f_spike = (param[7] & VITAL_BIT_1F_SPIKE);
+
+	}
+
+	if (!cytp->tp_max_pressure ||
+	    (cytp->tp_max_pressure < cytp->tp_min_pressure) ||
+	    (!cytp->tp_width || !cytp->tp_high) ||
+	    (!cytp->tp_max_abs_x) ||
+	    (cytp->tp_max_abs_x < cytp->tp_width) ||
+	    (!cytp->tp_max_abs_y) ||
+	    (cytp->tp_max_abs_y < cytp->tp_high))
+		return -1;
+
+	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+	cytp_dbg_dump("Dump trackpad hardware configuration as below:\n");
+	cytp_dbg_dump("cytp->tp_width = %d\n", cytp->tp_width);
+	cytp_dbg_dump("cytp->tp_high = %d\n", cytp->tp_high);
+	cytp_dbg_dump("cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
+	cytp_dbg_dump("cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
+	cytp_dbg_dump("cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
+	cytp_dbg_dump("cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
+	cytp_dbg_dump("cytp->tp_res_x = %d\n", cytp->tp_res_x);
+	cytp_dbg_dump("cytp->tp_res_y = %d\n", cytp->tp_res_y);
+	cytp_dbg_dump("cytp->tp_type = %d\n", cytp->tp_type);
+	cytp_dbg_dump("cytp->tp_palm = %d\n", cytp->tp_palm);
+	cytp_dbg_dump("cytp->tp_stubborn = %d\n", cytp->tp_stubborn);
+	cytp_dbg_dump("cytp->tp_1f_jitter = %d\n", cytp->tp_1f_jitter);
+	cytp_dbg_dump("cytp->tp_2f_jitter = %d\n", cytp->tp_2f_jitter);
+	cytp_dbg_dump("cytp->tp_1f_spike = %d\n", cytp->tp_1f_spike);
+	cytp_dbg_dump("cytp->tp_2f_spike = %d\n", cytp->tp_2f_spike);
+	cytp_dbg_dump("cytp->tp_abs_packet_format_set = %d\n",
+		      cytp->tp_abs_packet_format_set);
+
+	return 0;
+}
+
+static int cypress_query_hardware(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	if (cypress_read_fw_version(psmouse))
+		return -1;
+
+	if (cytp->vital_statics_supported) {
+		if (cypress_read_vital_statistics(psmouse))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int cypress_set_absolute_mode(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+	unsigned char param[3];
+
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
+		return -1;
+
+	CYTP_SET_MODE_BIT(CYTP_BIT_ABS_PRESSURE);
+	CYTP_SET_PACKET_SIZE(5);
+
+	return 0;
+}
+
+/*
+ * reset trackpad device to standard relative mode.
+ * This is also the defalut mode when trackpad powered on.
+ */
+static void cypress_reset(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	psmouse_reset(psmouse);
+
+	CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
+	CYTP_SET_PACKET_SIZE(3);
+
+	cytp->prev_contact_cnt = 0;
+}
+
+static int cypress_set_input_params(struct input_dev *input,
+				    struct cytp_data *cytp)
+{
+	int ret;
+
+	if (cytp->mode & CYTP_BIT_ABS_MASK) {
+		__set_bit(EV_ABS, input->evbit);
+		input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
+		input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
+		input_set_abs_params(input, ABS_PRESSURE,
+				     cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
+		input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
+
+		/* finger position */
+		input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
+		input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+		ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
+			INPUT_MT_POINTER|INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
+		if (ret < 0) {
+			return ret;
+		}
+
+		__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
+
+		if (cytp->tp_res_x && cytp->tp_res_x) {
+			input_abs_set_res(input, ABS_X, cytp->tp_res_x);
+			input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
+
+			input_abs_set_res(input, ABS_MT_POSITION_X,
+					  cytp->tp_res_x);
+			input_abs_set_res(input, ABS_MT_POSITION_Y,
+					  cytp->tp_res_y);
+
+		}
+
+		__set_bit(BTN_TOUCH, input->keybit);
+		__set_bit(BTN_TOOL_FINGER, input->keybit);
+		__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+		__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+		__set_bit(BTN_TOOL_QUADTAP, input->keybit);
+		__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
+
+		__clear_bit(EV_REL, input->evbit);
+		__clear_bit(REL_X, input->relbit);
+		__clear_bit(REL_Y, input->relbit);
+	} else {
+		__set_bit(EV_REL, input->evbit);
+		__set_bit(REL_X, input->relbit);
+		__set_bit(REL_Y, input->relbit);
+		__set_bit(REL_WHEEL, input->relbit);
+		__set_bit(REL_HWHEEL, input->relbit);
+
+		__clear_bit(EV_ABS, input->evbit);
+	}
+
+	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_LEFT, input->keybit);
+	__set_bit(BTN_RIGHT, input->keybit);
+	__set_bit(BTN_MIDDLE, input->keybit);
+
+	input_set_drvdata(input, cytp);
+
+	return 0;
+}
+
+static int cypress_get_finger_count(unsigned char header_byte)
+{
+	unsigned char bits6_7;
+	int finger_count;
+
+	bits6_7 = header_byte >> 6;
+	finger_count = bits6_7 & 0x03;
+
+	if (finger_count != 1) {
+		if (header_byte & ABS_HSCROLL_BIT) {
+			if (finger_count == 0) {
+				/* HSCROLL gets added on to 0 finger count. */
+				finger_count = 4;
+				/* should remove HSCROLL bit. */
+			} else {
+				if (finger_count == 2) {
+					finger_count = 5;
+				} else {
+					/* Invalid contact (e.g. palm). Ignore it. */
+					finger_count = 0;
+				}
+			}
+		}
+	}
+
+	return finger_count;
+}
+
+
+static int cypress_parse_packet(struct psmouse *psmouse,
+				struct cytp_data *cytp, struct cytp_report_data *report_data)
+{
+	int i;
+	unsigned char *packet = psmouse->packet;
+	unsigned char header_byte = packet[0];
+
+	memset(report_data, 0, sizeof(struct cytp_report_data));
+	if (cytp->mode & CYTP_BIT_ABS_MASK) {
+		report_data->contact_cnt = cypress_get_finger_count(header_byte);
+
+		if (report_data->contact_cnt > CYTP_MAX_CONTACTS) {
+			/* report invalid data as zero package except the button data. */
+			report_data->contact_cnt = 0;
+			cytp_dbg("cypress_parse_packet: received invalid packet.\n");
+		}
+
+		report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
+
+		/* Remove HSCROLL bit */
+		if (report_data->contact_cnt == 4)
+			header_byte &= ~(ABS_HSCROLL_BIT);
+
+		if (report_data->contact_cnt == 1) {
+			report_data->contacts[0].x =
+				((packet[1] & 0x70) << 4) | packet[2];
+			report_data->contacts[0].y =
+				((packet[1] & 0x07) << 8) | packet[3];
+			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+				report_data->contacts[0].z = packet[4];
+
+			if ((packet[1] & ABS_EDGE_MOTION_MASK) != ABS_EDGE_MOTION_MASK) {
+				report_data->vscroll = (header_byte & ABS_VSCROLL_BIT) ? 1 : 0;
+				report_data->hscroll = (header_byte & ABS_HSCROLL_BIT) ? 1 : 0;
+			}
+
+		} else if (report_data->contact_cnt >= 2) {
+			report_data->contacts[0].x =
+				((packet[1] & 0x70) << 4) | packet[2];
+			report_data->contacts[0].y =
+				((packet[1] & 0x07) << 8) | packet[3];
+			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+				report_data->contacts[0].z = packet[4];
+
+			report_data->contacts[1].x =
+				((packet[5] & 0xf0) << 4) | packet[6];
+			report_data->contacts[1].y =
+				((packet[5] & 0x0f) << 8) | packet[7];
+			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+				report_data->contacts[1].z = report_data->contacts[0].z;
+		}
+
+		report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
+		report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
+
+	} else {
+		report_data->contact_cnt = 1;
+		report_data->contacts[0].x =
+			(packet[0] & REL_X_SIGN_BIT) ? -packet[1] : packet[1];
+		report_data->contacts[0].y =
+			(packet[0] & REL_Y_SIGN_BIT) ? -packet[2] : packet[2];
+		report_data->vscroll = packet[3];
+		report_data->left = (packet[0] & BTN_LEFT_BIT) ? 1 : 0;
+		report_data->right = (packet[0] & BTN_RIGHT_BIT) ? 1 : 0;
+
+		if (cytp->mode & CYTP_BIT_STANDARD_REL)
+			report_data->middle =
+				(packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
+		if (cytp->mode & CYTP_BIT_CYPRESS_REL) {
+			report_data->left =
+				(packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
+			report_data->hscroll = packet[4];
+		}
+	}
+
+	/* This is only true if one of the mouse buttons were tapped.
+	 * Make sure it doesn't turn into a click. The regular tap-to-
+	 * click functionality will handle that on its own. If we don't
+	 * do this, disabling tap-to-click won't affect the mouse button
+	 * zones. */
+	if (report_data->tap)
+		report_data->left = 0;
+
+	if (report_data->contact_cnt <= 0)
+		return 0;
+
+	cytp_dbg_dump("cypress_parse_packet cytp->zero_packet_cnt = %d\n", cytp->zero_packet_cnt);
+	cytp_dbg_dump("Dump parsed report data as below:\n");
+	cytp_dbg_dump("contact_cnt = %d\n", report_data->contact_cnt);
+	for (i = 0; i < report_data->contact_cnt; i++) {
+		cytp_dbg_dump("contacts[%d].x = %d\n", i, report_data->contacts[i].x);
+		cytp_dbg_dump("contacts[%d].y = %d\n", i, report_data->contacts[i].y);
+		cytp_dbg_dump("contacts[%d].z = %d\n", i, report_data->contacts[i].z);
+	}
+	cytp_dbg_dump("vscroll = %d\n", report_data->vscroll);
+	cytp_dbg_dump("hscroll = %d\n", report_data->hscroll);
+	cytp_dbg_dump("left = %d\n", report_data->left);
+	cytp_dbg_dump("right = %d\n", report_data->right);
+	cytp_dbg_dump("middle = %d\n", report_data->middle);
+
+	return 0;
+}
+
+static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
+{
+	int i;
+	struct input_dev *input = psmouse->dev;
+	struct cytp_data *cytp = psmouse->private;
+	struct cytp_report_data report_data;
+	struct cytp_contact *contact;
+	int slot;
+
+	if (cypress_parse_packet(psmouse, cytp, &report_data))
+		return;
+
+	if (cytp->mode & CYTP_BIT_ABS_MASK) {
+		struct input_mt_pos pos[CYTP_MAX_CONTACTS];
+		int slots[CYTP_MAX_MT_SLOTS];
+		int n = report_data.contact_cnt;
+		int ret;
+
+		if (n > CYTP_MAX_MT_SLOTS)
+			n = CYTP_MAX_MT_SLOTS;
+
+		for (i = 0; i < n; i++) {
+			contact = &report_data.contacts[i];
+			pos[i].x = contact->x;
+			pos[i].y = contact->y;
+		}
+
+		ret = input_mt_assign_slots(input, slots, pos, n);
+		if (ret < 0) {
+			psmouse_err(psmouse,
+			    "input_mt_assign_slots(%d) returned %d\n", n, ret);
+			n = 0;
+		}
+
+		for (i = 0; i < n; i++) {
+			contact = &report_data.contacts[i];
+			slot = slots[i];
+			input_mt_slot(input, slot);
+			input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+			input_report_abs(input, ABS_MT_POSITION_X, contact->x);
+			input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
+			input_report_abs(input, ABS_MT_PRESSURE, contact->z);
+		}
+
+		input_mt_sync_frame(input);
+
+	} else {
+		if (report_data.contact_cnt == 1) {
+			input_report_rel(input, REL_X, report_data.contacts[0].x);
+			input_report_rel(input, REL_Y, report_data.contacts[0].y);
+		}
+
+		input_report_rel(input, REL_WHEEL, report_data.vscroll);
+		if (cytp->mode & CYTP_BIT_CYPRESS_REL)
+			input_report_rel(input, REL_HWHEEL, report_data.hscroll);
+	}
+
+	input_report_key(input, BTN_LEFT, report_data.left);
+	input_report_key(input, BTN_RIGHT, report_data.right);
+	input_report_key(input, BTN_MIDDLE, report_data.middle);
+
+	input_sync(input);
+}
+
+static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
+{
+	int contact_cnt;
+	int index = psmouse->pktcnt - 1;
+	unsigned char *packet = psmouse->packet;
+	struct cytp_data *cytp = psmouse->private;
+
+	if (index < 0 || index > cytp->pkt_size)
+		return PSMOUSE_BAD_DATA;
+
+	if ((index == 0) && ((packet[0] & 0xfc) == 0)) {
+		/* call packet process for reporting finger leave. */
+		cypress_process_packet(psmouse, 1);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	if (cytp->mode & CYTP_BIT_ABS_MASK) {
+		if (index == 0) {
+			if ((packet[0] & 0x08) == 0x08)
+				return PSMOUSE_BAD_DATA;
+
+			contact_cnt = cypress_get_finger_count(packet[0]);
+
+			if (contact_cnt > 5)
+				return PSMOUSE_BAD_DATA;
+
+			if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) {
+				CYTP_SET_PACKET_SIZE(4);
+				if (contact_cnt == 2)
+					CYTP_SET_PACKET_SIZE(7);
+			} else {
+				CYTP_SET_PACKET_SIZE(5);
+				if (contact_cnt == 2)
+					CYTP_SET_PACKET_SIZE(8);
+			}
+		}
+
+		return PSMOUSE_GOOD_DATA;
+	} else {
+		if (index == 0) {
+			if ((packet[0] & 0x08) != 0x08)
+				return PSMOUSE_BAD_DATA;
+
+			CYTP_SET_PACKET_SIZE(3);
+			if (cytp->mode & CYTP_BIT_CYPRESS_REL)
+				CYTP_SET_PACKET_SIZE(5);
+		}
+
+		return PSMOUSE_GOOD_DATA;
+	}
+}
+
+static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	if (psmouse->pktcnt >= cytp->pkt_size) {
+		cypress_process_packet(psmouse, 0);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	return cypress_validate_byte(psmouse);
+}
+
+static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	if (rate >= 80) {
+		psmouse->rate = 80;
+		CYTP_SET_MODE_BIT(CYTP_BIT_HIGH_RATE);
+	} else {
+		psmouse->rate = 40;
+		CYTP_CLEAR_MODE_BIT(CYTP_BIT_HIGH_RATE);
+	}
+
+	ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
+		    PSMOUSE_CMD_SETRATE);
+}
+
+static void cypress_disconnect(struct psmouse *psmouse)
+{
+	cypress_reset(psmouse);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+#if 0
+/*
+ * FIXME: cypress_reconnect() never works...
+ * cypress_detect() always fails here.
+ */
+static int cypress_reconnect(struct psmouse *psmouse)
+{
+	int tries = CYTP_PS2_CMD_TRIES;
+	int rc;
+
+	do {
+		cypress_reset(psmouse);
+		rc = cypress_detect(psmouse, false);
+	} while (rc && (--tries > 0));
+
+	if (rc) {
+		psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
+		return -1;
+	}
+
+	if (cypress_query_hardware(psmouse)) {
+		psmouse_err(psmouse, "Reconnect: unable to query Trackpad hardware.\n");
+		return -1;
+	}
+
+	if (cypress_set_absolute_mode(psmouse)) {
+		psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
+		return -1;
+	}
+
+	return 0;
+}
+#endif
+
+int cypress_init(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp;
+
+	cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
+	psmouse->private = (void *)cytp;
+	if (cytp == NULL)
+		return -ENOMEM;
+
+	cypress_reset(psmouse);
+
+	if (cypress_query_hardware(psmouse)) {
+		psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
+		goto err_exit;
+	}
+
+	if (cypress_set_absolute_mode(psmouse)) {
+		psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
+		goto err_exit;
+	}
+
+	if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
+		psmouse_err(psmouse, "init: Unable to set input params.\n");
+		goto err_exit;
+	}
+
+	psmouse->model = 1;
+	psmouse->protocol_handler = cypress_protocol_handler;
+	psmouse->set_rate = cypress_set_rate;
+	psmouse->disconnect = cypress_disconnect;
+#if 0
+	/* FIXME: cypress_reconnect() never works...
+	 * just let psmouse re-init() us for now.
+	 */
+	psmouse->reconnect = cypress_reconnect;
+#endif
+	psmouse->cleanup = cypress_reset;
+	psmouse->pktsize = 8;
+	psmouse->resync_time = 0;
+
+	return 0;
+
+err_exit:
+	/*
+	 * Reset Cypress Trackpad as a standard mouse. Then
+	 * let psmouse driver commmunicating with it as default PS2 mouse.
+	 */
+	cypress_reset(psmouse);
+
+	psmouse->private = NULL;
+	kfree(cytp);
+
+	return -1;
+}
+
+bool cypress_supported(void)
+{
+	return true;
+}
diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
new file mode 100644
index 0000000..ce70462
--- /dev/null
+++ b/drivers/input/mouse/cypress_ps2.h
@@ -0,0 +1,219 @@
+#ifndef _CYPRESS_PS2_H
+#define _CYPRESS_PS2_H
+
+#include "psmouse.h"
+
+#define CMD_BITS_MASK 0x03
+#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
+
+#define ENCODE_CMD(aa, bb, cc, dd) \
+	(COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
+#define CYTP_CMD_ABS_NO_PRESSURE_MODE       ENCODE_CMD(0, 1, 0, 0)
+#define CYTP_CMD_ABS_WITH_PRESSURE_MODE     ENCODE_CMD(0, 1, 0, 1)
+#define CYTP_CMD_SMBUS_MODE                 ENCODE_CMD(0, 1, 1, 0)
+#define CYTP_CMD_STANDARD_MODE              ENCODE_CMD(0, 2, 0, 0)  /* not implemented yet. */
+#define CYTP_CMD_CYPRESS_REL_MODE           ENCODE_CMD(1, 1, 1, 1)  /* not implemented yet. */
+#define CYTP_CMD_READ_CYPRESS_ID            ENCODE_CMD(0, 0, 0, 0)
+#define CYTP_CMD_READ_VITAL_STATISTICS      ENCODE_CMD(0, 0, 0, 1)
+#define CYTP_CMD_SET_HSCROLL_WIDTH(w)       ENCODE_CMD(1, 1, 0, (w))
+#define     CYTP_CMD_SET_HSCROLL_MASK       ENCODE_CMD(1, 1, 0, 0)
+#define CYTP_CMD_SET_VSCROLL_WIDTH(w)       ENCODE_CMD(1, 2, 0, (w))
+#define     CYTP_CMD_SET_VSCROLL_MASK       ENCODE_CMD(1, 2, 0, 0)
+#define CYTP_CMD_SET_PALM_GEOMETRY(e)       ENCODE_CMD(1, 2, 1, (e))
+#define     CYTP_CMD_PALM_GEMMETRY_MASK     ENCODE_CMD(1, 2, 1, 0)
+#define CYTP_CMD_SET_PALM_SENSITIVITY(s)    ENCODE_CMD(1, 2, 2, (s))
+#define     CYTP_CMD_PALM_SENSITIVITY_MASK  ENCODE_CMD(1, 2, 2, 0)
+#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s)   ENCODE_CMD(1, 3, ((s) >> 2), (s))
+#define     CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
+#define CYTP_CMD_REQUEST_BASELINE_STATUS    ENCODE_CMD(2, 0, 0, 1)
+#define CYTP_CMD_REQUEST_RECALIBRATION      ENCODE_CMD(2, 0, 0, 3)
+
+#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
+#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
+#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
+#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
+
+/* Cypress trackpad working mode. */
+#define CYTP_BIT_ABS_PRESSURE    (1 << 3)
+#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
+#define CYTP_BIT_CYPRESS_REL     (1 << 1)
+#define CYTP_BIT_STANDARD_REL    (1 << 0)
+#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
+#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
+#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
+
+#define CYTP_BIT_HIGH_RATE       (1 << 4)
+/*
+ * report mode bit is set, firmware working in Remote Mode.
+ * report mode bit is cleared, firmware working in Stream Mode.
+ */
+#define CYTP_BIT_REPORT_MODE     (1 << 5)
+
+/* scrolling width values for set HSCROLL and VSCROLL width command. */
+#define SCROLL_WIDTH_NARROW 1
+#define SCROLL_WIDTH_NORMAL 2
+#define SCROLL_WIDTH_WIDE   3
+
+#define PALM_GEOMETRY_ENABLE  1
+#define PALM_GEOMETRY_DISABLE 0
+
+#define CYPRESS_KEY_1 0x33
+#define CYPRESS_KEY_2 0xCC
+
+#define VITAL_STATICS_MASK 0x80
+#define FW_VERSION_MASX    0x7f
+#define FW_VER_HIGH_MASK 0x70
+#define FW_VER_LOW_MASK  0x0f
+
+/* Times to retry a ps2_command and millisecond delay between tries. */
+#define CYTP_PS2_CMD_TRIES 3
+#define CYTP_PS2_CMD_DELAY 500
+
+/* time out for PS/2 command only in milliseconds. */
+#define CYTP_CMD_TIMEOUT  200
+#define CYTP_DATA_TIMEOUT 30
+
+#define CYTP_EXT_CMD   0xe8
+#define CYTP_PS2_RETRY 0xfe
+#define CYTP_PS2_ERROR 0xfc
+
+#define CYTP_RESP_RETRY 0x01
+#define CYTP_RESP_ERROR 0xfe
+
+
+#define CYTP_105001_WIDTH  97   /* Dell XPS 13 */
+#define CYTP_105001_HIGH   59
+#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
+#define CYTP_DEFAULT_HIGH  (CYTP_105001_HIGH)
+
+#define CYTP_ABS_MAX_X     1600
+#define CYTP_ABS_MAX_Y     900
+#define CYTP_MAX_PRESSURE  255
+#define CYTP_MIN_PRESSURE  0
+
+/* header byte bits of relative package. */
+#define BTN_LEFT_BIT   0x01
+#define BTN_RIGHT_BIT  0x02
+#define BTN_MIDDLE_BIT 0x04
+#define REL_X_SIGN_BIT 0x10
+#define REL_Y_SIGN_BIT 0x20
+
+/* header byte bits of absolute package. */
+#define ABS_VSCROLL_BIT 0x10
+#define ABS_HSCROLL_BIT 0x20
+#define ABS_MULTIFINGER_TAP 0x04
+#define ABS_EDGE_MOTION_MASK 0x80
+
+#define DFLT_RESP_BITS_VALID     0x88  /* SMBus bit should not be set. */
+#define DFLT_RESP_SMBUS_BIT      0x80
+#define   DFLT_SMBUS_MODE        0x80
+#define   DFLT_PS2_MODE          0x00
+#define DFLT_RESP_BIT_MODE       0x40
+#define   DFLT_RESP_REMOTE_MODE  0x40
+#define   DFLT_RESP_STREAM_MODE  0x00
+#define DFLT_RESP_BIT_REPORTING  0x20
+#define DFLT_RESP_BIT_SCALING    0x10
+
+#define VITAL_BIT_PALM               0x80
+#define VITAL_BIT_STUBBORN           0x40
+#define VITAL_BIT_2F_JITTER          0x30
+#define VITAL_BIT_1F_JITTER          0x0c
+#define VITAL_BIT_APA                0x02
+#define VITAL_BIT_MTG                0x01
+#define VITAL_BIT_ABS_PKT_FORMAT_SET 0xf0
+#define VITAL_BIT_2F_SPIKE           0x0c
+#define VITAL_BIT_1F_SPIKE           0x03
+
+/* bits of first byte response of E9h-Status Request command. */
+#define RESP_BTN_RIGHT_BIT  0x01
+#define RESP_BTN_MIDDLE_BIT 0x02
+#define RESP_BTN_LEFT_BIT   0x04
+#define RESP_SCALING_BIT    0x10
+#define RESP_ENABLE_BIT     0x20
+#define RESP_REMOTE_BIT     0x40
+#define RESP_SMBUS_BIT      0x80
+
+#define CYTP_MAX_CONTACTS 5
+#define CYTP_MAX_MT_SLOTS 2
+
+enum cytp_type {
+	CYTP_STG,
+	CYTP_MTG,
+	CYTP_APA,
+};
+
+struct cytp_contact {
+	int x;
+	int y;
+	int z;  /* also named as touch pressure. */
+};
+
+/* The structure of */
+struct cytp_report_data {
+	int contact_cnt;
+	struct cytp_contact contacts[CYTP_MAX_CONTACTS];
+	unsigned int left:1;
+	unsigned int right:1;
+	unsigned int middle:1;
+	unsigned int tap:1;  /* multi-finger tap detected. */
+	signed char vscroll;
+	signed char hscroll;
+};
+
+/* The structure of Cypress Trackpad device private data. */
+struct cytp_data {
+	int fw_version;
+
+	int pkt_size;
+	int mode;
+
+	int scaling;
+	int reporting;
+
+	int tp_min_pressure;
+	int tp_max_pressure;
+	int tp_width;  /* X direction physical size in mm. */
+	int tp_high;  /* Y direction physical size in mm. */
+	int tp_max_abs_x;  /* Max X absolution units can be reported. */
+	int tp_max_abs_y;  /* Max Y absolution units can be reported. */
+
+	int tp_res_x;  /* X resolution in units/mm. */
+	int tp_res_y;  /* Y resolution in units/mm. */
+
+	enum cytp_type tp_type;
+	unsigned char tp_palm;
+	unsigned char tp_stubborn;
+	unsigned char tp_2f_jitter;
+	unsigned char tp_1f_jitter;
+	unsigned char tp_abs_packet_format_set;
+	unsigned char tp_2f_spike;
+	unsigned char tp_1f_spike;
+
+	int vital_statics_supported;
+
+	int prev_contact_cnt;
+	int zero_packet_cnt;
+	struct cytp_report_data prev_report_data;
+};
+
+
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+int cypress_detect(struct psmouse *psmouse, bool set_properties);
+int cypress_init(struct psmouse *psmouse);
+bool cypress_supported(void);
+#else
+inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+inline int cypress_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+inline bool cypress_supported(void)
+{
+	return 0;
+}
+#endif /* CONFIG_MOUSE_PS2_CYPRESS */
+
+#endif  /* _CYPRESS_PS2_H */
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 3/4] input: Cypress PS/2 Trackpad link into psmouse-base
  2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
  2012-11-29 21:57 ` [PATCH v3 1/4] input: increase struct ps2dev cmdbuf[] to 8 bytes Kamal Mostafa
  2012-11-29 21:57 ` [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver Kamal Mostafa
@ 2012-11-29 21:58 ` Kamal Mostafa
  2012-11-29 21:58 ` [PATCH v3 4/4] input: Cypress PS/2 Trackpad simulated multitouch Kamal Mostafa
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 17+ messages in thread
From: Kamal Mostafa @ 2012-11-29 21:58 UTC (permalink / raw)
  To: linux-input, linux-kernel, Dmitry Torokhov, Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, Dudley Du,
	Cypress Semiconductor Corporation, Kamal Mostafa, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski

From: Cypress Semiconductor Corporation <customercare@cypress.com>

Original code contributed by Cypress Semiconductor Corporation,
modified by Kamal Mostafa.

BugLink: http://launchpad.net/bugs/978807

Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
 drivers/input/mouse/Kconfig        |   10 ++++++++++
 drivers/input/mouse/Makefile       |    1 +
 drivers/input/mouse/psmouse-base.c |   32 ++++++++++++++++++++++++++++++++
 drivers/input/mouse/psmouse.h      |    1 +
 4 files changed, 44 insertions(+)

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index cd6268c..88954dd 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS
 
 	  If unsure, say Y.
 
+config MOUSE_PS2_CYPRESS
+       bool "Cypress PS/2 mouse protocol extension" if EXPERT
+       default y
+       depends on MOUSE_PS2
+       help
+         Say Y here if you have a Cypress PS/2 Trackpad connected to
+         your system.
+
+         If unsure, say Y.
+
 config MOUSE_PS2_LIFEBOOK
 	bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
 	default y
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 46ba755..323e352 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -32,3 +32,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK)	+= lifebook.o
 psmouse-$(CONFIG_MOUSE_PS2_SENTELIC)	+= sentelic.o
 psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT)	+= trackpoint.o
 psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)	+= touchkit_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_CYPRESS)	+= cypress_ps2.o
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 22fe254..cff065f 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -34,6 +34,7 @@
 #include "touchkit_ps2.h"
 #include "elantech.h"
 #include "sentelic.h"
+#include "cypress_ps2.h"
 
 #define DRIVER_DESC	"PS/2 mouse driver"
 
@@ -759,6 +760,28 @@ static int psmouse_extensions(struct psmouse *psmouse,
 	}
 
 /*
+ * Try Cypress Trackpad.
+ * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
+ * upsets some modules of Cypress Trackpads.
+ */
+	if (max_proto > PSMOUSE_IMEX &&
+			cypress_detect(psmouse, set_properties) == 0) {
+		if (cypress_supported()) {
+			if (cypress_init(psmouse) == 0)
+				return PSMOUSE_CYPRESS;
+
+			/*
+			 * Finger Sensing Pad probe upsets some modules of
+			 * Cypress Trackpad, must avoid Finger Sensing Pad
+			 * probe if Cypress Trackpad device detected.
+			 */
+			return PSMOUSE_PS2;
+		}
+
+		max_proto = PSMOUSE_IMEX;
+	}
+
+/*
  * Try ALPS TouchPad
  */
 	if (max_proto > PSMOUSE_IMEX) {
@@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
 		.alias		= "thinkps",
 		.detect		= thinking_detect,
 	},
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+	{
+		.type		= PSMOUSE_CYPRESS,
+		.name		= "CyPS/2",
+		.alias		= "cypress",
+		.detect		= cypress_detect,
+		.init		= cypress_init,
+	},
+#endif
 	{
 		.type		= PSMOUSE_GENPS,
 		.name		= "GenPS/2",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index fe1df23..2f0b39d 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -95,6 +95,7 @@ enum psmouse_type {
 	PSMOUSE_ELANTECH,
 	PSMOUSE_FSP,
 	PSMOUSE_SYNAPTICS_RELATIVE,
+	PSMOUSE_CYPRESS,
 	PSMOUSE_AUTO		/* This one should always be last */
 };
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 4/4] input: Cypress PS/2 Trackpad simulated multitouch
  2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
                   ` (2 preceding siblings ...)
  2012-11-29 21:58 ` [PATCH v3 3/4] input: Cypress PS/2 Trackpad link into psmouse-base Kamal Mostafa
@ 2012-11-29 21:58 ` Kamal Mostafa
  2012-12-03  1:58   ` Dudley Du
  2012-12-03  7:36 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Dmitry Torokhov
  2012-12-03  7:50 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Henrik Rydberg
  5 siblings, 1 reply; 17+ messages in thread
From: Kamal Mostafa @ 2012-11-29 21:58 UTC (permalink / raw)
  To: linux-input, linux-kernel, Dmitry Torokhov, Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, Dudley Du,
	Cypress Semiconductor Corporation, Kamal Mostafa, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski

Instead of SEMI_MT, present a full mt interface with simulated contact
positions for >=3 fingers.  Enables e.g. multi-finger tap and drag.

Signed-off-by: Kamal Mostafa <kamal@canonical.com>
---
 drivers/input/mouse/cypress_ps2.c |   16 ++++++++++++++++
 drivers/input/mouse/cypress_ps2.h |   14 +++++++++++++-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
index 472342a..bbaa9b9 100644
--- a/drivers/input/mouse/cypress_ps2.c
+++ b/drivers/input/mouse/cypress_ps2.c
@@ -408,7 +408,9 @@ static int cypress_set_input_params(struct input_dev *input,
 			return ret;
 		}
 
+#if ( CYPRESS_SIMULATED_MT != 1 )
 		__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
+#endif
 
 		if (cytp->tp_res_x && cytp->tp_res_x) {
 			input_abs_set_res(input, ABS_X, cytp->tp_res_x);
@@ -531,6 +533,20 @@ static int cypress_parse_packet(struct psmouse *psmouse,
 				((packet[5] & 0x0f) << 8) | packet[7];
 			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
 				report_data->contacts[1].z = report_data->contacts[0].z;
+#if ( CYPRESS_SIMULATED_MT == 1 )
+			/* simulate contact positions for >2 fingers */
+			if ( report_data->contact_cnt >= 3 )
+				for ( i=1; i<report_data->contact_cnt; i++ ) {
+				    report_data->contacts[i].x =
+						    report_data->contacts[0].x
+						    + 100*(i)*((i%2)?-1:1);
+				    report_data->contacts[i].y =
+						    report_data->contacts[0].y;
+				    if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+					    report_data->contacts[i].z =
+						    report_data->contacts[0].z;
+				}
+#endif
 		}
 
 		report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
index ce70462..4b903a9 100644
--- a/drivers/input/mouse/cypress_ps2.h
+++ b/drivers/input/mouse/cypress_ps2.h
@@ -134,7 +134,19 @@
 #define RESP_SMBUS_BIT      0x80
 
 #define CYTP_MAX_CONTACTS 5
-#define CYTP_MAX_MT_SLOTS 2
+
+/*
+ * CYPRESS_SIMULATED_MT
+ *   set to 1 for simulated multitouch (up to CTYP_MAX_CONTACTS fingers)
+ *   set to 0 for SEMI_MT (2 fingers only)
+ */
+#define CYPRESS_SIMULATED_MT 1
+
+#if ( CYPRESS_SIMULATED_MT == 1 )
+# define CYTP_MAX_MT_SLOTS 16
+#else
+# define CYTP_MAX_MT_SLOTS 2
+#endif
 
 enum cytp_type {
 	CYTP_STG,
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* RE: [PATCH v3 4/4] input: Cypress PS/2 Trackpad simulated multitouch
  2012-11-29 21:58 ` [PATCH v3 4/4] input: Cypress PS/2 Trackpad simulated multitouch Kamal Mostafa
@ 2012-12-03  1:58   ` Dudley Du
  0 siblings, 0 replies; 17+ messages in thread
From: Dudley Du @ 2012-12-03  1:58 UTC (permalink / raw)
  To: Kamal Mostafa, linux-input, linux-kernel, Dmitry Torokhov,
	Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, customercare, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski

> Instead of SEMI_MT, present a full mt interface with simulated contact positions for >=3 fingers.  Enables e.g. multi-finger tap and drag.
>
> Signed-off-by: Kamal Mostafa <kamal@canonical.com>
> ---
>  drivers/input/mouse/cypress_ps2.c |   16 ++++++++++++++++
>  drivers/input/mouse/cypress_ps2.h |   14 +++++++++++++-
>  2 files changed, 29 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
> index 472342a..bbaa9b9 100644
> --- a/drivers/input/mouse/cypress_ps2.c
> +++ b/drivers/input/mouse/cypress_ps2.c
> @@ -408,7 +408,9 @@ static int cypress_set_input_params(struct input_dev *input,
>                       return ret;
>               }
>
> +#if ( CYPRESS_SIMULATED_MT != 1 )
>               __set_bit(INPUT_PROP_SEMI_MT, input->propbit);
> +#endif
>
>               if (cytp->tp_res_x && cytp->tp_res_x) {


Here, should be "if (cytp->tp_res_x && cytp->tp_res_y) {".
And since cytp->tp_max_abs_y read from trackpad maybe zero, so we should better double check here.


>                       input_abs_set_res(input, ABS_X, cytp->tp_res_x); @@ -531,6 +533,20 @@ static int cypress_parse_packet(struct psmouse *psmouse,
>                               ((packet[5] & 0x0f) << 8) | packet[7];
>                       if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>                               report_data->contacts[1].z = report_data->contacts[0].z;
> +#if ( CYPRESS_SIMULATED_MT == 1 )
> +                     /* simulate contact positions for >2 fingers */
> +                     if ( report_data->contact_cnt >= 3 )
> +                             for ( i=1; i<report_data->contact_cnt; i++ ) {
> +                                 report_data->contacts[i].x =
> +                                                 report_data->contacts[0].x
> +                                                 + 100*(i)*((i%2)?-1:1);
> +                                 report_data->contacts[i].y =
> +                                                 report_data->contacts[0].y;
> +                                 if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
> +                                         report_data->contacts[i].z =
> +                                                 report_data->contacts[0].z;
> +                             }
> +#endif
>               }
>
>               report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
> index ce70462..4b903a9 100644
> --- a/drivers/input/mouse/cypress_ps2.h
> +++ b/drivers/input/mouse/cypress_ps2.h
> @@ -134,7 +134,19 @@
>  #define RESP_SMBUS_BIT      0x80
>
>  #define CYTP_MAX_CONTACTS 5
> -#define CYTP_MAX_MT_SLOTS 2
> +
> +/*
> + * CYPRESS_SIMULATED_MT
> + *   set to 1 for simulated multitouch (up to CTYP_MAX_CONTACTS fingers)
> + *   set to 0 for SEMI_MT (2 fingers only)
> + */
> +#define CYPRESS_SIMULATED_MT 1
> +
> +#if ( CYPRESS_SIMULATED_MT == 1 )
> +# define CYTP_MAX_MT_SLOTS 16
> +#else
> +# define CYTP_MAX_MT_SLOTS 2
> +#endif
>
>  enum cytp_type {
>       CYTP_STG,
> --
> 1.7.10.4


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-11-29 21:57 ` [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver Kamal Mostafa
@ 2012-12-03  3:20   ` Dudley Du
  2012-12-03  5:57   ` Dudley Du
  2012-12-03  7:45   ` Henrik Rydberg
  2 siblings, 0 replies; 17+ messages in thread
From: Dudley Du @ 2012-12-03  3:20 UTC (permalink / raw)
  To: Kamal Mostafa, linux-input, linux-kernel, Dmitry Torokhov,
	Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, customercare, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski, dudl

Hi Kamal,

I'm the writer of this driver.
Thanks for help upstream this driver.

I noticed that you marked out the reconnect function for it never work at all.
But in my test, it seems work fine.
I do the test steps as below:
1. load this driver with Cypress trackpad. Cursor moving on system;
2. directly unplug the trackpad from system;
3. reconnect the trackpad device to PC again;
4. moving finger on trackpad to check cursor still working on the system.
And also, I checked the debug message through dmesg, it makes sure that the reconnection function is called, and also the driver and trackpad still working fine.

So I want to know what situation is in your test to this function,
could you help describe your test steps to me, so we can figure out this issue.

Thanks.

Best Wishes,
Dudley Du
dudl@cyrpess.com



>From: Cypress Semiconductor Corporation <customercare@cypress.com>
>
>Input/mouse driver for Cypress PS/2 Trackpad.
>
>Original code contributed by Cypress Semiconductor Corporation,
>modified by Kamal Mostafa and Kyle Fazzari.
>
>BugLink: http://launchpad.net/bugs/978807
>
>Signed-off-by: Kamal Mostafa <kamal@canonical.com>
>Signed-off-by: Kyle Fazzari <git@status.e4ward.com>
>Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
>Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
>Acked-by: Herton Krzesinski <herton.krzesinski@canonical.com>
>---
> drivers/input/mouse/cypress_ps2.c |  830 +++++++++++++++++++++++++++++++++++++
> drivers/input/mouse/cypress_ps2.h |  219 ++++++++++
> 2 files changed, 1049 insertions(+)
> create mode 100644 drivers/input/mouse/cypress_ps2.c
> create mode 100644 drivers/input/mouse/cypress_ps2.h
>
>diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
>new file mode 100644
>index 0000000..472342a
>--- /dev/null
>+++ b/drivers/input/mouse/cypress_ps2.c
>@@ -0,0 +1,830 @@
>+/*
>+ * Cypress Trackpad PS/2 mouse driver
>+ *
>+ * Copyright (c) 2012 Cypress Semiconductor Corporation.
>+ *
>+ * Additional contributors include:
>+ *   Kamal Mostafa <kamal@canonical.com>
>+ *   Kyle Fazzari <git@status.e4ward.com>
>+ *
>+ * This program is free software; you can redistribute it and/or modify it
>+ * under the terms of the GNU General Public License version 2 as published by
>+ * the Free Software Foundation.
>+ */
>+
>+#include <linux/init.h>
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <linux/slab.h>
>+#include <linux/serio.h>
>+#include <linux/libps2.h>
>+#include <linux/input.h>
>+#include <linux/input/mt.h>
>+#include <linux/sched.h>
>+#include <linux/wait.h>
>+
>+#include "cypress_ps2.h"
>+
>+#define CYTP_DBG_DUMP 0               /* set to 1 for more verbose debug dump */
>+
>+#define cytp_dbg(fmt, ...)  \
>+      do {  \
>+              if (cytp)  \
>+                      psmouse_dbg(psmouse, pr_fmt(fmt), ##__VA_ARGS__);  \
>+      } while (0)
>+
>+#if CYTP_DBG_DUMP
>+# define cytp_dbg_dump cytp_dbg
>+#else
>+# define cytp_dbg_dump(fmt, ...)
>+#endif
>+
>+
>+/* p is a pointer points to the buffer containing Cypress Keys. */
>+#define IS_CYPRESS_KEY(p) ((p[0] == CYPRESS_KEY_1) && (p[1] == CYPRESS_KEY_2))
>+#define CYTP_SET_PACKET_SIZE(n) { psmouse->pktsize = cytp->pkt_size = (n); }
>+#define CYTP_SET_MODE_BIT(x)  \
>+      do {  \
>+              if ((x) & CYTP_BIT_ABS_REL_MASK)  \
>+                      cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK);  \
>+              cytp->mode |= (x);  \
>+      } while (0)
>+#define CYTP_CLEAR_MODE_BIT(x)        { cytp->mode &= ~(x); }
>+
>+#define CYTP_SUPPORT_ABS
>+
>+static unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
>+static unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
>+
>+static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      struct ps2dev *ps2dev = &psmouse->ps2dev;
>+
>+      if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
>+              cytp_dbg("send command 0x%02x failed, resp 0x%02x\n",
>+                       value & 0xff, ps2dev->nak);
>+              if (ps2dev->nak == CYTP_PS2_RETRY)
>+                      return CYTP_PS2_RETRY;
>+              else
>+                      return CYTP_PS2_ERROR;
>+      }
>+
>+      cytp_dbg("send command 0x%02x success, resp 0xfa\n", value & 0xff);
>+
>+      return 0;
>+}
>+
>+static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
>+                             unsigned char data)
>+{
>+      struct ps2dev *ps2dev = &psmouse->ps2dev;
>+      int tries = CYTP_PS2_CMD_TRIES;
>+      int rc;
>+
>+      ps2_begin_command(ps2dev);
>+
>+      do {
>+              /*
>+               * send extension command 0xE8 or 0xF3,
>+               * if send extension command failed,
>+               * try to send recovery command to make
>+               * trackpad device return to ready wait command state.
>+               * It alwasy success based on this recovery commands.
>+               */
>+              rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
>+              if (rc == CYTP_PS2_RETRY) {
>+                      rc = cypress_ps2_sendbyte(psmouse, 0x00);
>+                      if (rc == CYTP_PS2_RETRY)
>+                              rc = cypress_ps2_sendbyte(psmouse, 0x0a);
>+              }
>+              if (rc == CYTP_PS2_ERROR)
>+                      continue;
>+
>+              rc = cypress_ps2_sendbyte(psmouse, data);
>+              if (rc == CYTP_PS2_RETRY)
>+                      rc = cypress_ps2_sendbyte(psmouse, data);
>+              if (rc == CYTP_PS2_ERROR)
>+                      continue;
>+              else
>+                      break;
>+      } while (--tries > 0);
>+
>+      ps2_end_command(ps2dev);
>+
>+      return rc;
>+}
>+
>+static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
>+                                     unsigned char cmd,
>+                                     unsigned char *param)
>+{
>+      int i;
>+      int rc;
>+      struct ps2dev *ps2dev = &psmouse->ps2dev;
>+      struct cytp_data *cytp = psmouse->private;
>+      enum psmouse_state old_state;
>+      unsigned char old_pktsize;
>+
>+      ps2_begin_command(&psmouse->ps2dev);
>+
>+      old_state = psmouse->state;
>+      psmouse->state = PSMOUSE_CMD_MODE;
>+      psmouse->pktcnt = 0;
>+      old_pktsize = psmouse->pktsize;
>+      psmouse->pktsize = 3;
>+      if (cmd == CYTP_CMD_READ_VITAL_STATISTICS)
>+              psmouse->pktsize = 8;
>+      memset(param, 0, psmouse->pktsize);
>+
>+      rc = cypress_ps2_sendbyte(psmouse, 0xe9);
>+      if (rc < 0)
>+              goto out;
>+
>+      wait_event_timeout(ps2dev->wait,
>+                      (psmouse->pktcnt >= psmouse->pktsize),
>+                      msecs_to_jiffies(CYTP_CMD_TIMEOUT));
>+
>+      memcpy(param, psmouse->packet, psmouse->pktsize);
>+
>+      cytp_dbg("Command 0x%02x response data: (0x)", cmd);
>+      for (i = 0; i < psmouse->pktsize; i++)
>+              cytp_dbg(" %02x", param[i]);
>+      cytp_dbg("\n");
>+
>+out:
>+      psmouse->state = old_state;
>+      psmouse->pktcnt = 0;
>+      psmouse->pktsize = old_pktsize;
>+
>+      ps2_end_command(&psmouse->ps2dev);
>+
>+      return rc;
>+}
>+
>+static int cypress_verify_cmd_state(struct psmouse *psmouse,
>+                                  unsigned char cmd, unsigned char *param)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      bool rate_match = 0;
>+      bool resolution_match = 0;
>+      int i;
>+
>+      /* callers will do further checking. */
>+      if ((cmd == CYTP_CMD_READ_CYPRESS_ID) ||
>+          (cmd == CYTP_CMD_STANDARD_MODE) ||
>+          (cmd == CYTP_CMD_READ_VITAL_STATISTICS))
>+              return 0;
>+      if (((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID) &&
>+          ((param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE)) {
>+              for (i = 0; i < sizeof(cytp_resolution); i++)
>+                      if (cytp_resolution[i] == param[1])
>+                              resolution_match =  1;
>+
>+              for (i = 0; i < sizeof(cytp_rate); i++)
>+                      if (cytp_rate[i] == param[2])
>+                              rate_match = 1;
>+
>+              if (resolution_match && rate_match)
>+                      return 0;
>+      }
>+
>+      cytp_dbg("verify cmd state failed.\n");
>+      return -1;
>+}
>+
>+static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
>+                              unsigned char *param)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      int tries = CYTP_PS2_CMD_TRIES;
>+      int rc;
>+
>+      cytp_dbg("send extension cmd 0x%02x, [%d %d %d %d]\n",
>+               cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
>+               DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
>+      do {
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
>+
>+              rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
>+              if ((rc == 0) &&
>+                  (cypress_verify_cmd_state(psmouse, cmd, param) == 0))
>+                      return 0;
>+      } while (--tries > 0);
>+
>+
>+      if (tries <= 0)
>+              return -1;
>+
>+      return 0;
>+
>+}
>+
>+int cypress_detect(struct psmouse *psmouse, bool set_properties)
>+{
>+      unsigned char param[3];
>+
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
>+              return -1;
>+
>+      if (!IS_CYPRESS_KEY(param))
>+              return -ENODEV;
>+
>+      if (set_properties) {
>+              psmouse->vendor = "Cypress";
>+              psmouse->name = "Trackpad";
>+      }
>+
>+      return 0;
>+}
>+
>+static int cypress_read_fw_version(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      unsigned char param[3];
>+
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
>+              return -1;
>+
>+      if (!IS_CYPRESS_KEY(param))
>+              return -ENODEV;
>+
>+      cytp->fw_version = param[2] & FW_VERSION_MASX;
>+      cytp->vital_statics_supported = (param[2] & VITAL_STATICS_MASK) ? 1 : 0;
>+
>+      cytp_dbg("cytp->fw_version = %d\n", cytp->fw_version);
>+      cytp_dbg("cytp->vital_statics_supported = %d\n",
>+               cytp->vital_statics_supported);
>+      return 0;
>+}
>+
>+static int cypress_read_vital_statistics(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      unsigned char param[8];
>+
>+      /* set default values for vital statistics not supported trackpad. */
>+      cytp->tp_width = CYTP_DEFAULT_WIDTH;
>+      cytp->tp_high = CYTP_DEFAULT_HIGH;
>+      cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
>+      cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
>+      cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
>+      cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
>+      cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
>+      cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
>+
>+      memset(param, 0, sizeof(param));
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_VITAL_STATISTICS, param) == 0) {
>+              /* Update trackpad parameters. */
>+              cytp->tp_max_abs_x = (param[1] << 8) | param[0];
>+              cytp->tp_max_abs_y = (param[3] << 8) | param[2];
>+              cytp->tp_min_pressure = param[4];
>+              cytp->tp_max_pressure = param[5];
>+
>+              if (param[6] & VITAL_BIT_APA)
>+                      cytp->tp_type = CYTP_APA;
>+              else if (param[6] & VITAL_BIT_MTG)
>+                      cytp->tp_type = CYTP_MTG;
>+              else
>+                      cytp->tp_type = CYTP_STG;
>+              cytp->tp_palm = (param[6] & VITAL_BIT_PALM) ? 1 : 0;
>+              cytp->tp_stubborn = (param[6] & VITAL_BIT_STUBBORN) ? 1 : 0;
>+              cytp->tp_2f_jitter = (param[6] & VITAL_BIT_2F_JITTER) >> 4;
>+              cytp->tp_1f_jitter = (param[6] & VITAL_BIT_1F_JITTER) >> 2;
>+              cytp->tp_abs_packet_format_set =
>+                      (param[7] & VITAL_BIT_ABS_PKT_FORMAT_SET) >> 4;
>+              cytp->tp_2f_spike = (param[7] & VITAL_BIT_2F_SPIKE) >> 2;
>+              cytp->tp_1f_spike = (param[7] & VITAL_BIT_1F_SPIKE);
>+
>+      }
>+
>+      if (!cytp->tp_max_pressure ||
>+          (cytp->tp_max_pressure < cytp->tp_min_pressure) ||
>+          (!cytp->tp_width || !cytp->tp_high) ||
>+          (!cytp->tp_max_abs_x) ||
>+          (cytp->tp_max_abs_x < cytp->tp_width) ||
>+          (!cytp->tp_max_abs_y) ||
>+          (cytp->tp_max_abs_y < cytp->tp_high))
>+              return -1;
>+
>+      cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
>+      cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
>+
>+      cytp_dbg_dump("Dump trackpad hardware configuration as below:\n");
>+      cytp_dbg_dump("cytp->tp_width = %d\n", cytp->tp_width);
>+      cytp_dbg_dump("cytp->tp_high = %d\n", cytp->tp_high);
>+      cytp_dbg_dump("cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
>+      cytp_dbg_dump("cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
>+      cytp_dbg_dump("cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
>+      cytp_dbg_dump("cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
>+      cytp_dbg_dump("cytp->tp_res_x = %d\n", cytp->tp_res_x);
>+      cytp_dbg_dump("cytp->tp_res_y = %d\n", cytp->tp_res_y);
>+      cytp_dbg_dump("cytp->tp_type = %d\n", cytp->tp_type);
>+      cytp_dbg_dump("cytp->tp_palm = %d\n", cytp->tp_palm);
>+      cytp_dbg_dump("cytp->tp_stubborn = %d\n", cytp->tp_stubborn);
>+      cytp_dbg_dump("cytp->tp_1f_jitter = %d\n", cytp->tp_1f_jitter);
>+      cytp_dbg_dump("cytp->tp_2f_jitter = %d\n", cytp->tp_2f_jitter);
>+      cytp_dbg_dump("cytp->tp_1f_spike = %d\n", cytp->tp_1f_spike);
>+      cytp_dbg_dump("cytp->tp_2f_spike = %d\n", cytp->tp_2f_spike);
>+      cytp_dbg_dump("cytp->tp_abs_packet_format_set = %d\n",
>+                    cytp->tp_abs_packet_format_set);
>+
>+      return 0;
>+}
>+
>+static int cypress_query_hardware(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (cypress_read_fw_version(psmouse))
>+              return -1;
>+
>+      if (cytp->vital_statics_supported) {
>+              if (cypress_read_vital_statistics(psmouse))
>+                      return -1;
>+      }
>+
>+      return 0;
>+}
>+
>+static int cypress_set_absolute_mode(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      unsigned char param[3];
>+
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
>+              return -1;
>+
>+      CYTP_SET_MODE_BIT(CYTP_BIT_ABS_PRESSURE);
>+      CYTP_SET_PACKET_SIZE(5);
>+
>+      return 0;
>+}
>+
>+/*
>+ * reset trackpad device to standard relative mode.
>+ * This is also the defalut mode when trackpad powered on.
>+ */
>+static void cypress_reset(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      psmouse_reset(psmouse);
>+
>+      CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
>+      CYTP_SET_PACKET_SIZE(3);
>+
>+      cytp->prev_contact_cnt = 0;
>+}
>+
>+static int cypress_set_input_params(struct input_dev *input,
>+                                  struct cytp_data *cytp)
>+{
>+      int ret;
>+
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              __set_bit(EV_ABS, input->evbit);
>+              input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
>+              input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
>+              input_set_abs_params(input, ABS_PRESSURE,
>+                                   cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
>+              input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
>+
>+              /* finger position */
>+              input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
>+              input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
>+              input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
>+
>+              ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
>+                      INPUT_MT_POINTER|INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
>+              if (ret < 0) {
>+                      return ret;
>+              }
>+
>+              __set_bit(INPUT_PROP_SEMI_MT, input->propbit);
>+
>+              if (cytp->tp_res_x && cytp->tp_res_x) {
>+                      input_abs_set_res(input, ABS_X, cytp->tp_res_x);
>+                      input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
>+
>+                      input_abs_set_res(input, ABS_MT_POSITION_X,
>+                                        cytp->tp_res_x);
>+                      input_abs_set_res(input, ABS_MT_POSITION_Y,
>+                                        cytp->tp_res_y);
>+
>+              }
>+
>+              __set_bit(BTN_TOUCH, input->keybit);
>+              __set_bit(BTN_TOOL_FINGER, input->keybit);
>+              __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
>+              __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
>+              __set_bit(BTN_TOOL_QUADTAP, input->keybit);
>+              __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
>+
>+              __clear_bit(EV_REL, input->evbit);
>+              __clear_bit(REL_X, input->relbit);
>+              __clear_bit(REL_Y, input->relbit);
>+      } else {
>+              __set_bit(EV_REL, input->evbit);
>+              __set_bit(REL_X, input->relbit);
>+              __set_bit(REL_Y, input->relbit);
>+              __set_bit(REL_WHEEL, input->relbit);
>+              __set_bit(REL_HWHEEL, input->relbit);
>+
>+              __clear_bit(EV_ABS, input->evbit);
>+      }
>+
>+      __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
>+      __set_bit(EV_KEY, input->evbit);
>+      __set_bit(BTN_LEFT, input->keybit);
>+      __set_bit(BTN_RIGHT, input->keybit);
>+      __set_bit(BTN_MIDDLE, input->keybit);
>+
>+      input_set_drvdata(input, cytp);
>+
>+      return 0;
>+}
>+
>+static int cypress_get_finger_count(unsigned char header_byte)
>+{
>+      unsigned char bits6_7;
>+      int finger_count;
>+
>+      bits6_7 = header_byte >> 6;
>+      finger_count = bits6_7 & 0x03;
>+
>+      if (finger_count != 1) {
>+              if (header_byte & ABS_HSCROLL_BIT) {
>+                      if (finger_count == 0) {
>+                              /* HSCROLL gets added on to 0 finger count. */
>+                              finger_count = 4;
>+                              /* should remove HSCROLL bit. */
>+                      } else {
>+                              if (finger_count == 2) {
>+                                      finger_count = 5;
>+                              } else {
>+                                      /* Invalid contact (e.g. palm). Ignore it. */
>+                                      finger_count = 0;
>+                              }
>+                      }
>+              }
>+      }
>+
>+      return finger_count;
>+}
>+
>+
>+static int cypress_parse_packet(struct psmouse *psmouse,
>+                              struct cytp_data *cytp, struct cytp_report_data *report_data)
>+{
>+      int i;
>+      unsigned char *packet = psmouse->packet;
>+      unsigned char header_byte = packet[0];
>+
>+      memset(report_data, 0, sizeof(struct cytp_report_data));
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              report_data->contact_cnt = cypress_get_finger_count(header_byte);
>+
>+              if (report_data->contact_cnt > CYTP_MAX_CONTACTS) {
>+                      /* report invalid data as zero package except the button data. */
>+                      report_data->contact_cnt = 0;
>+                      cytp_dbg("cypress_parse_packet: received invalid packet.\n");
>+              }
>+
>+              report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
>+
>+              /* Remove HSCROLL bit */
>+              if (report_data->contact_cnt == 4)
>+                      header_byte &= ~(ABS_HSCROLL_BIT);
>+
>+              if (report_data->contact_cnt == 1) {
>+                      report_data->contacts[0].x =
>+                              ((packet[1] & 0x70) << 4) | packet[2];
>+                      report_data->contacts[0].y =
>+                              ((packet[1] & 0x07) << 8) | packet[3];
>+                      if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>+                              report_data->contacts[0].z = packet[4];
>+
>+                      if ((packet[1] & ABS_EDGE_MOTION_MASK) != ABS_EDGE_MOTION_MASK) {
>+                              report_data->vscroll = (header_byte & ABS_VSCROLL_BIT) ? 1 : 0;
>+                              report_data->hscroll = (header_byte & ABS_HSCROLL_BIT) ? 1 : 0;
>+                      }
>+
>+              } else if (report_data->contact_cnt >= 2) {
>+                      report_data->contacts[0].x =
>+                              ((packet[1] & 0x70) << 4) | packet[2];
>+                      report_data->contacts[0].y =
>+                              ((packet[1] & 0x07) << 8) | packet[3];
>+                      if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>+                              report_data->contacts[0].z = packet[4];
>+
>+                      report_data->contacts[1].x =
>+                              ((packet[5] & 0xf0) << 4) | packet[6];
>+                      report_data->contacts[1].y =
>+                              ((packet[5] & 0x0f) << 8) | packet[7];
>+                      if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>+                              report_data->contacts[1].z = report_data->contacts[0].z;
>+              }
>+
>+              report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
>+              report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
>+
>+      } else {
>+              report_data->contact_cnt = 1;
>+              report_data->contacts[0].x =
>+                      (packet[0] & REL_X_SIGN_BIT) ? -packet[1] : packet[1];
>+              report_data->contacts[0].y =
>+                      (packet[0] & REL_Y_SIGN_BIT) ? -packet[2] : packet[2];
>+              report_data->vscroll = packet[3];
>+              report_data->left = (packet[0] & BTN_LEFT_BIT) ? 1 : 0;
>+              report_data->right = (packet[0] & BTN_RIGHT_BIT) ? 1 : 0;
>+
>+              if (cytp->mode & CYTP_BIT_STANDARD_REL)
>+                      report_data->middle =
>+                              (packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
>+              if (cytp->mode & CYTP_BIT_CYPRESS_REL) {
>+                      report_data->left =
>+                              (packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
>+                      report_data->hscroll = packet[4];
>+              }
>+      }
>+
>+      /* This is only true if one of the mouse buttons were tapped.
>+       * Make sure it doesn't turn into a click. The regular tap-to-
>+       * click functionality will handle that on its own. If we don't
>+       * do this, disabling tap-to-click won't affect the mouse button
>+       * zones. */
>+      if (report_data->tap)
>+              report_data->left = 0;
>+
>+      if (report_data->contact_cnt <= 0)
>+              return 0;
>+
>+      cytp_dbg_dump("cypress_parse_packet cytp->zero_packet_cnt = %d\n", cytp->zero_packet_cnt);
>+      cytp_dbg_dump("Dump parsed report data as below:\n");
>+      cytp_dbg_dump("contact_cnt = %d\n", report_data->contact_cnt);
>+      for (i = 0; i < report_data->contact_cnt; i++) {
>+              cytp_dbg_dump("contacts[%d].x = %d\n", i, report_data->contacts[i].x);
>+              cytp_dbg_dump("contacts[%d].y = %d\n", i, report_data->contacts[i].y);
>+              cytp_dbg_dump("contacts[%d].z = %d\n", i, report_data->contacts[i].z);
>+      }
>+      cytp_dbg_dump("vscroll = %d\n", report_data->vscroll);
>+      cytp_dbg_dump("hscroll = %d\n", report_data->hscroll);
>+      cytp_dbg_dump("left = %d\n", report_data->left);
>+      cytp_dbg_dump("right = %d\n", report_data->right);
>+      cytp_dbg_dump("middle = %d\n", report_data->middle);
>+
>+      return 0;
>+}
>+
>+static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
>+{
>+      int i;
>+      struct input_dev *input = psmouse->dev;
>+      struct cytp_data *cytp = psmouse->private;
>+      struct cytp_report_data report_data;
>+      struct cytp_contact *contact;
>+      int slot;
>+
>+      if (cypress_parse_packet(psmouse, cytp, &report_data))
>+              return;
>+
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              struct input_mt_pos pos[CYTP_MAX_CONTACTS];
>+              int slots[CYTP_MAX_MT_SLOTS];
>+              int n = report_data.contact_cnt;
>+              int ret;
>+
>+              if (n > CYTP_MAX_MT_SLOTS)
>+                      n = CYTP_MAX_MT_SLOTS;
>+
>+              for (i = 0; i < n; i++) {
>+                      contact = &report_data.contacts[i];
>+                      pos[i].x = contact->x;
>+                      pos[i].y = contact->y;
>+              }
>+
>+              ret = input_mt_assign_slots(input, slots, pos, n);
>+              if (ret < 0) {
>+                      psmouse_err(psmouse,
>+                          "input_mt_assign_slots(%d) returned %d\n", n, ret);
>+                      n = 0;
>+              }
>+
>+              for (i = 0; i < n; i++) {
>+                      contact = &report_data.contacts[i];
>+                      slot = slots[i];
>+                      input_mt_slot(input, slot);
>+                      input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
>+                      input_report_abs(input, ABS_MT_POSITION_X, contact->x);
>+                      input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
>+                      input_report_abs(input, ABS_MT_PRESSURE, contact->z);
>+              }
>+
>+              input_mt_sync_frame(input);
>+
>+      } else {
>+              if (report_data.contact_cnt == 1) {
>+                      input_report_rel(input, REL_X, report_data.contacts[0].x);
>+                      input_report_rel(input, REL_Y, report_data.contacts[0].y);
>+              }
>+
>+              input_report_rel(input, REL_WHEEL, report_data.vscroll);
>+              if (cytp->mode & CYTP_BIT_CYPRESS_REL)
>+                      input_report_rel(input, REL_HWHEEL, report_data.hscroll);
>+      }
>+
>+      input_report_key(input, BTN_LEFT, report_data.left);
>+      input_report_key(input, BTN_RIGHT, report_data.right);
>+      input_report_key(input, BTN_MIDDLE, report_data.middle);
>+
>+      input_sync(input);
>+}
>+
>+static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
>+{
>+      int contact_cnt;
>+      int index = psmouse->pktcnt - 1;
>+      unsigned char *packet = psmouse->packet;
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (index < 0 || index > cytp->pkt_size)
>+              return PSMOUSE_BAD_DATA;
>+
>+      if ((index == 0) && ((packet[0] & 0xfc) == 0)) {
>+              /* call packet process for reporting finger leave. */
>+              cypress_process_packet(psmouse, 1);
>+              return PSMOUSE_FULL_PACKET;
>+      }
>+
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              if (index == 0) {
>+                      if ((packet[0] & 0x08) == 0x08)
>+                              return PSMOUSE_BAD_DATA;
>+
>+                      contact_cnt = cypress_get_finger_count(packet[0]);
>+
>+                      if (contact_cnt > 5)
>+                              return PSMOUSE_BAD_DATA;
>+
>+                      if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) {
>+                              CYTP_SET_PACKET_SIZE(4);
>+                              if (contact_cnt == 2)
>+                                      CYTP_SET_PACKET_SIZE(7);
>+                      } else {
>+                              CYTP_SET_PACKET_SIZE(5);
>+                              if (contact_cnt == 2)
>+                                      CYTP_SET_PACKET_SIZE(8);
>+                      }
>+              }
>+
>+              return PSMOUSE_GOOD_DATA;
>+      } else {
>+              if (index == 0) {
>+                      if ((packet[0] & 0x08) != 0x08)
>+                              return PSMOUSE_BAD_DATA;
>+
>+                      CYTP_SET_PACKET_SIZE(3);
>+                      if (cytp->mode & CYTP_BIT_CYPRESS_REL)
>+                              CYTP_SET_PACKET_SIZE(5);
>+              }
>+
>+              return PSMOUSE_GOOD_DATA;
>+      }
>+}
>+
>+static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (psmouse->pktcnt >= cytp->pkt_size) {
>+              cypress_process_packet(psmouse, 0);
>+              return PSMOUSE_FULL_PACKET;
>+      }
>+
>+      return cypress_validate_byte(psmouse);
>+}
>+
>+static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (rate >= 80) {
>+              psmouse->rate = 80;
>+              CYTP_SET_MODE_BIT(CYTP_BIT_HIGH_RATE);
>+      } else {
>+              psmouse->rate = 40;
>+              CYTP_CLEAR_MODE_BIT(CYTP_BIT_HIGH_RATE);
>+      }
>+
>+      ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
>+                  PSMOUSE_CMD_SETRATE);
>+}
>+
>+static void cypress_disconnect(struct psmouse *psmouse)
>+{
>+      cypress_reset(psmouse);
>+      kfree(psmouse->private);
>+      psmouse->private = NULL;
>+}
>+
>+#if 0
>+/*
>+ * FIXME: cypress_reconnect() never works...
>+ * cypress_detect() always fails here.
>+ */
>+static int cypress_reconnect(struct psmouse *psmouse)
>+{
>+      int tries = CYTP_PS2_CMD_TRIES;
>+      int rc;
>+
>+      do {
>+              cypress_reset(psmouse);
>+              rc = cypress_detect(psmouse, false);
>+      } while (rc && (--tries > 0));
>+
>+      if (rc) {
>+              psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
>+              return -1;
>+      }
>+
>+      if (cypress_query_hardware(psmouse)) {
>+              psmouse_err(psmouse, "Reconnect: unable to query Trackpad hardware.\n");
>+              return -1;
>+      }
>+
>+      if (cypress_set_absolute_mode(psmouse)) {
>+              psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
>+              return -1;
>+      }
>+
>+      return 0;
>+}
>+#endif

Could you help describe the test steps that you see it never works, so I can help fix this issue. Thanks.

>+
>+int cypress_init(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp;
>+
>+      cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
>+      psmouse->private = (void *)cytp;
>+      if (cytp == NULL)
>+              return -ENOMEM;
>+
>+      cypress_reset(psmouse);
>+
>+      if (cypress_query_hardware(psmouse)) {
>+              psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
>+              goto err_exit;
>+      }
>+
>+      if (cypress_set_absolute_mode(psmouse)) {
>+              psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
>+              goto err_exit;
>+      }
>+
>+      if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
>+              psmouse_err(psmouse, "init: Unable to set input params.\n");
>+              goto err_exit;
>+      }
>+
>+      psmouse->model = 1;
>+      psmouse->protocol_handler = cypress_protocol_handler;
>+      psmouse->set_rate = cypress_set_rate;
>+      psmouse->disconnect = cypress_disconnect;
>+#if 0
>+      /* FIXME: cypress_reconnect() never works...
>+       * just let psmouse re-init() us for now.
>+       */
>+      psmouse->reconnect = cypress_reconnect;
>+#endif
>+      psmouse->cleanup = cypress_reset;
>+      psmouse->pktsize = 8;
>+      psmouse->resync_time = 0;
>+
>+      return 0;
>+
>+err_exit:
>+      /*
>+       * Reset Cypress Trackpad as a standard mouse. Then
>+       * let psmouse driver commmunicating with it as default PS2 mouse.
>+       */
>+      cypress_reset(psmouse);
>+
>+      psmouse->private = NULL;
>+      kfree(cytp);
>+
>+      return -1;
>+}
>+
>+bool cypress_supported(void)
>+{
>+      return true;
>+}
>diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
>new file mode 100644
>index 0000000..ce70462
>--- /dev/null
>+++ b/drivers/input/mouse/cypress_ps2.h
>@@ -0,0 +1,219 @@
>+#ifndef _CYPRESS_PS2_H
>+#define _CYPRESS_PS2_H
>+
>+#include "psmouse.h"
>+
>+#define CMD_BITS_MASK 0x03
>+#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
>+
>+#define ENCODE_CMD(aa, bb, cc, dd) \
>+      (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
>+#define CYTP_CMD_ABS_NO_PRESSURE_MODE       ENCODE_CMD(0, 1, 0, 0)
>+#define CYTP_CMD_ABS_WITH_PRESSURE_MODE     ENCODE_CMD(0, 1, 0, 1)
>+#define CYTP_CMD_SMBUS_MODE                 ENCODE_CMD(0, 1, 1, 0)
>+#define CYTP_CMD_STANDARD_MODE              ENCODE_CMD(0, 2, 0, 0)  /* not implemented yet. */
>+#define CYTP_CMD_CYPRESS_REL_MODE           ENCODE_CMD(1, 1, 1, 1)  /* not implemented yet. */
>+#define CYTP_CMD_READ_CYPRESS_ID            ENCODE_CMD(0, 0, 0, 0)
>+#define CYTP_CMD_READ_VITAL_STATISTICS      ENCODE_CMD(0, 0, 0, 1)
>+#define CYTP_CMD_SET_HSCROLL_WIDTH(w)       ENCODE_CMD(1, 1, 0, (w))
>+#define     CYTP_CMD_SET_HSCROLL_MASK       ENCODE_CMD(1, 1, 0, 0)
>+#define CYTP_CMD_SET_VSCROLL_WIDTH(w)       ENCODE_CMD(1, 2, 0, (w))
>+#define     CYTP_CMD_SET_VSCROLL_MASK       ENCODE_CMD(1, 2, 0, 0)
>+#define CYTP_CMD_SET_PALM_GEOMETRY(e)       ENCODE_CMD(1, 2, 1, (e))
>+#define     CYTP_CMD_PALM_GEMMETRY_MASK     ENCODE_CMD(1, 2, 1, 0)
>+#define CYTP_CMD_SET_PALM_SENSITIVITY(s)    ENCODE_CMD(1, 2, 2, (s))
>+#define     CYTP_CMD_PALM_SENSITIVITY_MASK  ENCODE_CMD(1, 2, 2, 0)
>+#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s)   ENCODE_CMD(1, 3, ((s) >> 2), (s))
>+#define     CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
>+#define CYTP_CMD_REQUEST_BASELINE_STATUS    ENCODE_CMD(2, 0, 0, 1)
>+#define CYTP_CMD_REQUEST_RECALIBRATION      ENCODE_CMD(2, 0, 0, 3)
>+
>+#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
>+#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
>+#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
>+#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
>+
>+/* Cypress trackpad working mode. */
>+#define CYTP_BIT_ABS_PRESSURE    (1 << 3)
>+#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
>+#define CYTP_BIT_CYPRESS_REL     (1 << 1)
>+#define CYTP_BIT_STANDARD_REL    (1 << 0)
>+#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
>+#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
>+#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
>+
>+#define CYTP_BIT_HIGH_RATE       (1 << 4)
>+/*
>+ * report mode bit is set, firmware working in Remote Mode.
>+ * report mode bit is cleared, firmware working in Stream Mode.
>+ */
>+#define CYTP_BIT_REPORT_MODE     (1 << 5)
>+
>+/* scrolling width values for set HSCROLL and VSCROLL width command. */
>+#define SCROLL_WIDTH_NARROW 1
>+#define SCROLL_WIDTH_NORMAL 2
>+#define SCROLL_WIDTH_WIDE   3
>+
>+#define PALM_GEOMETRY_ENABLE  1
>+#define PALM_GEOMETRY_DISABLE 0
>+
>+#define CYPRESS_KEY_1 0x33
>+#define CYPRESS_KEY_2 0xCC
>+
>+#define VITAL_STATICS_MASK 0x80
>+#define FW_VERSION_MASX    0x7f
>+#define FW_VER_HIGH_MASK 0x70
>+#define FW_VER_LOW_MASK  0x0f
>+
>+/* Times to retry a ps2_command and millisecond delay between tries. */
>+#define CYTP_PS2_CMD_TRIES 3
>+#define CYTP_PS2_CMD_DELAY 500
>+
>+/* time out for PS/2 command only in milliseconds. */
>+#define CYTP_CMD_TIMEOUT  200
>+#define CYTP_DATA_TIMEOUT 30
>+
>+#define CYTP_EXT_CMD   0xe8
>+#define CYTP_PS2_RETRY 0xfe
>+#define CYTP_PS2_ERROR 0xfc
>+
>+#define CYTP_RESP_RETRY 0x01
>+#define CYTP_RESP_ERROR 0xfe
>+
>+
>+#define CYTP_105001_WIDTH  97   /* Dell XPS 13 */
>+#define CYTP_105001_HIGH   59
>+#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
>+#define CYTP_DEFAULT_HIGH  (CYTP_105001_HIGH)
>+
>+#define CYTP_ABS_MAX_X     1600
>+#define CYTP_ABS_MAX_Y     900
>+#define CYTP_MAX_PRESSURE  255
>+#define CYTP_MIN_PRESSURE  0
>+
>+/* header byte bits of relative package. */
>+#define BTN_LEFT_BIT   0x01
>+#define BTN_RIGHT_BIT  0x02
>+#define BTN_MIDDLE_BIT 0x04
>+#define REL_X_SIGN_BIT 0x10
>+#define REL_Y_SIGN_BIT 0x20
>+
>+/* header byte bits of absolute package. */
>+#define ABS_VSCROLL_BIT 0x10
>+#define ABS_HSCROLL_BIT 0x20
>+#define ABS_MULTIFINGER_TAP 0x04
>+#define ABS_EDGE_MOTION_MASK 0x80
>+
>+#define DFLT_RESP_BITS_VALID     0x88  /* SMBus bit should not be set. */
>+#define DFLT_RESP_SMBUS_BIT      0x80
>+#define   DFLT_SMBUS_MODE        0x80
>+#define   DFLT_PS2_MODE          0x00
>+#define DFLT_RESP_BIT_MODE       0x40
>+#define   DFLT_RESP_REMOTE_MODE  0x40
>+#define   DFLT_RESP_STREAM_MODE  0x00
>+#define DFLT_RESP_BIT_REPORTING  0x20
>+#define DFLT_RESP_BIT_SCALING    0x10
>+
>+#define VITAL_BIT_PALM               0x80
>+#define VITAL_BIT_STUBBORN           0x40
>+#define VITAL_BIT_2F_JITTER          0x30
>+#define VITAL_BIT_1F_JITTER          0x0c
>+#define VITAL_BIT_APA                0x02
>+#define VITAL_BIT_MTG                0x01
>+#define VITAL_BIT_ABS_PKT_FORMAT_SET 0xf0
>+#define VITAL_BIT_2F_SPIKE           0x0c
>+#define VITAL_BIT_1F_SPIKE           0x03
>+
>+/* bits of first byte response of E9h-Status Request command. */
>+#define RESP_BTN_RIGHT_BIT  0x01
>+#define RESP_BTN_MIDDLE_BIT 0x02
>+#define RESP_BTN_LEFT_BIT   0x04
>+#define RESP_SCALING_BIT    0x10
>+#define RESP_ENABLE_BIT     0x20
>+#define RESP_REMOTE_BIT     0x40
>+#define RESP_SMBUS_BIT      0x80
>+
>+#define CYTP_MAX_CONTACTS 5
>+#define CYTP_MAX_MT_SLOTS 2
>+
>+enum cytp_type {
>+      CYTP_STG,
>+      CYTP_MTG,
>+      CYTP_APA,
>+};
>+
>+struct cytp_contact {
>+      int x;
>+      int y;
>+      int z;  /* also named as touch pressure. */
>+};
>+
>+/* The structure of */
>+struct cytp_report_data {
>+      int contact_cnt;
>+      struct cytp_contact contacts[CYTP_MAX_CONTACTS];
>+      unsigned int left:1;
>+      unsigned int right:1;
>+      unsigned int middle:1;
>+      unsigned int tap:1;  /* multi-finger tap detected. */
>+      signed char vscroll;
>+      signed char hscroll;
>+};
>+
>+/* The structure of Cypress Trackpad device private data. */
>+struct cytp_data {
>+      int fw_version;
>+
>+      int pkt_size;
>+      int mode;
>+
>+      int scaling;
>+      int reporting;
>+
>+      int tp_min_pressure;
>+      int tp_max_pressure;
>+      int tp_width;  /* X direction physical size in mm. */
>+      int tp_high;  /* Y direction physical size in mm. */
>+      int tp_max_abs_x;  /* Max X absolution units can be reported. */
>+      int tp_max_abs_y;  /* Max Y absolution units can be reported. */
>+
>+      int tp_res_x;  /* X resolution in units/mm. */
>+      int tp_res_y;  /* Y resolution in units/mm. */
>+
>+      enum cytp_type tp_type;
>+      unsigned char tp_palm;
>+      unsigned char tp_stubborn;
>+      unsigned char tp_2f_jitter;
>+      unsigned char tp_1f_jitter;
>+      unsigned char tp_abs_packet_format_set;
>+      unsigned char tp_2f_spike;
>+      unsigned char tp_1f_spike;
>+
>+      int vital_statics_supported;
>+
>+      int prev_contact_cnt;
>+      int zero_packet_cnt;
>+      struct cytp_report_data prev_report_data;
>+};
>+
>+
>+#ifdef CONFIG_MOUSE_PS2_CYPRESS
>+int cypress_detect(struct psmouse *psmouse, bool set_properties);
>+int cypress_init(struct psmouse *psmouse);
>+bool cypress_supported(void);
>+#else
>+inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
>+{
>+      return -ENOSYS;
>+}
>+inline int cypress_init(struct psmouse *psmouse)
>+{
>+      return -ENOSYS;
>+}
>+inline bool cypress_supported(void)
>+{
>+      return 0;
>+}
>+#endif /* CONFIG_MOUSE_PS2_CYPRESS */
>+
>+#endif  /* _CYPRESS_PS2_H */
>--
>1.7.10.4


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-11-29 21:57 ` [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver Kamal Mostafa
  2012-12-03  3:20   ` Dudley Du
@ 2012-12-03  5:57   ` Dudley Du
  2012-12-03  6:31     ` Henrik Rydberg
  2012-12-03  7:45   ` Henrik Rydberg
  2 siblings, 1 reply; 17+ messages in thread
From: Dudley Du @ 2012-12-03  5:57 UTC (permalink / raw)
  To: Kamal Mostafa, linux-input, linux-kernel, Dmitry Torokhov,
	Henrik Rydberg
  Cc: David Solda, Troy Abercrombia, customercare, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski, dudl

Hi Kamal,

Another question is that new interface input_mt_assign_slots() is introduced since 3.7-rc7 or later,
So we could not support the kernel releases that earlier than 3.7-rc7 with this driver.

So my idea is that in order to keep compatible with earlier kernel release,
Let's use our old method to calculate the slots, not using latest interface input_mt_assign_slots() here.

What's your idea about it?

Thanks.

Best Wishes,
Dudley Du
dudl@cypress.com


-----Original Message-----
From: Dudley Du
Sent: Monday, December 03, 2012 11:21 AM
To: 'Kamal Mostafa'; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Dmitry Torokhov; Henrik Rydberg
Cc: David Solda; Troy Abercrombia; customercare; Kyle Fazzari; Mario Limonciello; Tim Gardner; Herton Krzesinski; 'dudl@cyress.com'
Subject: RE: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver

Hi Kamal,

I'm the writer of this driver.
Thanks for help upstream this driver.

I noticed that you marked out the reconnect function for it never work at all.
But in my test, it seems work fine.
I do the test steps as below:
1. load this driver with Cypress trackpad. Cursor moving on system; 2. directly unplug the trackpad from system; 3. reconnect the trackpad device to PC again; 4. moving finger on trackpad to check cursor still working on the system.
And also, I checked the debug message through dmesg, it makes sure that the reconnection function is called, and also the driver and trackpad still working fine.

So I want to know what situation is in your test to this function, could you help describe your test steps to me, so we can figure out this issue.

Thanks.

Best Wishes,
Dudley Du
dudl@cyrpess.com



>From: Cypress Semiconductor Corporation <customercare@cypress.com>
>
>Input/mouse driver for Cypress PS/2 Trackpad.
>
>Original code contributed by Cypress Semiconductor Corporation,
>modified by Kamal Mostafa and Kyle Fazzari.
>
>BugLink: http://launchpad.net/bugs/978807
>
>Signed-off-by: Kamal Mostafa <kamal@canonical.com>
>Signed-off-by: Kyle Fazzari <git@status.e4ward.com>
>Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
>Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
>Acked-by: Herton Krzesinski <herton.krzesinski@canonical.com>
>---
> drivers/input/mouse/cypress_ps2.c |  830
>+++++++++++++++++++++++++++++++++++++
> drivers/input/mouse/cypress_ps2.h |  219 ++++++++++
> 2 files changed, 1049 insertions(+)
> create mode 100644 drivers/input/mouse/cypress_ps2.c  create mode
>100644 drivers/input/mouse/cypress_ps2.h
>
>diff --git a/drivers/input/mouse/cypress_ps2.c
>b/drivers/input/mouse/cypress_ps2.c
>new file mode 100644
>index 0000000..472342a
>--- /dev/null
>+++ b/drivers/input/mouse/cypress_ps2.c
>@@ -0,0 +1,830 @@
>+/*
>+ * Cypress Trackpad PS/2 mouse driver
>+ *
>+ * Copyright (c) 2012 Cypress Semiconductor Corporation.
>+ *
>+ * Additional contributors include:
>+ *   Kamal Mostafa <kamal@canonical.com>
>+ *   Kyle Fazzari <git@status.e4ward.com>
>+ *
>+ * This program is free software; you can redistribute it and/or
>+modify it
>+ * under the terms of the GNU General Public License version 2 as
>+published by
>+ * the Free Software Foundation.
>+ */
>+
>+#include <linux/init.h>
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <linux/slab.h>
>+#include <linux/serio.h>
>+#include <linux/libps2.h>
>+#include <linux/input.h>
>+#include <linux/input/mt.h>
>+#include <linux/sched.h>
>+#include <linux/wait.h>
>+
>+#include "cypress_ps2.h"
>+
>+#define CYTP_DBG_DUMP 0               /* set to 1 for more verbose debug dump */
>+
>+#define cytp_dbg(fmt, ...)  \
>+      do {  \
>+              if (cytp)  \
>+                      psmouse_dbg(psmouse, pr_fmt(fmt), ##__VA_ARGS__);  \
>+      } while (0)
>+
>+#if CYTP_DBG_DUMP
>+# define cytp_dbg_dump cytp_dbg
>+#else
>+# define cytp_dbg_dump(fmt, ...)
>+#endif
>+
>+
>+/* p is a pointer points to the buffer containing Cypress Keys. */
>+#define IS_CYPRESS_KEY(p) ((p[0] == CYPRESS_KEY_1) && (p[1] ==
>+CYPRESS_KEY_2)) #define CYTP_SET_PACKET_SIZE(n) { psmouse->pktsize =
>+cytp->pkt_size = (n); } #define CYTP_SET_MODE_BIT(x)  \
>+      do {  \
>+              if ((x) & CYTP_BIT_ABS_REL_MASK)  \
>+                      cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK);  \
>+              cytp->mode |= (x);  \
>+      } while (0)
>+#define CYTP_CLEAR_MODE_BIT(x)        { cytp->mode &= ~(x); }
>+
>+#define CYTP_SUPPORT_ABS
>+
>+static unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; static
>+unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
>+
>+static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) {
>+      struct cytp_data *cytp = psmouse->private;
>+      struct ps2dev *ps2dev = &psmouse->ps2dev;
>+
>+      if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
>+              cytp_dbg("send command 0x%02x failed, resp 0x%02x\n",
>+                       value & 0xff, ps2dev->nak);
>+              if (ps2dev->nak == CYTP_PS2_RETRY)
>+                      return CYTP_PS2_RETRY;
>+              else
>+                      return CYTP_PS2_ERROR;
>+      }
>+
>+      cytp_dbg("send command 0x%02x success, resp 0xfa\n", value & 0xff);
>+
>+      return 0;
>+}
>+
>+static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
>+                             unsigned char data)
>+{
>+      struct ps2dev *ps2dev = &psmouse->ps2dev;
>+      int tries = CYTP_PS2_CMD_TRIES;
>+      int rc;
>+
>+      ps2_begin_command(ps2dev);
>+
>+      do {
>+              /*
>+               * send extension command 0xE8 or 0xF3,
>+               * if send extension command failed,
>+               * try to send recovery command to make
>+               * trackpad device return to ready wait command state.
>+               * It alwasy success based on this recovery commands.
>+               */
>+              rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
>+              if (rc == CYTP_PS2_RETRY) {
>+                      rc = cypress_ps2_sendbyte(psmouse, 0x00);
>+                      if (rc == CYTP_PS2_RETRY)
>+                              rc = cypress_ps2_sendbyte(psmouse, 0x0a);
>+              }
>+              if (rc == CYTP_PS2_ERROR)
>+                      continue;
>+
>+              rc = cypress_ps2_sendbyte(psmouse, data);
>+              if (rc == CYTP_PS2_RETRY)
>+                      rc = cypress_ps2_sendbyte(psmouse, data);
>+              if (rc == CYTP_PS2_ERROR)
>+                      continue;
>+              else
>+                      break;
>+      } while (--tries > 0);
>+
>+      ps2_end_command(ps2dev);
>+
>+      return rc;
>+}
>+
>+static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
>+                                     unsigned char cmd,
>+                                     unsigned char *param)
>+{
>+      int i;
>+      int rc;
>+      struct ps2dev *ps2dev = &psmouse->ps2dev;
>+      struct cytp_data *cytp = psmouse->private;
>+      enum psmouse_state old_state;
>+      unsigned char old_pktsize;
>+
>+      ps2_begin_command(&psmouse->ps2dev);
>+
>+      old_state = psmouse->state;
>+      psmouse->state = PSMOUSE_CMD_MODE;
>+      psmouse->pktcnt = 0;
>+      old_pktsize = psmouse->pktsize;
>+      psmouse->pktsize = 3;
>+      if (cmd == CYTP_CMD_READ_VITAL_STATISTICS)
>+              psmouse->pktsize = 8;
>+      memset(param, 0, psmouse->pktsize);
>+
>+      rc = cypress_ps2_sendbyte(psmouse, 0xe9);
>+      if (rc < 0)
>+              goto out;
>+
>+      wait_event_timeout(ps2dev->wait,
>+                      (psmouse->pktcnt >= psmouse->pktsize),
>+                      msecs_to_jiffies(CYTP_CMD_TIMEOUT));
>+
>+      memcpy(param, psmouse->packet, psmouse->pktsize);
>+
>+      cytp_dbg("Command 0x%02x response data: (0x)", cmd);
>+      for (i = 0; i < psmouse->pktsize; i++)
>+              cytp_dbg(" %02x", param[i]);
>+      cytp_dbg("\n");
>+
>+out:
>+      psmouse->state = old_state;
>+      psmouse->pktcnt = 0;
>+      psmouse->pktsize = old_pktsize;
>+
>+      ps2_end_command(&psmouse->ps2dev);
>+
>+      return rc;
>+}
>+
>+static int cypress_verify_cmd_state(struct psmouse *psmouse,
>+                                  unsigned char cmd, unsigned char *param) {
>+      struct cytp_data *cytp = psmouse->private;
>+      bool rate_match = 0;
>+      bool resolution_match = 0;
>+      int i;
>+
>+      /* callers will do further checking. */
>+      if ((cmd == CYTP_CMD_READ_CYPRESS_ID) ||
>+          (cmd == CYTP_CMD_STANDARD_MODE) ||
>+          (cmd == CYTP_CMD_READ_VITAL_STATISTICS))
>+              return 0;
>+      if (((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID) &&
>+          ((param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE)) {
>+              for (i = 0; i < sizeof(cytp_resolution); i++)
>+                      if (cytp_resolution[i] == param[1])
>+                              resolution_match =  1;
>+
>+              for (i = 0; i < sizeof(cytp_rate); i++)
>+                      if (cytp_rate[i] == param[2])
>+                              rate_match = 1;
>+
>+              if (resolution_match && rate_match)
>+                      return 0;
>+      }
>+
>+      cytp_dbg("verify cmd state failed.\n");
>+      return -1;
>+}
>+
>+static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
>+                              unsigned char *param)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+      int tries = CYTP_PS2_CMD_TRIES;
>+      int rc;
>+
>+      cytp_dbg("send extension cmd 0x%02x, [%d %d %d %d]\n",
>+               cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
>+               DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
>+      do {
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
>+              cypress_ps2_ext_cmd(psmouse,
>+                                  PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
>+
>+              rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
>+              if ((rc == 0) &&
>+                  (cypress_verify_cmd_state(psmouse, cmd, param) == 0))
>+                      return 0;
>+      } while (--tries > 0);
>+
>+
>+      if (tries <= 0)
>+              return -1;
>+
>+      return 0;
>+
>+}
>+
>+int cypress_detect(struct psmouse *psmouse, bool set_properties) {
>+      unsigned char param[3];
>+
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
>+              return -1;
>+
>+      if (!IS_CYPRESS_KEY(param))
>+              return -ENODEV;
>+
>+      if (set_properties) {
>+              psmouse->vendor = "Cypress";
>+              psmouse->name = "Trackpad";
>+      }
>+
>+      return 0;
>+}
>+
>+static int cypress_read_fw_version(struct psmouse *psmouse) {
>+      struct cytp_data *cytp = psmouse->private;
>+      unsigned char param[3];
>+
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
>+              return -1;
>+
>+      if (!IS_CYPRESS_KEY(param))
>+              return -ENODEV;
>+
>+      cytp->fw_version = param[2] & FW_VERSION_MASX;
>+      cytp->vital_statics_supported = (param[2] & VITAL_STATICS_MASK) ? 1 :
>+0;
>+
>+      cytp_dbg("cytp->fw_version = %d\n", cytp->fw_version);
>+      cytp_dbg("cytp->vital_statics_supported = %d\n",
>+               cytp->vital_statics_supported);
>+      return 0;
>+}
>+
>+static int cypress_read_vital_statistics(struct psmouse *psmouse) {
>+      struct cytp_data *cytp = psmouse->private;
>+      unsigned char param[8];
>+
>+      /* set default values for vital statistics not supported trackpad. */
>+      cytp->tp_width = CYTP_DEFAULT_WIDTH;
>+      cytp->tp_high = CYTP_DEFAULT_HIGH;
>+      cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
>+      cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
>+      cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
>+      cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
>+      cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
>+      cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
>+
>+      memset(param, 0, sizeof(param));
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_VITAL_STATISTICS, param) == 0) {
>+              /* Update trackpad parameters. */
>+              cytp->tp_max_abs_x = (param[1] << 8) | param[0];
>+              cytp->tp_max_abs_y = (param[3] << 8) | param[2];
>+              cytp->tp_min_pressure = param[4];
>+              cytp->tp_max_pressure = param[5];
>+
>+              if (param[6] & VITAL_BIT_APA)
>+                      cytp->tp_type = CYTP_APA;
>+              else if (param[6] & VITAL_BIT_MTG)
>+                      cytp->tp_type = CYTP_MTG;
>+              else
>+                      cytp->tp_type = CYTP_STG;
>+              cytp->tp_palm = (param[6] & VITAL_BIT_PALM) ? 1 : 0;
>+              cytp->tp_stubborn = (param[6] & VITAL_BIT_STUBBORN) ? 1 : 0;
>+              cytp->tp_2f_jitter = (param[6] & VITAL_BIT_2F_JITTER) >> 4;
>+              cytp->tp_1f_jitter = (param[6] & VITAL_BIT_1F_JITTER) >> 2;
>+              cytp->tp_abs_packet_format_set =
>+                      (param[7] & VITAL_BIT_ABS_PKT_FORMAT_SET) >> 4;
>+              cytp->tp_2f_spike = (param[7] & VITAL_BIT_2F_SPIKE) >> 2;
>+              cytp->tp_1f_spike = (param[7] & VITAL_BIT_1F_SPIKE);
>+
>+      }
>+
>+      if (!cytp->tp_max_pressure ||
>+          (cytp->tp_max_pressure < cytp->tp_min_pressure) ||
>+          (!cytp->tp_width || !cytp->tp_high) ||
>+          (!cytp->tp_max_abs_x) ||
>+          (cytp->tp_max_abs_x < cytp->tp_width) ||
>+          (!cytp->tp_max_abs_y) ||
>+          (cytp->tp_max_abs_y < cytp->tp_high))
>+              return -1;
>+
>+      cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
>+      cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
>+
>+      cytp_dbg_dump("Dump trackpad hardware configuration as below:\n");
>+      cytp_dbg_dump("cytp->tp_width = %d\n", cytp->tp_width);
>+      cytp_dbg_dump("cytp->tp_high = %d\n", cytp->tp_high);
>+      cytp_dbg_dump("cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
>+      cytp_dbg_dump("cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
>+      cytp_dbg_dump("cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
>+      cytp_dbg_dump("cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
>+      cytp_dbg_dump("cytp->tp_res_x = %d\n", cytp->tp_res_x);
>+      cytp_dbg_dump("cytp->tp_res_y = %d\n", cytp->tp_res_y);
>+      cytp_dbg_dump("cytp->tp_type = %d\n", cytp->tp_type);
>+      cytp_dbg_dump("cytp->tp_palm = %d\n", cytp->tp_palm);
>+      cytp_dbg_dump("cytp->tp_stubborn = %d\n", cytp->tp_stubborn);
>+      cytp_dbg_dump("cytp->tp_1f_jitter = %d\n", cytp->tp_1f_jitter);
>+      cytp_dbg_dump("cytp->tp_2f_jitter = %d\n", cytp->tp_2f_jitter);
>+      cytp_dbg_dump("cytp->tp_1f_spike = %d\n", cytp->tp_1f_spike);
>+      cytp_dbg_dump("cytp->tp_2f_spike = %d\n", cytp->tp_2f_spike);
>+      cytp_dbg_dump("cytp->tp_abs_packet_format_set = %d\n",
>+                    cytp->tp_abs_packet_format_set);
>+
>+      return 0;
>+}
>+
>+static int cypress_query_hardware(struct psmouse *psmouse) {
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (cypress_read_fw_version(psmouse))
>+              return -1;
>+
>+      if (cytp->vital_statics_supported) {
>+              if (cypress_read_vital_statistics(psmouse))
>+                      return -1;
>+      }
>+
>+      return 0;
>+}
>+
>+static int cypress_set_absolute_mode(struct psmouse *psmouse) {
>+      struct cytp_data *cytp = psmouse->private;
>+      unsigned char param[3];
>+
>+      if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
>+              return -1;
>+
>+      CYTP_SET_MODE_BIT(CYTP_BIT_ABS_PRESSURE);
>+      CYTP_SET_PACKET_SIZE(5);
>+
>+      return 0;
>+}
>+
>+/*
>+ * reset trackpad device to standard relative mode.
>+ * This is also the defalut mode when trackpad powered on.
>+ */
>+static void cypress_reset(struct psmouse *psmouse) {
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      psmouse_reset(psmouse);
>+
>+      CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
>+      CYTP_SET_PACKET_SIZE(3);
>+
>+      cytp->prev_contact_cnt = 0;
>+}
>+
>+static int cypress_set_input_params(struct input_dev *input,
>+                                  struct cytp_data *cytp)
>+{
>+      int ret;
>+
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              __set_bit(EV_ABS, input->evbit);
>+              input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
>+              input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
>+              input_set_abs_params(input, ABS_PRESSURE,
>+                                   cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
>+              input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
>+
>+              /* finger position */
>+              input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
>+              input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
>+              input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
>+
>+              ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
>+                      INPUT_MT_POINTER|INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
>+              if (ret < 0) {
>+                      return ret;
>+              }
>+
>+              __set_bit(INPUT_PROP_SEMI_MT, input->propbit);
>+
>+              if (cytp->tp_res_x && cytp->tp_res_x) {
>+                      input_abs_set_res(input, ABS_X, cytp->tp_res_x);
>+                      input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
>+
>+                      input_abs_set_res(input, ABS_MT_POSITION_X,
>+                                        cytp->tp_res_x);
>+                      input_abs_set_res(input, ABS_MT_POSITION_Y,
>+                                        cytp->tp_res_y);
>+
>+              }
>+
>+              __set_bit(BTN_TOUCH, input->keybit);
>+              __set_bit(BTN_TOOL_FINGER, input->keybit);
>+              __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
>+              __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
>+              __set_bit(BTN_TOOL_QUADTAP, input->keybit);
>+              __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
>+
>+              __clear_bit(EV_REL, input->evbit);
>+              __clear_bit(REL_X, input->relbit);
>+              __clear_bit(REL_Y, input->relbit);
>+      } else {
>+              __set_bit(EV_REL, input->evbit);
>+              __set_bit(REL_X, input->relbit);
>+              __set_bit(REL_Y, input->relbit);
>+              __set_bit(REL_WHEEL, input->relbit);
>+              __set_bit(REL_HWHEEL, input->relbit);
>+
>+              __clear_bit(EV_ABS, input->evbit);
>+      }
>+
>+      __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
>+      __set_bit(EV_KEY, input->evbit);
>+      __set_bit(BTN_LEFT, input->keybit);
>+      __set_bit(BTN_RIGHT, input->keybit);
>+      __set_bit(BTN_MIDDLE, input->keybit);
>+
>+      input_set_drvdata(input, cytp);
>+
>+      return 0;
>+}
>+
>+static int cypress_get_finger_count(unsigned char header_byte) {
>+      unsigned char bits6_7;
>+      int finger_count;
>+
>+      bits6_7 = header_byte >> 6;
>+      finger_count = bits6_7 & 0x03;
>+
>+      if (finger_count != 1) {
>+              if (header_byte & ABS_HSCROLL_BIT) {
>+                      if (finger_count == 0) {
>+                              /* HSCROLL gets added on to 0 finger count. */
>+                              finger_count = 4;
>+                              /* should remove HSCROLL bit. */
>+                      } else {
>+                              if (finger_count == 2) {
>+                                      finger_count = 5;
>+                              } else {
>+                                      /* Invalid contact (e.g. palm). Ignore it. */
>+                                      finger_count = 0;
>+                              }
>+                      }
>+              }
>+      }
>+
>+      return finger_count;
>+}
>+
>+
>+static int cypress_parse_packet(struct psmouse *psmouse,
>+                              struct cytp_data *cytp, struct cytp_report_data *report_data) {
>+      int i;
>+      unsigned char *packet = psmouse->packet;
>+      unsigned char header_byte = packet[0];
>+
>+      memset(report_data, 0, sizeof(struct cytp_report_data));
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              report_data->contact_cnt = cypress_get_finger_count(header_byte);
>+
>+              if (report_data->contact_cnt > CYTP_MAX_CONTACTS) {
>+                      /* report invalid data as zero package except the button data. */
>+                      report_data->contact_cnt = 0;
>+                      cytp_dbg("cypress_parse_packet: received invalid packet.\n");
>+              }
>+
>+              report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
>+
>+              /* Remove HSCROLL bit */
>+              if (report_data->contact_cnt == 4)
>+                      header_byte &= ~(ABS_HSCROLL_BIT);
>+
>+              if (report_data->contact_cnt == 1) {
>+                      report_data->contacts[0].x =
>+                              ((packet[1] & 0x70) << 4) | packet[2];
>+                      report_data->contacts[0].y =
>+                              ((packet[1] & 0x07) << 8) | packet[3];
>+                      if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>+                              report_data->contacts[0].z = packet[4];
>+
>+                      if ((packet[1] & ABS_EDGE_MOTION_MASK) != ABS_EDGE_MOTION_MASK) {
>+                              report_data->vscroll = (header_byte & ABS_VSCROLL_BIT) ? 1 : 0;
>+                              report_data->hscroll = (header_byte & ABS_HSCROLL_BIT) ? 1 : 0;
>+                      }
>+
>+              } else if (report_data->contact_cnt >= 2) {
>+                      report_data->contacts[0].x =
>+                              ((packet[1] & 0x70) << 4) | packet[2];
>+                      report_data->contacts[0].y =
>+                              ((packet[1] & 0x07) << 8) | packet[3];
>+                      if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>+                              report_data->contacts[0].z = packet[4];
>+
>+                      report_data->contacts[1].x =
>+                              ((packet[5] & 0xf0) << 4) | packet[6];
>+                      report_data->contacts[1].y =
>+                              ((packet[5] & 0x0f) << 8) | packet[7];
>+                      if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
>+                              report_data->contacts[1].z = report_data->contacts[0].z;
>+              }
>+
>+              report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
>+              report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
>+
>+      } else {
>+              report_data->contact_cnt = 1;
>+              report_data->contacts[0].x =
>+                      (packet[0] & REL_X_SIGN_BIT) ? -packet[1] : packet[1];
>+              report_data->contacts[0].y =
>+                      (packet[0] & REL_Y_SIGN_BIT) ? -packet[2] : packet[2];
>+              report_data->vscroll = packet[3];
>+              report_data->left = (packet[0] & BTN_LEFT_BIT) ? 1 : 0;
>+              report_data->right = (packet[0] & BTN_RIGHT_BIT) ? 1 : 0;
>+
>+              if (cytp->mode & CYTP_BIT_STANDARD_REL)
>+                      report_data->middle =
>+                              (packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
>+              if (cytp->mode & CYTP_BIT_CYPRESS_REL) {
>+                      report_data->left =
>+                              (packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
>+                      report_data->hscroll = packet[4];
>+              }
>+      }
>+
>+      /* This is only true if one of the mouse buttons were tapped.
>+       * Make sure it doesn't turn into a click. The regular tap-to-
>+       * click functionality will handle that on its own. If we don't
>+       * do this, disabling tap-to-click won't affect the mouse button
>+       * zones. */
>+      if (report_data->tap)
>+              report_data->left = 0;
>+
>+      if (report_data->contact_cnt <= 0)
>+              return 0;
>+
>+      cytp_dbg_dump("cypress_parse_packet cytp->zero_packet_cnt = %d\n", cytp->zero_packet_cnt);
>+      cytp_dbg_dump("Dump parsed report data as below:\n");
>+      cytp_dbg_dump("contact_cnt = %d\n", report_data->contact_cnt);
>+      for (i = 0; i < report_data->contact_cnt; i++) {
>+              cytp_dbg_dump("contacts[%d].x = %d\n", i, report_data->contacts[i].x);
>+              cytp_dbg_dump("contacts[%d].y = %d\n", i, report_data->contacts[i].y);
>+              cytp_dbg_dump("contacts[%d].z = %d\n", i, report_data->contacts[i].z);
>+      }
>+      cytp_dbg_dump("vscroll = %d\n", report_data->vscroll);
>+      cytp_dbg_dump("hscroll = %d\n", report_data->hscroll);
>+      cytp_dbg_dump("left = %d\n", report_data->left);
>+      cytp_dbg_dump("right = %d\n", report_data->right);
>+      cytp_dbg_dump("middle = %d\n", report_data->middle);
>+
>+      return 0;
>+}
>+
>+static void cypress_process_packet(struct psmouse *psmouse, bool
>+zero_pkt) {
>+      int i;
>+      struct input_dev *input = psmouse->dev;
>+      struct cytp_data *cytp = psmouse->private;
>+      struct cytp_report_data report_data;
>+      struct cytp_contact *contact;
>+      int slot;
>+
>+      if (cypress_parse_packet(psmouse, cytp, &report_data))
>+              return;
>+
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              struct input_mt_pos pos[CYTP_MAX_CONTACTS];
>+              int slots[CYTP_MAX_MT_SLOTS];
>+              int n = report_data.contact_cnt;
>+              int ret;
>+
>+              if (n > CYTP_MAX_MT_SLOTS)
>+                      n = CYTP_MAX_MT_SLOTS;
>+
>+              for (i = 0; i < n; i++) {
>+                      contact = &report_data.contacts[i];
>+                      pos[i].x = contact->x;
>+                      pos[i].y = contact->y;
>+              }
>+
>+              ret = input_mt_assign_slots(input, slots, pos, n);
>+              if (ret < 0) {
>+                      psmouse_err(psmouse,
>+                          "input_mt_assign_slots(%d) returned %d\n", n, ret);
>+                      n = 0;
>+              }
>+
>+              for (i = 0; i < n; i++) {
>+                      contact = &report_data.contacts[i];
>+                      slot = slots[i];
>+                      input_mt_slot(input, slot);
>+                      input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
>+                      input_report_abs(input, ABS_MT_POSITION_X, contact->x);
>+                      input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
>+                      input_report_abs(input, ABS_MT_PRESSURE, contact->z);
>+              }
>+
>+              input_mt_sync_frame(input);
>+
>+      } else {
>+              if (report_data.contact_cnt == 1) {
>+                      input_report_rel(input, REL_X, report_data.contacts[0].x);
>+                      input_report_rel(input, REL_Y, report_data.contacts[0].y);
>+              }
>+
>+              input_report_rel(input, REL_WHEEL, report_data.vscroll);
>+              if (cytp->mode & CYTP_BIT_CYPRESS_REL)
>+                      input_report_rel(input, REL_HWHEEL, report_data.hscroll);
>+      }
>+
>+      input_report_key(input, BTN_LEFT, report_data.left);
>+      input_report_key(input, BTN_RIGHT, report_data.right);
>+      input_report_key(input, BTN_MIDDLE, report_data.middle);
>+
>+      input_sync(input);
>+}
>+
>+static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) {
>+      int contact_cnt;
>+      int index = psmouse->pktcnt - 1;
>+      unsigned char *packet = psmouse->packet;
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (index < 0 || index > cytp->pkt_size)
>+              return PSMOUSE_BAD_DATA;
>+
>+      if ((index == 0) && ((packet[0] & 0xfc) == 0)) {
>+              /* call packet process for reporting finger leave. */
>+              cypress_process_packet(psmouse, 1);
>+              return PSMOUSE_FULL_PACKET;
>+      }
>+
>+      if (cytp->mode & CYTP_BIT_ABS_MASK) {
>+              if (index == 0) {
>+                      if ((packet[0] & 0x08) == 0x08)
>+                              return PSMOUSE_BAD_DATA;
>+
>+                      contact_cnt = cypress_get_finger_count(packet[0]);
>+
>+                      if (contact_cnt > 5)
>+                              return PSMOUSE_BAD_DATA;
>+
>+                      if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) {
>+                              CYTP_SET_PACKET_SIZE(4);
>+                              if (contact_cnt == 2)
>+                                      CYTP_SET_PACKET_SIZE(7);
>+                      } else {
>+                              CYTP_SET_PACKET_SIZE(5);
>+                              if (contact_cnt == 2)
>+                                      CYTP_SET_PACKET_SIZE(8);
>+                      }
>+              }
>+
>+              return PSMOUSE_GOOD_DATA;
>+      } else {
>+              if (index == 0) {
>+                      if ((packet[0] & 0x08) != 0x08)
>+                              return PSMOUSE_BAD_DATA;
>+
>+                      CYTP_SET_PACKET_SIZE(3);
>+                      if (cytp->mode & CYTP_BIT_CYPRESS_REL)
>+                              CYTP_SET_PACKET_SIZE(5);
>+              }
>+
>+              return PSMOUSE_GOOD_DATA;
>+      }
>+}
>+
>+static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
>+{
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (psmouse->pktcnt >= cytp->pkt_size) {
>+              cypress_process_packet(psmouse, 0);
>+              return PSMOUSE_FULL_PACKET;
>+      }
>+
>+      return cypress_validate_byte(psmouse); }
>+
>+static void cypress_set_rate(struct psmouse *psmouse, unsigned int
>+rate) {
>+      struct cytp_data *cytp = psmouse->private;
>+
>+      if (rate >= 80) {
>+              psmouse->rate = 80;
>+              CYTP_SET_MODE_BIT(CYTP_BIT_HIGH_RATE);
>+      } else {
>+              psmouse->rate = 40;
>+              CYTP_CLEAR_MODE_BIT(CYTP_BIT_HIGH_RATE);
>+      }
>+
>+      ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
>+                  PSMOUSE_CMD_SETRATE);
>+}
>+
>+static void cypress_disconnect(struct psmouse *psmouse) {
>+      cypress_reset(psmouse);
>+      kfree(psmouse->private);
>+      psmouse->private = NULL;
>+}
>+
>+#if 0
>+/*
>+ * FIXME: cypress_reconnect() never works...
>+ * cypress_detect() always fails here.
>+ */
>+static int cypress_reconnect(struct psmouse *psmouse) {
>+      int tries = CYTP_PS2_CMD_TRIES;
>+      int rc;
>+
>+      do {
>+              cypress_reset(psmouse);
>+              rc = cypress_detect(psmouse, false);
>+      } while (rc && (--tries > 0));
>+
>+      if (rc) {
>+              psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
>+              return -1;
>+      }
>+
>+      if (cypress_query_hardware(psmouse)) {
>+              psmouse_err(psmouse, "Reconnect: unable to query Trackpad hardware.\n");
>+              return -1;
>+      }
>+
>+      if (cypress_set_absolute_mode(psmouse)) {
>+              psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
>+              return -1;
>+      }
>+
>+      return 0;
>+}
>+#endif

Could you help describe the test steps that you see it never works, so I can help fix this issue. Thanks.

>+
>+int cypress_init(struct psmouse *psmouse) {
>+      struct cytp_data *cytp;
>+
>+      cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
>+      psmouse->private = (void *)cytp;
>+      if (cytp == NULL)
>+              return -ENOMEM;
>+
>+      cypress_reset(psmouse);
>+
>+      if (cypress_query_hardware(psmouse)) {
>+              psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
>+              goto err_exit;
>+      }
>+
>+      if (cypress_set_absolute_mode(psmouse)) {
>+              psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
>+              goto err_exit;
>+      }
>+
>+      if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
>+              psmouse_err(psmouse, "init: Unable to set input params.\n");
>+              goto err_exit;
>+      }
>+
>+      psmouse->model = 1;
>+      psmouse->protocol_handler = cypress_protocol_handler;
>+      psmouse->set_rate = cypress_set_rate;
>+      psmouse->disconnect = cypress_disconnect; #if 0
>+      /* FIXME: cypress_reconnect() never works...
>+       * just let psmouse re-init() us for now.
>+       */
>+      psmouse->reconnect = cypress_reconnect; #endif
>+      psmouse->cleanup = cypress_reset;
>+      psmouse->pktsize = 8;
>+      psmouse->resync_time = 0;
>+
>+      return 0;
>+
>+err_exit:
>+      /*
>+       * Reset Cypress Trackpad as a standard mouse. Then
>+       * let psmouse driver commmunicating with it as default PS2 mouse.
>+       */
>+      cypress_reset(psmouse);
>+
>+      psmouse->private = NULL;
>+      kfree(cytp);
>+
>+      return -1;
>+}
>+
>+bool cypress_supported(void)
>+{
>+      return true;
>+}
>diff --git a/drivers/input/mouse/cypress_ps2.h
>b/drivers/input/mouse/cypress_ps2.h
>new file mode 100644
>index 0000000..ce70462
>--- /dev/null
>+++ b/drivers/input/mouse/cypress_ps2.h
>@@ -0,0 +1,219 @@
>+#ifndef _CYPRESS_PS2_H
>+#define _CYPRESS_PS2_H
>+
>+#include "psmouse.h"
>+
>+#define CMD_BITS_MASK 0x03
>+#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
>+
>+#define ENCODE_CMD(aa, bb, cc, dd) \
>+      (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
>+#define CYTP_CMD_ABS_NO_PRESSURE_MODE       ENCODE_CMD(0, 1, 0, 0)
>+#define CYTP_CMD_ABS_WITH_PRESSURE_MODE     ENCODE_CMD(0, 1, 0, 1)
>+#define CYTP_CMD_SMBUS_MODE                 ENCODE_CMD(0, 1, 1, 0)
>+#define CYTP_CMD_STANDARD_MODE              ENCODE_CMD(0, 2, 0, 0)  /* not implemented yet. */
>+#define CYTP_CMD_CYPRESS_REL_MODE           ENCODE_CMD(1, 1, 1, 1)  /* not implemented yet. */
>+#define CYTP_CMD_READ_CYPRESS_ID            ENCODE_CMD(0, 0, 0, 0)
>+#define CYTP_CMD_READ_VITAL_STATISTICS      ENCODE_CMD(0, 0, 0, 1)
>+#define CYTP_CMD_SET_HSCROLL_WIDTH(w)       ENCODE_CMD(1, 1, 0, (w))
>+#define     CYTP_CMD_SET_HSCROLL_MASK       ENCODE_CMD(1, 1, 0, 0)
>+#define CYTP_CMD_SET_VSCROLL_WIDTH(w)       ENCODE_CMD(1, 2, 0, (w))
>+#define     CYTP_CMD_SET_VSCROLL_MASK       ENCODE_CMD(1, 2, 0, 0)
>+#define CYTP_CMD_SET_PALM_GEOMETRY(e)       ENCODE_CMD(1, 2, 1, (e))
>+#define     CYTP_CMD_PALM_GEMMETRY_MASK     ENCODE_CMD(1, 2, 1, 0)
>+#define CYTP_CMD_SET_PALM_SENSITIVITY(s)    ENCODE_CMD(1, 2, 2, (s))
>+#define     CYTP_CMD_PALM_SENSITIVITY_MASK  ENCODE_CMD(1, 2, 2, 0)
>+#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s)   ENCODE_CMD(1, 3, ((s) >> 2), (s))
>+#define     CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
>+#define CYTP_CMD_REQUEST_BASELINE_STATUS    ENCODE_CMD(2, 0, 0, 1)
>+#define CYTP_CMD_REQUEST_RECALIBRATION      ENCODE_CMD(2, 0, 0, 3)
>+
>+#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK) #define
>+DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK) #define DECODE_CMD_CC(x)
>+(((x) >> 2) & CMD_BITS_MASK) #define DECODE_CMD_DD(x) ((x) &
>+CMD_BITS_MASK)
>+
>+/* Cypress trackpad working mode. */
>+#define CYTP_BIT_ABS_PRESSURE    (1 << 3)
>+#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
>+#define CYTP_BIT_CYPRESS_REL     (1 << 1)
>+#define CYTP_BIT_STANDARD_REL    (1 << 0)
>+#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL |
>+CYTP_BIT_STANDARD_REL) #define CYTP_BIT_ABS_MASK
>+(CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE) #define
>+CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
>+
>+#define CYTP_BIT_HIGH_RATE       (1 << 4)
>+/*
>+ * report mode bit is set, firmware working in Remote Mode.
>+ * report mode bit is cleared, firmware working in Stream Mode.
>+ */
>+#define CYTP_BIT_REPORT_MODE     (1 << 5)
>+
>+/* scrolling width values for set HSCROLL and VSCROLL width command.
>+*/ #define SCROLL_WIDTH_NARROW 1 #define SCROLL_WIDTH_NORMAL 2
>+#define SCROLL_WIDTH_WIDE   3
>+
>+#define PALM_GEOMETRY_ENABLE  1
>+#define PALM_GEOMETRY_DISABLE 0
>+
>+#define CYPRESS_KEY_1 0x33
>+#define CYPRESS_KEY_2 0xCC
>+
>+#define VITAL_STATICS_MASK 0x80
>+#define FW_VERSION_MASX    0x7f
>+#define FW_VER_HIGH_MASK 0x70
>+#define FW_VER_LOW_MASK  0x0f
>+
>+/* Times to retry a ps2_command and millisecond delay between tries.
>+*/ #define CYTP_PS2_CMD_TRIES 3 #define CYTP_PS2_CMD_DELAY 500
>+
>+/* time out for PS/2 command only in milliseconds. */ #define
>+CYTP_CMD_TIMEOUT  200 #define CYTP_DATA_TIMEOUT 30
>+
>+#define CYTP_EXT_CMD   0xe8
>+#define CYTP_PS2_RETRY 0xfe
>+#define CYTP_PS2_ERROR 0xfc
>+
>+#define CYTP_RESP_RETRY 0x01
>+#define CYTP_RESP_ERROR 0xfe
>+
>+
>+#define CYTP_105001_WIDTH  97   /* Dell XPS 13 */
>+#define CYTP_105001_HIGH   59
>+#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH) #define
>+CYTP_DEFAULT_HIGH  (CYTP_105001_HIGH)
>+
>+#define CYTP_ABS_MAX_X     1600
>+#define CYTP_ABS_MAX_Y     900
>+#define CYTP_MAX_PRESSURE  255
>+#define CYTP_MIN_PRESSURE  0
>+
>+/* header byte bits of relative package. */
>+#define BTN_LEFT_BIT   0x01
>+#define BTN_RIGHT_BIT  0x02
>+#define BTN_MIDDLE_BIT 0x04
>+#define REL_X_SIGN_BIT 0x10
>+#define REL_Y_SIGN_BIT 0x20
>+
>+/* header byte bits of absolute package. */ #define ABS_VSCROLL_BIT
>+0x10 #define ABS_HSCROLL_BIT 0x20 #define ABS_MULTIFINGER_TAP 0x04
>+#define ABS_EDGE_MOTION_MASK 0x80
>+
>+#define DFLT_RESP_BITS_VALID     0x88  /* SMBus bit should not be set. */
>+#define DFLT_RESP_SMBUS_BIT      0x80
>+#define   DFLT_SMBUS_MODE        0x80
>+#define   DFLT_PS2_MODE          0x00
>+#define DFLT_RESP_BIT_MODE       0x40
>+#define   DFLT_RESP_REMOTE_MODE  0x40
>+#define   DFLT_RESP_STREAM_MODE  0x00
>+#define DFLT_RESP_BIT_REPORTING  0x20
>+#define DFLT_RESP_BIT_SCALING    0x10
>+
>+#define VITAL_BIT_PALM               0x80
>+#define VITAL_BIT_STUBBORN           0x40
>+#define VITAL_BIT_2F_JITTER          0x30
>+#define VITAL_BIT_1F_JITTER          0x0c
>+#define VITAL_BIT_APA                0x02
>+#define VITAL_BIT_MTG                0x01
>+#define VITAL_BIT_ABS_PKT_FORMAT_SET 0xf0
>+#define VITAL_BIT_2F_SPIKE           0x0c
>+#define VITAL_BIT_1F_SPIKE           0x03
>+
>+/* bits of first byte response of E9h-Status Request command. */
>+#define RESP_BTN_RIGHT_BIT  0x01 #define RESP_BTN_MIDDLE_BIT 0x02
>+#define RESP_BTN_LEFT_BIT   0x04
>+#define RESP_SCALING_BIT    0x10
>+#define RESP_ENABLE_BIT     0x20
>+#define RESP_REMOTE_BIT     0x40
>+#define RESP_SMBUS_BIT      0x80
>+
>+#define CYTP_MAX_CONTACTS 5
>+#define CYTP_MAX_MT_SLOTS 2
>+
>+enum cytp_type {
>+      CYTP_STG,
>+      CYTP_MTG,
>+      CYTP_APA,
>+};
>+
>+struct cytp_contact {
>+      int x;
>+      int y;
>+      int z;  /* also named as touch pressure. */ };
>+
>+/* The structure of */
>+struct cytp_report_data {
>+      int contact_cnt;
>+      struct cytp_contact contacts[CYTP_MAX_CONTACTS];
>+      unsigned int left:1;
>+      unsigned int right:1;
>+      unsigned int middle:1;
>+      unsigned int tap:1;  /* multi-finger tap detected. */
>+      signed char vscroll;
>+      signed char hscroll;
>+};
>+
>+/* The structure of Cypress Trackpad device private data. */ struct
>+cytp_data {
>+      int fw_version;
>+
>+      int pkt_size;
>+      int mode;
>+
>+      int scaling;
>+      int reporting;
>+
>+      int tp_min_pressure;
>+      int tp_max_pressure;
>+      int tp_width;  /* X direction physical size in mm. */
>+      int tp_high;  /* Y direction physical size in mm. */
>+      int tp_max_abs_x;  /* Max X absolution units can be reported. */
>+      int tp_max_abs_y;  /* Max Y absolution units can be reported. */
>+
>+      int tp_res_x;  /* X resolution in units/mm. */
>+      int tp_res_y;  /* Y resolution in units/mm. */
>+
>+      enum cytp_type tp_type;
>+      unsigned char tp_palm;
>+      unsigned char tp_stubborn;
>+      unsigned char tp_2f_jitter;
>+      unsigned char tp_1f_jitter;
>+      unsigned char tp_abs_packet_format_set;
>+      unsigned char tp_2f_spike;
>+      unsigned char tp_1f_spike;
>+
>+      int vital_statics_supported;
>+
>+      int prev_contact_cnt;
>+      int zero_packet_cnt;
>+      struct cytp_report_data prev_report_data; };
>+
>+
>+#ifdef CONFIG_MOUSE_PS2_CYPRESS
>+int cypress_detect(struct psmouse *psmouse, bool set_properties); int
>+cypress_init(struct psmouse *psmouse); bool cypress_supported(void);
>+#else inline int cypress_detect(struct psmouse *psmouse, bool
>+set_properties) {
>+      return -ENOSYS;
>+}
>+inline int cypress_init(struct psmouse *psmouse) {
>+      return -ENOSYS;
>+}
>+inline bool cypress_supported(void)
>+{
>+      return 0;
>+}
>+#endif /* CONFIG_MOUSE_PS2_CYPRESS */
>+
>+#endif  /* _CYPRESS_PS2_H */
>--
>1.7.10.4


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-12-03  5:57   ` Dudley Du
@ 2012-12-03  6:31     ` Henrik Rydberg
  2012-12-03  6:31       ` Dudley Du
  0 siblings, 1 reply; 17+ messages in thread
From: Henrik Rydberg @ 2012-12-03  6:31 UTC (permalink / raw)
  To: Dudley Du
  Cc: Kamal Mostafa, linux-input, linux-kernel, Dmitry Torokhov,
	David Solda, Troy Abercrombia, customercare, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski, dudl

Hi Dudley,

> Another question is that new interface input_mt_assign_slots() is introduced since 3.7-rc7 or later,
> So we could not support the kernel releases that earlier than 3.7-rc7 with this driver.

The patch is a submission to mainline, so of course it should be in sync with mainline.

Thanks.
Henrik

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-12-03  6:31     ` Henrik Rydberg
@ 2012-12-03  6:31       ` Dudley Du
  0 siblings, 0 replies; 17+ messages in thread
From: Dudley Du @ 2012-12-03  6:31 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Kamal Mostafa, linux-input, linux-kernel, Dmitry Torokhov,
	David Solda, Troy Abercrombia, customercare, Kyle Fazzari,
	Mario Limonciello, Tim Gardner, Herton Krzesinski, dudl

Hi Henrik,

Okay, agree.
Thanks.

Best Wishes,
Dudley Du
dudl@cypress.com

-----Original Message-----
From: Henrik Rydberg [mailto:rydberg@euromail.se]
Sent: Monday, December 03, 2012 2:31 PM
To: Dudley Du
Cc: Kamal Mostafa; linux-input@vger.kernel.org; linux-kernel@vger.kernel.org; Dmitry Torokhov; David Solda; Troy Abercrombia; customercare; Kyle Fazzari; Mario Limonciello; Tim Gardner; Herton Krzesinski; dudl@cyress.com
Subject: Re: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver

Hi Dudley,

> Another question is that new interface input_mt_assign_slots() is
> introduced since 3.7-rc7 or later, So we could not support the kernel releases that earlier than 3.7-rc7 with this driver.

The patch is a submission to mainline, so of course it should be in sync with mainline.

Thanks.
Henrik

This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/4] Cypress PS/2 Trackpad driver
  2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
                   ` (3 preceding siblings ...)
  2012-11-29 21:58 ` [PATCH v3 4/4] input: Cypress PS/2 Trackpad simulated multitouch Kamal Mostafa
@ 2012-12-03  7:36 ` Dmitry Torokhov
  2012-12-05  2:22   ` SEMI_MT vs. simulated mt (was Re: [PATCH v3 0/4] Cypress PS/2 Trackpad driver) Kamal Mostafa
  2012-12-03  7:50 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Henrik Rydberg
  5 siblings, 1 reply; 17+ messages in thread
From: Dmitry Torokhov @ 2012-12-03  7:36 UTC (permalink / raw)
  To: Kamal Mostafa
  Cc: linux-input, linux-kernel, Henrik Rydberg, David Solda,
	Troy Abercrombia, Dudley Du, Cypress Semiconductor Corporation,
	Kyle Fazzari, Mario Limonciello, Tim Gardner, Herton Krzesinski

Hi Kamal,

On Thu, Nov 29, 2012 at 01:57:57PM -0800, Kamal Mostafa wrote:
> This driver, submitted on behalf of Cypress Semiconductor Corporation and
> additional contributors, provides support for the Cypress PS/2 Trackpad.
> 
> This [PATCH v3] version differs from my previous submitted version[1]:
> 
>   Patch #1 (cmdbuf to 8 bytes) and #3 (link in driver) are unchanged.
> 
>   Patch #2 (main driver), as recommended by Henrik Rydberg[2]:
>   - use input_mt_assign_slots; drop cypress_cal_finger_id.
>   - enable 2-finger-only SEMI_MT; drop cypress_simulate_fingers.
>   - various code clean-ups.
> 
>   Henrik, does patch #2 appear to properly use assign_slots and SEMI_MT as
>   you intended?  This SEMI_MT method does work (with 2 finger support only),
>   but I'm not clear why we wouldn't want to handle >2 fingers also, so ...
> 
>   Patch #4 (new) reintroduces simulated multitouch for up to 5 fingers
>   (#if CYPRESS_SIMULATE_MT), disabling SEMI_MT again.
> 
>   If that functionality (support for >2 fingers) can be acheived in some
>   better way, please advise.

You can still report true number of fingers on the pad via BTN_TOOL_*TAP
while reporting the bounding box, the same way as Synaptics, Elantech,
ALPS and Sentelic drivers are doing it.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-11-29 21:57 ` [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver Kamal Mostafa
  2012-12-03  3:20   ` Dudley Du
  2012-12-03  5:57   ` Dudley Du
@ 2012-12-03  7:45   ` Henrik Rydberg
  2012-12-03 17:04     ` Dmitry Torokhov
  2012-12-05  2:22     ` Kamal Mostafa
  2 siblings, 2 replies; 17+ messages in thread
From: Henrik Rydberg @ 2012-12-03  7:45 UTC (permalink / raw)
  To: Kamal Mostafa
  Cc: linux-input, linux-kernel, Dmitry Torokhov, David Solda,
	Troy Abercrombia, Dudley Du, Cypress Semiconductor Corporation,
	Kyle Fazzari, Mario Limonciello, Tim Gardner, Herton Krzesinski

Hi Kamal,

> From: Cypress Semiconductor Corporation <customercare@cypress.com>
> 
> Input/mouse driver for Cypress PS/2 Trackpad.
> 
> Original code contributed by Cypress Semiconductor Corporation,
> modified by Kamal Mostafa and Kyle Fazzari.
> 
> BugLink: http://launchpad.net/bugs/978807
> 
> Signed-off-by: Kamal Mostafa <kamal@canonical.com>
> Signed-off-by: Kyle Fazzari <git@status.e4ward.com>
> Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> Acked-by: Herton Krzesinski <herton.krzesinski@canonical.com>
> ---
>  drivers/input/mouse/cypress_ps2.c |  830 +++++++++++++++++++++++++++++++++++++
>  drivers/input/mouse/cypress_ps2.h |  219 ++++++++++
>  2 files changed, 1049 insertions(+)
>  create mode 100644 drivers/input/mouse/cypress_ps2.c
>  create mode 100644 drivers/input/mouse/cypress_ps2.h

Thanks for the patch, it seems to improve nicely. Please find comments inline.

> 
> diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
> new file mode 100644
> index 0000000..472342a
> --- /dev/null
> +++ b/drivers/input/mouse/cypress_ps2.c
> @@ -0,0 +1,830 @@
> +/*
> + * Cypress Trackpad PS/2 mouse driver
> + *
> + * Copyright (c) 2012 Cypress Semiconductor Corporation.
> + *
> + * Additional contributors include:
> + *   Kamal Mostafa <kamal@canonical.com>
> + *   Kyle Fazzari <git@status.e4ward.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/serio.h>
> +#include <linux/libps2.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +
> +#include "cypress_ps2.h"
> +
> +#define CYTP_DBG_DUMP 0		/* set to 1 for more verbose debug dump */
> +
> +#define cytp_dbg(fmt, ...)  \
> +	do {  \
> +		if (cytp)  \
> +			psmouse_dbg(psmouse, pr_fmt(fmt), ##__VA_ARGS__);  \
> +	} while (0)

Where is cytp defined?

> +
> +#if CYTP_DBG_DUMP
> +# define cytp_dbg_dump cytp_dbg
> +#else
> +# define cytp_dbg_dump(fmt, ...)
> +#endif
> +
> +
> +/* p is a pointer points to the buffer containing Cypress Keys. */
> +#define IS_CYPRESS_KEY(p) ((p[0] == CYPRESS_KEY_1) && (p[1] == CYPRESS_KEY_2))

If you use a function instead, you do not need to explain the type.

> +#define CYTP_SET_PACKET_SIZE(n) { psmouse->pktsize = cytp->pkt_size = (n); }

More magic cytp argument stuff, please reformulate this.

> +#define CYTP_SET_MODE_BIT(x)  \
> +	do {  \
> +		if ((x) & CYTP_BIT_ABS_REL_MASK)  \
> +			cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK);  \
> +		cytp->mode |= (x);  \
> +	} while (0)

Given the generic name, this function does something completely
unexpected. Also, branching on bit manipulations seems odd.

> +#define CYTP_CLEAR_MODE_BIT(x)	{ cytp->mode &= ~(x); }

Seems unnecessary to define here for a single instance in the code.

> +
> +#define CYTP_SUPPORT_ABS

Stale code?

> +
> +static unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
> +static unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};

const, please.

> +
> +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +
> +	if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
> +		cytp_dbg("send command 0x%02x failed, resp 0x%02x\n",
> +			 value & 0xff, ps2dev->nak);
> +		if (ps2dev->nak == CYTP_PS2_RETRY)
> +			return CYTP_PS2_RETRY;
> +		else
> +			return CYTP_PS2_ERROR;
> +	}
> +
> +	cytp_dbg("send command 0x%02x success, resp 0xfa\n", value & 0xff);
> +
> +	return 0;
> +}
> +
> +static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
> +			       unsigned char data)
> +{
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	int tries = CYTP_PS2_CMD_TRIES;
> +	int rc;
> +
> +	ps2_begin_command(ps2dev);
> +
> +	do {
> +		/*
> +		 * send extension command 0xE8 or 0xF3,
> +		 * if send extension command failed,
> +		 * try to send recovery command to make
> +		 * trackpad device return to ready wait command state.
> +		 * It alwasy success based on this recovery commands.

-EPARSE

> +		 */
> +		rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
> +		if (rc == CYTP_PS2_RETRY) {
> +			rc = cypress_ps2_sendbyte(psmouse, 0x00);
> +			if (rc == CYTP_PS2_RETRY)
> +				rc = cypress_ps2_sendbyte(psmouse, 0x0a);
> +		}
> +		if (rc == CYTP_PS2_ERROR)
> +			continue;
> +
> +		rc = cypress_ps2_sendbyte(psmouse, data);
> +		if (rc == CYTP_PS2_RETRY)
> +			rc = cypress_ps2_sendbyte(psmouse, data);
> +		if (rc == CYTP_PS2_ERROR)
> +			continue;
> +		else
> +			break;
> +	} while (--tries > 0);
> +
> +	ps2_end_command(ps2dev);
> +
> +	return rc;
> +}
> +
> +static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
> +				       unsigned char cmd,
> +				       unsigned char *param)
> +{
> +	int i;
> +	int rc;
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	struct cytp_data *cytp = psmouse->private;
> +	enum psmouse_state old_state;
> +	unsigned char old_pktsize;
> +
> +	ps2_begin_command(&psmouse->ps2dev);
> +
> +	old_state = psmouse->state;
> +	psmouse->state = PSMOUSE_CMD_MODE;
> +	psmouse->pktcnt = 0;
> +	old_pktsize = psmouse->pktsize;
> +	psmouse->pktsize = 3;
> +	if (cmd == CYTP_CMD_READ_VITAL_STATISTICS)
> +		psmouse->pktsize = 8;
> +	memset(param, 0, psmouse->pktsize);
> +
> +	rc = cypress_ps2_sendbyte(psmouse, 0xe9);
> +	if (rc < 0)
> +		goto out;
> +
> +	wait_event_timeout(ps2dev->wait,
> +			(psmouse->pktcnt >= psmouse->pktsize),
> +			msecs_to_jiffies(CYTP_CMD_TIMEOUT));
> +
> +	memcpy(param, psmouse->packet, psmouse->pktsize);
> +
> +	cytp_dbg("Command 0x%02x response data: (0x)", cmd);
> +	for (i = 0; i < psmouse->pktsize; i++)
> +		cytp_dbg(" %02x", param[i]);
> +	cytp_dbg("\n");
> +
> +out:
> +	psmouse->state = old_state;
> +	psmouse->pktcnt = 0;
> +	psmouse->pktsize = old_pktsize;
> +
> +	ps2_end_command(&psmouse->ps2dev);
> +
> +	return rc;
> +}
> +
> +static int cypress_verify_cmd_state(struct psmouse *psmouse,
> +				    unsigned char cmd, unsigned char *param)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +	bool rate_match = 0;
> +	bool resolution_match = 0;
> +	int i;
> +
> +	/* callers will do further checking. */
> +	if ((cmd == CYTP_CMD_READ_CYPRESS_ID) ||
> +	    (cmd == CYTP_CMD_STANDARD_MODE) ||
> +	    (cmd == CYTP_CMD_READ_VITAL_STATISTICS))
> +		return 0;
> +	if (((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID) &&
> +	    ((param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE)) {
> +		for (i = 0; i < sizeof(cytp_resolution); i++)
> +			if (cytp_resolution[i] == param[1])
> +				resolution_match =  1;
> +
> +		for (i = 0; i < sizeof(cytp_rate); i++)
> +			if (cytp_rate[i] == param[2])
> +				rate_match = 1;
> +
> +		if (resolution_match && rate_match)
> +			return 0;
> +	}
> +
> +	cytp_dbg("verify cmd state failed.\n");
> +	return -1;
> +}
> +
> +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
> +				unsigned char *param)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +	int tries = CYTP_PS2_CMD_TRIES;
> +	int rc;
> +
> +	cytp_dbg("send extension cmd 0x%02x, [%d %d %d %d]\n",
> +		 cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
> +		 DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
> +	do {
> +		cypress_ps2_ext_cmd(psmouse,
> +				    PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
> +		cypress_ps2_ext_cmd(psmouse,
> +				    PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
> +		cypress_ps2_ext_cmd(psmouse,
> +				    PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
> +		cypress_ps2_ext_cmd(psmouse,
> +				    PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
> +
> +		rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
> +		if ((rc == 0) &&
> +		    (cypress_verify_cmd_state(psmouse, cmd, param) == 0))
> +			return 0;
> +	} while (--tries > 0);
> +
> +
> +	if (tries <= 0)
> +		return -1;

How could it be any different here?

> +
> +	return 0;
> +
> +}
> +
> +int cypress_detect(struct psmouse *psmouse, bool set_properties)
> +{
> +	unsigned char param[3];
> +
> +	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
> +		return -1;
> +
> +	if (!IS_CYPRESS_KEY(param))
> +		return -ENODEV;
> +
> +	if (set_properties) {
> +		psmouse->vendor = "Cypress";
> +		psmouse->name = "Trackpad";
> +	}
> +
> +	return 0;
> +}
> +
> +static int cypress_read_fw_version(struct psmouse *psmouse)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +	unsigned char param[3];
> +
> +	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
> +		return -1;
> +
> +	if (!IS_CYPRESS_KEY(param))
> +		return -ENODEV;
> +
> +	cytp->fw_version = param[2] & FW_VERSION_MASX;
> +	cytp->vital_statics_supported = (param[2] & VITAL_STATICS_MASK) ? 1 : 0;
> +
> +	cytp_dbg("cytp->fw_version = %d\n", cytp->fw_version);
> +	cytp_dbg("cytp->vital_statics_supported = %d\n",
> +		 cytp->vital_statics_supported);
> +	return 0;
> +}
> +
> +static int cypress_read_vital_statistics(struct psmouse *psmouse)

Why do you call this statistics?

> +{
> +	struct cytp_data *cytp = psmouse->private;
> +	unsigned char param[8];
> +
> +	/* set default values for vital statistics not supported trackpad. */
> +	cytp->tp_width = CYTP_DEFAULT_WIDTH;
> +	cytp->tp_high = CYTP_DEFAULT_HIGH;
> +	cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
> +	cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
> +	cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
> +	cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
> +	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
> +	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
> +
> +	memset(param, 0, sizeof(param));
> +	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_VITAL_STATISTICS, param) == 0) {
> +		/* Update trackpad parameters. */
> +		cytp->tp_max_abs_x = (param[1] << 8) | param[0];
> +		cytp->tp_max_abs_y = (param[3] << 8) | param[2];
> +		cytp->tp_min_pressure = param[4];
> +		cytp->tp_max_pressure = param[5];
> +
> +		if (param[6] & VITAL_BIT_APA)
> +			cytp->tp_type = CYTP_APA;
> +		else if (param[6] & VITAL_BIT_MTG)
> +			cytp->tp_type = CYTP_MTG;
> +		else
> +			cytp->tp_type = CYTP_STG;
> +		cytp->tp_palm = (param[6] & VITAL_BIT_PALM) ? 1 : 0;
> +		cytp->tp_stubborn = (param[6] & VITAL_BIT_STUBBORN) ? 1 : 0;
> +		cytp->tp_2f_jitter = (param[6] & VITAL_BIT_2F_JITTER) >> 4;
> +		cytp->tp_1f_jitter = (param[6] & VITAL_BIT_1F_JITTER) >> 2;
> +		cytp->tp_abs_packet_format_set =
> +			(param[7] & VITAL_BIT_ABS_PKT_FORMAT_SET) >> 4;
> +		cytp->tp_2f_spike = (param[7] & VITAL_BIT_2F_SPIKE) >> 2;
> +		cytp->tp_1f_spike = (param[7] & VITAL_BIT_1F_SPIKE);
> +
> +	}
> +
> +	if (!cytp->tp_max_pressure ||
> +	    (cytp->tp_max_pressure < cytp->tp_min_pressure) ||
> +	    (!cytp->tp_width || !cytp->tp_high) ||
> +	    (!cytp->tp_max_abs_x) ||
> +	    (cytp->tp_max_abs_x < cytp->tp_width) ||
> +	    (!cytp->tp_max_abs_y) ||
> +	    (cytp->tp_max_abs_y < cytp->tp_high))
> +		return -1;
> +
> +	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
> +	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
> +
> +	cytp_dbg_dump("Dump trackpad hardware configuration as below:\n");
> +	cytp_dbg_dump("cytp->tp_width = %d\n", cytp->tp_width);
> +	cytp_dbg_dump("cytp->tp_high = %d\n", cytp->tp_high);
> +	cytp_dbg_dump("cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
> +	cytp_dbg_dump("cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
> +	cytp_dbg_dump("cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
> +	cytp_dbg_dump("cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
> +	cytp_dbg_dump("cytp->tp_res_x = %d\n", cytp->tp_res_x);
> +	cytp_dbg_dump("cytp->tp_res_y = %d\n", cytp->tp_res_y);
> +	cytp_dbg_dump("cytp->tp_type = %d\n", cytp->tp_type);
> +	cytp_dbg_dump("cytp->tp_palm = %d\n", cytp->tp_palm);
> +	cytp_dbg_dump("cytp->tp_stubborn = %d\n", cytp->tp_stubborn);
> +	cytp_dbg_dump("cytp->tp_1f_jitter = %d\n", cytp->tp_1f_jitter);
> +	cytp_dbg_dump("cytp->tp_2f_jitter = %d\n", cytp->tp_2f_jitter);
> +	cytp_dbg_dump("cytp->tp_1f_spike = %d\n", cytp->tp_1f_spike);
> +	cytp_dbg_dump("cytp->tp_2f_spike = %d\n", cytp->tp_2f_spike);
> +	cytp_dbg_dump("cytp->tp_abs_packet_format_set = %d\n",
> +		      cytp->tp_abs_packet_format_set);
> +
> +	return 0;
> +}
> +
> +static int cypress_query_hardware(struct psmouse *psmouse)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +
> +	if (cypress_read_fw_version(psmouse))
> +		return -1;
> +
> +	if (cytp->vital_statics_supported) {
> +		if (cypress_read_vital_statistics(psmouse))
> +			return -1;
> +	}
> +
> +	return 0;
> +}

Don't you want to propagate the returned error here?

> +
> +static int cypress_set_absolute_mode(struct psmouse *psmouse)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +	unsigned char param[3];
> +
> +	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
> +		return -1;
> +
> +	CYTP_SET_MODE_BIT(CYTP_BIT_ABS_PRESSURE);
> +	CYTP_SET_PACKET_SIZE(5);

Open coding seems preferrable.

> +
> +	return 0;
> +}
> +
> +/*
> + * reset trackpad device to standard relative mode.
> + * This is also the defalut mode when trackpad powered on.
> + */
> +static void cypress_reset(struct psmouse *psmouse)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +
> +	psmouse_reset(psmouse);
> +
> +	CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
> +	CYTP_SET_PACKET_SIZE(3);
> +
> +	cytp->prev_contact_cnt = 0;
> +}

I suppose it is, but is it necessary to reset to default mode?

> +
> +static int cypress_set_input_params(struct input_dev *input,
> +				    struct cytp_data *cytp)
> +{
> +	int ret;
> +
> +	if (cytp->mode & CYTP_BIT_ABS_MASK) {

It seems the device will always operate in this mode, so please simplify.

> +		__set_bit(EV_ABS, input->evbit);
> +		input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
> +		input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
> +		input_set_abs_params(input, ABS_PRESSURE,
> +				     cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
> +		input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
> +
> +		/* finger position */
> +		input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
> +		input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
> +		input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
> +
> +		ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
> +			INPUT_MT_POINTER|INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);

Here, INPUT_MT_POINTER needs to be dropped; for semi-mt devices, the
number of fingers is propagated separately using

   input_mt_report_finger_count(dev, finger_count_as_reported_by_the_device),

which conflicts with automatic handling of pointer logic.

> +		if (ret < 0) {
> +			return ret;
> +		}

Style issue, please drop '{}'.

> +
> +		__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
> +
> +		if (cytp->tp_res_x && cytp->tp_res_x) {
> +			input_abs_set_res(input, ABS_X, cytp->tp_res_x);
> +			input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
> +
> +			input_abs_set_res(input, ABS_MT_POSITION_X,
> +					  cytp->tp_res_x);
> +			input_abs_set_res(input, ABS_MT_POSITION_Y,
> +					  cytp->tp_res_y);
> +
> +		}
> +
> +		__set_bit(BTN_TOUCH, input->keybit);
> +		__set_bit(BTN_TOOL_FINGER, input->keybit);
> +		__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
> +		__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
> +		__set_bit(BTN_TOOL_QUADTAP, input->keybit);
> +		__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
> +
> +		__clear_bit(EV_REL, input->evbit);
> +		__clear_bit(REL_X, input->relbit);
> +		__clear_bit(REL_Y, input->relbit);
> +	} else {
> +		__set_bit(EV_REL, input->evbit);
> +		__set_bit(REL_X, input->relbit);
> +		__set_bit(REL_Y, input->relbit);
> +		__set_bit(REL_WHEEL, input->relbit);
> +		__set_bit(REL_HWHEEL, input->relbit);
> +
> +		__clear_bit(EV_ABS, input->evbit);

It seems this block can be removed.

> +	}
> +
> +	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
> +	__set_bit(EV_KEY, input->evbit);
> +	__set_bit(BTN_LEFT, input->keybit);
> +	__set_bit(BTN_RIGHT, input->keybit);
> +	__set_bit(BTN_MIDDLE, input->keybit);
> +
> +	input_set_drvdata(input, cytp);
> +
> +	return 0;
> +}
> +
> +static int cypress_get_finger_count(unsigned char header_byte)
> +{
> +	unsigned char bits6_7;
> +	int finger_count;
> +
> +	bits6_7 = header_byte >> 6;
> +	finger_count = bits6_7 & 0x03;
> +
> +	if (finger_count != 1) {

Maybe return on finger_count == 1 here instead.

> +		if (header_byte & ABS_HSCROLL_BIT) {
> +			if (finger_count == 0) {
> +				/* HSCROLL gets added on to 0 finger count. */
> +				finger_count = 4;
> +				/* should remove HSCROLL bit. */
> +			} else {
> +				if (finger_count == 2) {
> +					finger_count = 5;
> +				} else {
> +					/* Invalid contact (e.g. palm). Ignore it. */
> +					finger_count = 0;
> +				}
> +			}
> +		}
> +	}
> +
> +	return finger_count;
> +}
> +
> +
> +static int cypress_parse_packet(struct psmouse *psmouse,
> +				struct cytp_data *cytp, struct cytp_report_data *report_data)
> +{
> +	int i;
> +	unsigned char *packet = psmouse->packet;
> +	unsigned char header_byte = packet[0];
> +
> +	memset(report_data, 0, sizeof(struct cytp_report_data));
> +	if (cytp->mode & CYTP_BIT_ABS_MASK) {

Ought to be always true, so please drop.

> +		report_data->contact_cnt = cypress_get_finger_count(header_byte);
> +
> +		if (report_data->contact_cnt > CYTP_MAX_CONTACTS) {
> +			/* report invalid data as zero package except the button data. */
> +			report_data->contact_cnt = 0;
> +			cytp_dbg("cypress_parse_packet: received invalid packet.\n");
> +		}
> +
> +		report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
> +
> +		/* Remove HSCROLL bit */
> +		if (report_data->contact_cnt == 4)
> +			header_byte &= ~(ABS_HSCROLL_BIT);

Why conditionally?

> +
> +		if (report_data->contact_cnt == 1) {
> +			report_data->contacts[0].x =
> +				((packet[1] & 0x70) << 4) | packet[2];
> +			report_data->contacts[0].y =
> +				((packet[1] & 0x07) << 8) | packet[3];
> +			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
> +				report_data->contacts[0].z = packet[4];
> +
> +			if ((packet[1] & ABS_EDGE_MOTION_MASK) != ABS_EDGE_MOTION_MASK) {
> +				report_data->vscroll = (header_byte & ABS_VSCROLL_BIT) ? 1 : 0;
> +				report_data->hscroll = (header_byte & ABS_HSCROLL_BIT) ? 1 : 0;
> +			}
> +
> +		} else if (report_data->contact_cnt >= 2) {
> +			report_data->contacts[0].x =
> +				((packet[1] & 0x70) << 4) | packet[2];
> +			report_data->contacts[0].y =
> +				((packet[1] & 0x07) << 8) | packet[3];
> +			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
> +				report_data->contacts[0].z = packet[4];
> +
> +			report_data->contacts[1].x =
> +				((packet[5] & 0xf0) << 4) | packet[6];
> +			report_data->contacts[1].y =
> +				((packet[5] & 0x0f) << 8) | packet[7];
> +			if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
> +				report_data->contacts[1].z = report_data->contacts[0].z;
> +		}
> +
> +		report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
> +		report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
> +
> +	} else {
> +		report_data->contact_cnt = 1;
> +		report_data->contacts[0].x =
> +			(packet[0] & REL_X_SIGN_BIT) ? -packet[1] : packet[1];
> +		report_data->contacts[0].y =
> +			(packet[0] & REL_Y_SIGN_BIT) ? -packet[2] : packet[2];
> +		report_data->vscroll = packet[3];
> +		report_data->left = (packet[0] & BTN_LEFT_BIT) ? 1 : 0;
> +		report_data->right = (packet[0] & BTN_RIGHT_BIT) ? 1 : 0;
> +
> +		if (cytp->mode & CYTP_BIT_STANDARD_REL)
> +			report_data->middle =
> +				(packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
> +		if (cytp->mode & CYTP_BIT_CYPRESS_REL) {
> +			report_data->left =
> +				(packet[0] & BTN_MIDDLE_BIT) ? 1 : 0;
> +			report_data->hscroll = packet[4];
> +		}

Seems unused.

> +	}
> +
> +	/* This is only true if one of the mouse buttons were tapped.
> +	 * Make sure it doesn't turn into a click. The regular tap-to-
> +	 * click functionality will handle that on its own. If we don't
> +	 * do this, disabling tap-to-click won't affect the mouse button
> +	 * zones. */
> +	if (report_data->tap)
> +		report_data->left = 0;
> +
> +	if (report_data->contact_cnt <= 0)
> +		return 0;
> +
> +	cytp_dbg_dump("cypress_parse_packet cytp->zero_packet_cnt = %d\n", cytp->zero_packet_cnt);
> +	cytp_dbg_dump("Dump parsed report data as below:\n");
> +	cytp_dbg_dump("contact_cnt = %d\n", report_data->contact_cnt);
> +	for (i = 0; i < report_data->contact_cnt; i++) {
> +		cytp_dbg_dump("contacts[%d].x = %d\n", i, report_data->contacts[i].x);
> +		cytp_dbg_dump("contacts[%d].y = %d\n", i, report_data->contacts[i].y);
> +		cytp_dbg_dump("contacts[%d].z = %d\n", i, report_data->contacts[i].z);
> +	}
> +	cytp_dbg_dump("vscroll = %d\n", report_data->vscroll);
> +	cytp_dbg_dump("hscroll = %d\n", report_data->hscroll);
> +	cytp_dbg_dump("left = %d\n", report_data->left);
> +	cytp_dbg_dump("right = %d\n", report_data->right);
> +	cytp_dbg_dump("middle = %d\n", report_data->middle);
> +
> +	return 0;
> +}
> +
> +static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
> +{
> +	int i;
> +	struct input_dev *input = psmouse->dev;
> +	struct cytp_data *cytp = psmouse->private;
> +	struct cytp_report_data report_data;
> +	struct cytp_contact *contact;
> +	int slot;

'slot' can be removed.

> +
> +	if (cypress_parse_packet(psmouse, cytp, &report_data))
> +		return;
> +
> +	if (cytp->mode & CYTP_BIT_ABS_MASK) {
> +		struct input_mt_pos pos[CYTP_MAX_CONTACTS];
> +		int slots[CYTP_MAX_MT_SLOTS];
> +		int n = report_data.contact_cnt;
> +		int ret;
> +
> +		if (n > CYTP_MAX_MT_SLOTS)
> +			n = CYTP_MAX_MT_SLOTS;
> +
> +		for (i = 0; i < n; i++) {
> +			contact = &report_data.contacts[i];
> +			pos[i].x = contact->x;
> +			pos[i].y = contact->y;
> +		}
> +
> +		ret = input_mt_assign_slots(input, slots, pos, n);
> +		if (ret < 0) {
> +			psmouse_err(psmouse,
> +			    "input_mt_assign_slots(%d) returned %d\n", n, ret);
> +			n = 0;
> +		}

There can be no error here, since you already checked the init_slots return code.

> +
> +		for (i = 0; i < n; i++) {
> +			contact = &report_data.contacts[i];
> +			slot = slots[i];
> +			input_mt_slot(input, slot);

slot[i] here instead.

> +			input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
> +			input_report_abs(input, ABS_MT_POSITION_X, contact->x);
> +			input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
> +			input_report_abs(input, ABS_MT_PRESSURE, contact->z);
> +		}
> +
> +		input_mt_sync_frame(input);
> +
> +	} else {
> +		if (report_data.contact_cnt == 1) {
> +			input_report_rel(input, REL_X, report_data.contacts[0].x);
> +			input_report_rel(input, REL_Y, report_data.contacts[0].y);
> +		}
> +
> +		input_report_rel(input, REL_WHEEL, report_data.vscroll);
> +		if (cytp->mode & CYTP_BIT_CYPRESS_REL)
> +			input_report_rel(input, REL_HWHEEL, report_data.hscroll);

And this seems unused.

> +	}
> +
> +	input_report_key(input, BTN_LEFT, report_data.left);
> +	input_report_key(input, BTN_RIGHT, report_data.right);
> +	input_report_key(input, BTN_MIDDLE, report_data.middle);
> +
> +	input_sync(input);
> +}
> +
> +static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
> +{
> +	int contact_cnt;
> +	int index = psmouse->pktcnt - 1;
> +	unsigned char *packet = psmouse->packet;
> +	struct cytp_data *cytp = psmouse->private;
> +
> +	if (index < 0 || index > cytp->pkt_size)
> +		return PSMOUSE_BAD_DATA;
> +
> +	if ((index == 0) && ((packet[0] & 0xfc) == 0)) {
> +		/* call packet process for reporting finger leave. */
> +		cypress_process_packet(psmouse, 1);
> +		return PSMOUSE_FULL_PACKET;
> +	}
> +
> +	if (cytp->mode & CYTP_BIT_ABS_MASK) {
> +		if (index == 0) {
> +			if ((packet[0] & 0x08) == 0x08)
> +				return PSMOUSE_BAD_DATA;
> +
> +			contact_cnt = cypress_get_finger_count(packet[0]);
> +
> +			if (contact_cnt > 5)
> +				return PSMOUSE_BAD_DATA;
> +
> +			if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) {
> +				CYTP_SET_PACKET_SIZE(4);
> +				if (contact_cnt == 2)
> +					CYTP_SET_PACKET_SIZE(7);
> +			} else {
> +				CYTP_SET_PACKET_SIZE(5);
> +				if (contact_cnt == 2)
> +					CYTP_SET_PACKET_SIZE(8);
> +			}
> +		}
> +
> +		return PSMOUSE_GOOD_DATA;
> +	} else {
> +		if (index == 0) {
> +			if ((packet[0] & 0x08) != 0x08)
> +				return PSMOUSE_BAD_DATA;
> +
> +			CYTP_SET_PACKET_SIZE(3);
> +			if (cytp->mode & CYTP_BIT_CYPRESS_REL)
> +				CYTP_SET_PACKET_SIZE(5);
> +		}
> +
> +		return PSMOUSE_GOOD_DATA;
> +	}
> +}
> +
> +static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +
> +	if (psmouse->pktcnt >= cytp->pkt_size) {
> +		cypress_process_packet(psmouse, 0);
> +		return PSMOUSE_FULL_PACKET;
> +	}
> +
> +	return cypress_validate_byte(psmouse);
> +}
> +
> +static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
> +{
> +	struct cytp_data *cytp = psmouse->private;
> +
> +	if (rate >= 80) {
> +		psmouse->rate = 80;
> +		CYTP_SET_MODE_BIT(CYTP_BIT_HIGH_RATE);
> +	} else {
> +		psmouse->rate = 40;
> +		CYTP_CLEAR_MODE_BIT(CYTP_BIT_HIGH_RATE);
> +	}
> +
> +	ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
> +		    PSMOUSE_CMD_SETRATE);
> +}
> +
> +static void cypress_disconnect(struct psmouse *psmouse)
> +{
> +	cypress_reset(psmouse);
> +	kfree(psmouse->private);
> +	psmouse->private = NULL;
> +}
> +
> +#if 0
> +/*
> + * FIXME: cypress_reconnect() never works...
> + * cypress_detect() always fails here.
> + */
> +static int cypress_reconnect(struct psmouse *psmouse)
> +{
> +	int tries = CYTP_PS2_CMD_TRIES;
> +	int rc;
> +
> +	do {
> +		cypress_reset(psmouse);
> +		rc = cypress_detect(psmouse, false);
> +	} while (rc && (--tries > 0));
> +
> +	if (rc) {
> +		psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
> +		return -1;
> +	}
> +
> +	if (cypress_query_hardware(psmouse)) {
> +		psmouse_err(psmouse, "Reconnect: unable to query Trackpad hardware.\n");
> +		return -1;
> +	}
> +
> +	if (cypress_set_absolute_mode(psmouse)) {
> +		psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +int cypress_init(struct psmouse *psmouse)
> +{
> +	struct cytp_data *cytp;
> +
> +	cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
> +	psmouse->private = (void *)cytp;
> +	if (cytp == NULL)
> +		return -ENOMEM;
> +
> +	cypress_reset(psmouse);
> +
> +	if (cypress_query_hardware(psmouse)) {
> +		psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
> +		goto err_exit;
> +	}
> +
> +	if (cypress_set_absolute_mode(psmouse)) {
> +		psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
> +		goto err_exit;
> +	}
> +
> +	if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
> +		psmouse_err(psmouse, "init: Unable to set input params.\n");
> +		goto err_exit;
> +	}
> +
> +	psmouse->model = 1;
> +	psmouse->protocol_handler = cypress_protocol_handler;
> +	psmouse->set_rate = cypress_set_rate;
> +	psmouse->disconnect = cypress_disconnect;
> +#if 0
> +	/* FIXME: cypress_reconnect() never works...
> +	 * just let psmouse re-init() us for now.
> +	 */
> +	psmouse->reconnect = cypress_reconnect;
> +#endif

This needs to be removed or fixed, of course.

> +	psmouse->cleanup = cypress_reset;
> +	psmouse->pktsize = 8;
> +	psmouse->resync_time = 0;
> +
> +	return 0;
> +
> +err_exit:
> +	/*
> +	 * Reset Cypress Trackpad as a standard mouse. Then
> +	 * let psmouse driver commmunicating with it as default PS2 mouse.
> +	 */
> +	cypress_reset(psmouse);
> +
> +	psmouse->private = NULL;
> +	kfree(cytp);
> +
> +	return -1;
> +}
> +
> +bool cypress_supported(void)
> +{
> +	return true;
> +}
> diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
> new file mode 100644
> index 0000000..ce70462
> --- /dev/null
> +++ b/drivers/input/mouse/cypress_ps2.h
> @@ -0,0 +1,219 @@
> +#ifndef _CYPRESS_PS2_H
> +#define _CYPRESS_PS2_H
> +
> +#include "psmouse.h"
> +
> +#define CMD_BITS_MASK 0x03
> +#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
> +
> +#define ENCODE_CMD(aa, bb, cc, dd) \
> +	(COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
> +#define CYTP_CMD_ABS_NO_PRESSURE_MODE       ENCODE_CMD(0, 1, 0, 0)
> +#define CYTP_CMD_ABS_WITH_PRESSURE_MODE     ENCODE_CMD(0, 1, 0, 1)
> +#define CYTP_CMD_SMBUS_MODE                 ENCODE_CMD(0, 1, 1, 0)
> +#define CYTP_CMD_STANDARD_MODE              ENCODE_CMD(0, 2, 0, 0)  /* not implemented yet. */
> +#define CYTP_CMD_CYPRESS_REL_MODE           ENCODE_CMD(1, 1, 1, 1)  /* not implemented yet. */
> +#define CYTP_CMD_READ_CYPRESS_ID            ENCODE_CMD(0, 0, 0, 0)
> +#define CYTP_CMD_READ_VITAL_STATISTICS      ENCODE_CMD(0, 0, 0, 1)
> +#define CYTP_CMD_SET_HSCROLL_WIDTH(w)       ENCODE_CMD(1, 1, 0, (w))
> +#define     CYTP_CMD_SET_HSCROLL_MASK       ENCODE_CMD(1, 1, 0, 0)
> +#define CYTP_CMD_SET_VSCROLL_WIDTH(w)       ENCODE_CMD(1, 2, 0, (w))
> +#define     CYTP_CMD_SET_VSCROLL_MASK       ENCODE_CMD(1, 2, 0, 0)
> +#define CYTP_CMD_SET_PALM_GEOMETRY(e)       ENCODE_CMD(1, 2, 1, (e))
> +#define     CYTP_CMD_PALM_GEMMETRY_MASK     ENCODE_CMD(1, 2, 1, 0)
> +#define CYTP_CMD_SET_PALM_SENSITIVITY(s)    ENCODE_CMD(1, 2, 2, (s))
> +#define     CYTP_CMD_PALM_SENSITIVITY_MASK  ENCODE_CMD(1, 2, 2, 0)
> +#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s)   ENCODE_CMD(1, 3, ((s) >> 2), (s))
> +#define     CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
> +#define CYTP_CMD_REQUEST_BASELINE_STATUS    ENCODE_CMD(2, 0, 0, 1)
> +#define CYTP_CMD_REQUEST_RECALIBRATION      ENCODE_CMD(2, 0, 0, 3)
> +
> +#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
> +#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
> +#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
> +#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
> +
> +/* Cypress trackpad working mode. */
> +#define CYTP_BIT_ABS_PRESSURE    (1 << 3)
> +#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
> +#define CYTP_BIT_CYPRESS_REL     (1 << 1)
> +#define CYTP_BIT_STANDARD_REL    (1 << 0)
> +#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
> +#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
> +#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
> +
> +#define CYTP_BIT_HIGH_RATE       (1 << 4)
> +/*
> + * report mode bit is set, firmware working in Remote Mode.
> + * report mode bit is cleared, firmware working in Stream Mode.
> + */
> +#define CYTP_BIT_REPORT_MODE     (1 << 5)
> +
> +/* scrolling width values for set HSCROLL and VSCROLL width command. */
> +#define SCROLL_WIDTH_NARROW 1
> +#define SCROLL_WIDTH_NORMAL 2
> +#define SCROLL_WIDTH_WIDE   3
> +
> +#define PALM_GEOMETRY_ENABLE  1
> +#define PALM_GEOMETRY_DISABLE 0
> +
> +#define CYPRESS_KEY_1 0x33
> +#define CYPRESS_KEY_2 0xCC
> +
> +#define VITAL_STATICS_MASK 0x80
> +#define FW_VERSION_MASX    0x7f
> +#define FW_VER_HIGH_MASK 0x70
> +#define FW_VER_LOW_MASK  0x0f
> +
> +/* Times to retry a ps2_command and millisecond delay between tries. */
> +#define CYTP_PS2_CMD_TRIES 3
> +#define CYTP_PS2_CMD_DELAY 500
> +
> +/* time out for PS/2 command only in milliseconds. */
> +#define CYTP_CMD_TIMEOUT  200
> +#define CYTP_DATA_TIMEOUT 30
> +
> +#define CYTP_EXT_CMD   0xe8
> +#define CYTP_PS2_RETRY 0xfe
> +#define CYTP_PS2_ERROR 0xfc
> +
> +#define CYTP_RESP_RETRY 0x01
> +#define CYTP_RESP_ERROR 0xfe
> +
> +
> +#define CYTP_105001_WIDTH  97   /* Dell XPS 13 */
> +#define CYTP_105001_HIGH   59
> +#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
> +#define CYTP_DEFAULT_HIGH  (CYTP_105001_HIGH)
> +
> +#define CYTP_ABS_MAX_X     1600
> +#define CYTP_ABS_MAX_Y     900
> +#define CYTP_MAX_PRESSURE  255
> +#define CYTP_MIN_PRESSURE  0
> +
> +/* header byte bits of relative package. */
> +#define BTN_LEFT_BIT   0x01
> +#define BTN_RIGHT_BIT  0x02
> +#define BTN_MIDDLE_BIT 0x04
> +#define REL_X_SIGN_BIT 0x10
> +#define REL_Y_SIGN_BIT 0x20
> +
> +/* header byte bits of absolute package. */
> +#define ABS_VSCROLL_BIT 0x10
> +#define ABS_HSCROLL_BIT 0x20
> +#define ABS_MULTIFINGER_TAP 0x04
> +#define ABS_EDGE_MOTION_MASK 0x80
> +
> +#define DFLT_RESP_BITS_VALID     0x88  /* SMBus bit should not be set. */
> +#define DFLT_RESP_SMBUS_BIT      0x80
> +#define   DFLT_SMBUS_MODE        0x80
> +#define   DFLT_PS2_MODE          0x00
> +#define DFLT_RESP_BIT_MODE       0x40
> +#define   DFLT_RESP_REMOTE_MODE  0x40
> +#define   DFLT_RESP_STREAM_MODE  0x00
> +#define DFLT_RESP_BIT_REPORTING  0x20
> +#define DFLT_RESP_BIT_SCALING    0x10
> +
> +#define VITAL_BIT_PALM               0x80
> +#define VITAL_BIT_STUBBORN           0x40
> +#define VITAL_BIT_2F_JITTER          0x30
> +#define VITAL_BIT_1F_JITTER          0x0c
> +#define VITAL_BIT_APA                0x02
> +#define VITAL_BIT_MTG                0x01
> +#define VITAL_BIT_ABS_PKT_FORMAT_SET 0xf0
> +#define VITAL_BIT_2F_SPIKE           0x0c
> +#define VITAL_BIT_1F_SPIKE           0x03
> +
> +/* bits of first byte response of E9h-Status Request command. */
> +#define RESP_BTN_RIGHT_BIT  0x01
> +#define RESP_BTN_MIDDLE_BIT 0x02
> +#define RESP_BTN_LEFT_BIT   0x04
> +#define RESP_SCALING_BIT    0x10
> +#define RESP_ENABLE_BIT     0x20
> +#define RESP_REMOTE_BIT     0x40
> +#define RESP_SMBUS_BIT      0x80
> +
> +#define CYTP_MAX_CONTACTS 5
> +#define CYTP_MAX_MT_SLOTS 2
> +
> +enum cytp_type {
> +	CYTP_STG,
> +	CYTP_MTG,
> +	CYTP_APA,
> +};
> +
> +struct cytp_contact {
> +	int x;
> +	int y;
> +	int z;  /* also named as touch pressure. */
> +};
> +
> +/* The structure of */

Hmm?

> +struct cytp_report_data {
> +	int contact_cnt;
> +	struct cytp_contact contacts[CYTP_MAX_CONTACTS];

Should be MAX_MT_SLOTS here.

> +	unsigned int left:1;
> +	unsigned int right:1;
> +	unsigned int middle:1;
> +	unsigned int tap:1;  /* multi-finger tap detected. */
> +	signed char vscroll;
> +	signed char hscroll;

scrolling unused?

> +};
> +
> +/* The structure of Cypress Trackpad device private data. */
> +struct cytp_data {
> +	int fw_version;
> +
> +	int pkt_size;
> +	int mode;
> +
> +	int scaling;
> +	int reporting;
> +
> +	int tp_min_pressure;
> +	int tp_max_pressure;
> +	int tp_width;  /* X direction physical size in mm. */
> +	int tp_high;  /* Y direction physical size in mm. */
> +	int tp_max_abs_x;  /* Max X absolution units can be reported. */
> +	int tp_max_abs_y;  /* Max Y absolution units can be reported. */

I do not think we are seeking absolution here. :-)

> +
> +	int tp_res_x;  /* X resolution in units/mm. */
> +	int tp_res_y;  /* Y resolution in units/mm. */
> +
> +	enum cytp_type tp_type;
> +	unsigned char tp_palm;
> +	unsigned char tp_stubborn;
> +	unsigned char tp_2f_jitter;
> +	unsigned char tp_1f_jitter;
> +	unsigned char tp_abs_packet_format_set;
> +	unsigned char tp_2f_spike;
> +	unsigned char tp_1f_spike;
> +
> +	int vital_statics_supported;
> +
> +	int prev_contact_cnt;
> +	int zero_packet_cnt;
> +	struct cytp_report_data prev_report_data;
> +};
> +
> +
> +#ifdef CONFIG_MOUSE_PS2_CYPRESS
> +int cypress_detect(struct psmouse *psmouse, bool set_properties);
> +int cypress_init(struct psmouse *psmouse);
> +bool cypress_supported(void);
> +#else
> +inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
> +{
> +	return -ENOSYS;
> +}
> +inline int cypress_init(struct psmouse *psmouse)
> +{
> +	return -ENOSYS;
> +}
> +inline bool cypress_supported(void)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_MOUSE_PS2_CYPRESS */
> +
> +#endif  /* _CYPRESS_PS2_H */
> -- 
> 1.7.10.4
> 

Thanks,
Henrik

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/4] Cypress PS/2 Trackpad driver
  2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
                   ` (4 preceding siblings ...)
  2012-12-03  7:36 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Dmitry Torokhov
@ 2012-12-03  7:50 ` Henrik Rydberg
  5 siblings, 0 replies; 17+ messages in thread
From: Henrik Rydberg @ 2012-12-03  7:50 UTC (permalink / raw)
  To: Kamal Mostafa
  Cc: linux-input, linux-kernel, Dmitry Torokhov, David Solda,
	Troy Abercrombia, Dudley Du, Cypress Semiconductor Corporation,
	Kyle Fazzari, Mario Limonciello, Tim Gardner, Herton Krzesinski

Hi Kamal,

>   Patch #2 (main driver), as recommended by Henrik Rydberg[2]:
>   - use input_mt_assign_slots; drop cypress_cal_finger_id.
>   - enable 2-finger-only SEMI_MT; drop cypress_simulate_fingers.
>   - various code clean-ups.
> 
>   Henrik, does patch #2 appear to properly use assign_slots and SEMI_MT as
>   you intended?  This SEMI_MT method does work (with 2 finger support only),
>   but I'm not clear why we wouldn't want to handle >2 fingers also, so ...

It looks pretty good, please see the patch comments for details. As
Dmitry said, one can report the number of fingers separately from the
two guiding contacts, and this semi-mt behavior is well supported in
userspace.

> 
>   Patch #4 (new) reintroduces simulated multitouch for up to 5 fingers
>   (#if CYPRESS_SIMULATE_MT), disabling SEMI_MT again.
> 
>   If that functionality (support for >2 fingers) can be acheived in some
>   better way, please advise.

Yes, as above, so this patch will not be needed.

Thanks,
Henrik

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-12-03  7:45   ` Henrik Rydberg
@ 2012-12-03 17:04     ` Dmitry Torokhov
  2012-12-05  2:22     ` Kamal Mostafa
  1 sibling, 0 replies; 17+ messages in thread
From: Dmitry Torokhov @ 2012-12-03 17:04 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Kamal Mostafa, linux-input, linux-kernel, David Solda,
	Troy Abercrombia, Dudley Du, Cypress Semiconductor Corporation,
	Kyle Fazzari, Mario Limonciello, Tim Gardner, Herton Krzesinski

On Mon, Dec 03, 2012 at 08:45:20AM +0100, Henrik Rydberg wrote:
> > +/*
> > + * reset trackpad device to standard relative mode.
> > + * This is also the defalut mode when trackpad powered on.
> > + */
> > +static void cypress_reset(struct psmouse *psmouse)
> > +{
> > +	struct cytp_data *cytp = psmouse->private;
> > +
> > +	psmouse_reset(psmouse);
> > +
> > +	CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
> > +	CYTP_SET_PACKET_SIZE(3);
> > +
> > +	cytp->prev_contact_cnt = 0;
> > +}
> 
> I suppose it is, but is it necessary to reset to default mode?

It most likely is, as quite a few BIOSes get very upset and refuse to
shutdown/suspend/resume if they find mouse (touchpad) in state other
than bog-standard PS/2 mode. So psmouse_reset() is needed, the rest is
not so much as the device supposed to be inactive from this point on.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 17+ messages in thread

* SEMI_MT vs. simulated mt (was Re: [PATCH v3 0/4] Cypress PS/2 Trackpad driver)
  2012-12-03  7:36 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Dmitry Torokhov
@ 2012-12-05  2:22   ` Kamal Mostafa
  0 siblings, 0 replies; 17+ messages in thread
From: Kamal Mostafa @ 2012-12-05  2:22 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg, Dudley Du
  Cc: linux-kernel, David Solda, Troy Abercrombia, Kyle Fazzari,
	Mario Limonciello, Herton Krzesinski, linux-input, Tim Gardner

Hi Dmitry and Henrik-

Regarding SEMI_MT vs. "simulated multitouch" . . .

> > 
> >   Patch #4 (new) reintroduces simulated multitouch for up to 5 fingers
> >   (#if CYPRESS_SIMULATE_MT), disabling SEMI_MT again.
> > 
> >   If that functionality (support for >2 fingers) can be acheived in some
> >   better way, please advise.


On Sun, 2012-12-02 at 23:36 -0800, Dmitry Torokhov wrote:
> You can still report true number of fingers on the pad via BTN_TOOL_*TAP
> while reporting the bounding box, the same way as Synaptics, Elantech,
> ALPS and Sentelic drivers are doing it.


On Mon, 2012-12-03 at 08:45 +0100, Henrik Rydberg wrote:
> +		ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
> +			INPUT_MT_POINTER|INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
> 
> Here, INPUT_MT_POINTER needs to be dropped; for semi-mt devices, the
> number of fingers is propagated separately using
> 
>    input_mt_report_finger_count(dev, finger_count_as_reported_by_the_device),
> 
> which conflicts with automatic handling of pointer logic.


Well...  The forthcoming PATCH v4 version now implements all that ^^ as
you requested, but it appears that my desktop (X with Unity desktop in
Ubuntu 12.10) does not actually respond to input_mt_report_finger_count
events (e.g. BTN_TOOL_TRIPLETAP).

I asked a colleague test a couple of other semi-mt touchpads (Synaptics
and ALPS) and we found the same result:  The desktop doesn't see >2
finger events with any of these semi-mt devices, even though
'input-events' does show that the BTN_TOOL_*TAP events are in the input
stream.  (Do other X desktops actually support BTN_TOOL_*TAP?)

I understand that this may be "a userspace problem", but from my
perspective the "simulated multitouch" patch is still necessary for >2
finger tap/drag.  I have left it in my PATCH v4 set for that reason
(patch 4/4; disabled by an ifdef).  It can be included and left
disabled, or enabled, or dropped as you see fit.

 -Kamal



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-12-03  7:45   ` Henrik Rydberg
  2012-12-03 17:04     ` Dmitry Torokhov
@ 2012-12-05  2:22     ` Kamal Mostafa
  2012-12-05  6:24       ` Dudley Du
  1 sibling, 1 reply; 17+ messages in thread
From: Kamal Mostafa @ 2012-12-05  2:22 UTC (permalink / raw)
  To: Henrik Rydberg, Dmitry Torokhov, Dudley Du
  Cc: linux-input, linux-kernel, David Solda, Troy Abercrombia,
	Kyle Fazzari, Mario Limonciello, Tim Gardner, Herton Krzesinski

Hi Henrik-

Thanks again for your review.  The forthcoming PATCH v4 includes the
majority of your change requests, except where noted below (and
considering my email "Subject: SEMI_MT vs. simulated mt")...


On Mon, 2012-12-03 at 08:45 +0100, Henrik Rydberg wrote:

> > +		/*
> > +		 * send extension command 0xE8 or 0xF3,
> > +		 * if send extension command failed,
> > +		 * try to send recovery command to make
> > +		 * trackpad device return to ready wait command state.
> > +		 * It alwasy success based on this recovery commands.
> 
> -EPARSE

I don't understand the meaning of that comment either.  Perhaps the
original author Dudley Du can explain it.


> > +static int cypress_read_vital_statistics(struct psmouse *psmouse)
> 
> Why do you call this statistics?

Perhaps "cypress_read_tp_metrics" would be a better name.  Any thoughts
on this, Dudley?


> > +/*
> > + * reset trackpad device to standard relative mode.
> > + * This is also the defalut mode when trackpad powered on.
> > + */
> > +static void cypress_reset(struct psmouse *psmouse)
> > +{
> > +	struct cytp_data *cytp = psmouse->private;
> > +
> > +	psmouse_reset(psmouse);
> > +
> > +	CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
> > +	CYTP_SET_PACKET_SIZE(3);
> > +
> > +	cytp->prev_contact_cnt = 0;
> > +}
> 
> I suppose it is, but is it necessary to reset to default mode?

I left this unchanged, as I think you and Dmitry agreed.


> > +
> > +static int cypress_set_input_params(struct input_dev *input,
> > +				    struct cytp_data *cytp)
> > +{
> > +	int ret;
> > +
> > +	if (cytp->mode & CYTP_BIT_ABS_MASK) {
> 
> It seems the device will always operate in this mode, so please simplify.

I #ifdef'd out the unused relative-mode code, as opposed to deleting it.
Will that be acceptable?


> > +		/* Remove HSCROLL bit */
> > +		if (report_data->contact_cnt == 4)
> > +			header_byte &= ~(ABS_HSCROLL_BIT);
> 
> Why conditionally?

Dudley, can you answer this?  I left this (and all the scroll code)
unchanged.


> > +#if 0
> > +	/* FIXME: cypress_reconnect() never works...
> > +	 * just let psmouse re-init() us for now.
> > +	 */
> > +	psmouse->reconnect = cypress_reconnect;
> > +#endif
> 
> This needs to be removed or fixed, of course.

Per Dudley, this does work in his disconnect/reconnect scenario, so I
re-enabled it.  We will continue to investigate why it fails
(harmlessly) in my suspend/resume scenario.


> > +	int tp_max_abs_x;  /* Max X absolution units can be reported. */
> > +	int tp_max_abs_y;  /* Max Y absolution units can be reported. */
> 
> I do not think we are seeking absolution here. :-)

Ha!  Speak for yourself, Henrik!  ;-)  I'm starting to feel like maybe I
should be seeking absolution here!


 -Kamal



^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver
  2012-12-05  2:22     ` Kamal Mostafa
@ 2012-12-05  6:24       ` Dudley Du
  0 siblings, 0 replies; 17+ messages in thread
From: Dudley Du @ 2012-12-05  6:24 UTC (permalink / raw)
  To: Kamal Mostafa, Henrik Rydberg, Dmitry Torokhov
  Cc: linux-input, linux-kernel, David Solda, Troy Abercrombia,
	Kyle Fazzari, Mario Limonciello, Tim Gardner, Herton Krzesinski,
	Dudley Du

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 4559 bytes --]

Hi Henrik, Kamal,

Thanks for your review.
And I add some comments in below.

Thanks.
Dudley Du
dudl@cypress.com

> Hi Henrik-
>
> Thanks again for your review.  The forthcoming PATCH v4 includes the majority of your change requests, except where noted below (and considering my email "Subject: SEMI_MT vs. simulated mt")...
>
>
> On Mon, 2012-12-03 at 08:45 +0100, Henrik Rydberg wrote:
>
> > > +         /*
> > > +          * send extension command 0xE8 or 0xF3,
> > > +          * if send extension command failed,
> > > +          * try to send recovery command to make
> > > +          * trackpad device return to ready wait command state.
> > > +          * It alwasy success based on this recovery commands.
> >
> > -EPARSE
>
> I don't understand the meaning of that comment either.  Perhaps the original author Dudley Du can explain it.

Since all trackpad specific commands are sent as extension command,
the format of extension command series is "0xE8 dd 0xE8 cc 0xE8 bb 0xE8".
And as we know, it may fail when sending the extension command series,
So this comment just want to indicates that if some unknown error occurred when sending the extension command,
this code will send recovery command to make trackpad firmware go back to ready state,
so same command can be tried to be re-sent.
Sorry for the bad comments causing confusion.

> > > +static int cypress_read_vital_statistics(struct psmouse *psmouse)
> >
> > Why do you call this statistics?
>
> Perhaps "cypress_read_tp_metrics" would be a better name.  Any thoughts on this, Dudley?
>
>
> > > +/*
> > > + * reset trackpad device to standard relative mode.
> > > + * This is also the defalut mode when trackpad powered on.
> > > + */
> > > +static void cypress_reset(struct psmouse *psmouse) {
> > > + struct cytp_data *cytp = psmouse->private;
> > > +
> > > + psmouse_reset(psmouse);
> > > +
> > > + CYTP_SET_MODE_BIT(CYTP_BIT_STANDARD_REL);
> > > + CYTP_SET_PACKET_SIZE(3);
> > > +
> > > + cytp->prev_contact_cnt = 0;
> > > +}
> >
> > I suppose it is, but is it necessary to reset to default mode?
>
> I left this unchanged, as I think you and Dmitry agreed.
>
>
> > > +
> > > +static int cypress_set_input_params(struct input_dev *input,
> > > +                             struct cytp_data *cytp)
> > > +{
> > > + int ret;
> > > +
> > > + if (cytp->mode & CYTP_BIT_ABS_MASK) {
> >
> > It seems the device will always operate in this mode, so please simplify.
>
> I #ifdef'd out the unused relative-mode code, as opposed to deleting it.
> Will that be acceptable?

I think relative-mode code should be reserved,
since for some unknown reason, trackpad may failed in absolution mode,
and in order to make trackpad device kept on usable,
we may set it to relative-mode,
so in this situation, the relative-mode code should be exist.


> > > +         /* Remove HSCROLL bit */
> > > +         if (report_data->contact_cnt == 4)
> > > +                 header_byte &= ~(ABS_HSCROLL_BIT);
> >
> > Why conditionally?
>
> Dudley, can you answer this?  I left this (and all the scroll code) unchanged.

Yes, this condition should be reserved.
Because ABS_HSCROLL_BIT is will reused to indicate finger numbers when finger number is bigger than 3.
And when the contact_cnt is 4, the ABS_HSCROLL_BIT is set to indicate 4 fingers touched, not HSCROLL BIT set.
But it will be confused with the flag of HSCROLL bit with one finger touched.
So we must clear this bit when contact_cnt is 4.


> > > +#if 0
> > > + /* FIXME: cypress_reconnect() never works...
> > > +  * just let psmouse re-init() us for now.
> > > +  */
> > > + psmouse->reconnect = cypress_reconnect; #endif
> >
> > This needs to be removed or fixed, of course.
>
> Per Dudley, this does work in his disconnect/reconnect scenario, so I re-enabled it.  We will continue to investigate why it fails
> (harmlessly) in my suspend/resume scenario.


> > > + int tp_max_abs_x;  /* Max X absolution units can be reported. */
> > > + int tp_max_abs_y;  /* Max Y absolution units can be reported. */
> >
> > I do not think we are seeking absolution here. :-)
>
> Ha!  Speak for yourself, Henrik!  ;-)  I'm starting to feel like maybe I should be seeking absolution here!
>
>
>  -Kamal

This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2012-12-05  6:24 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-29 21:57 [PATCH v3 0/4] Cypress PS/2 Trackpad driver Kamal Mostafa
2012-11-29 21:57 ` [PATCH v3 1/4] input: increase struct ps2dev cmdbuf[] to 8 bytes Kamal Mostafa
2012-11-29 21:57 ` [PATCH v3 2/4] input: Cypress PS/2 Trackpad psmouse driver Kamal Mostafa
2012-12-03  3:20   ` Dudley Du
2012-12-03  5:57   ` Dudley Du
2012-12-03  6:31     ` Henrik Rydberg
2012-12-03  6:31       ` Dudley Du
2012-12-03  7:45   ` Henrik Rydberg
2012-12-03 17:04     ` Dmitry Torokhov
2012-12-05  2:22     ` Kamal Mostafa
2012-12-05  6:24       ` Dudley Du
2012-11-29 21:58 ` [PATCH v3 3/4] input: Cypress PS/2 Trackpad link into psmouse-base Kamal Mostafa
2012-11-29 21:58 ` [PATCH v3 4/4] input: Cypress PS/2 Trackpad simulated multitouch Kamal Mostafa
2012-12-03  1:58   ` Dudley Du
2012-12-03  7:36 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Dmitry Torokhov
2012-12-05  2:22   ` SEMI_MT vs. simulated mt (was Re: [PATCH v3 0/4] Cypress PS/2 Trackpad driver) Kamal Mostafa
2012-12-03  7:50 ` [PATCH v3 0/4] Cypress PS/2 Trackpad driver Henrik Rydberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).