diff mbox

Input: elantech - Add v3 hardware support

Message ID 1313589793-31184-1-git-send-email-seth.forshee@canonical.com (mailing list archive)
State New, archived
Headers show

Commit Message

Seth Forshee Aug. 17, 2011, 2:03 p.m. UTC
Adds basic v3 hardware support for newer devices not currently
supported by the driver.

Thanks to Tom Lin <tom_lin@emc.com.tw> and Mark A. Stratman
<stratman@gmail.com>, whose work on a previous patch revealed many
of the details of the v3 protocol.

Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
---
 drivers/input/mouse/elantech.c |  255 ++++++++++++++++++++++++++++++++++------
 drivers/input/mouse/elantech.h |    4 +
 2 files changed, 223 insertions(+), 36 deletions(-)

Comments

Dmitry Torokhov Aug. 17, 2011, 5:31 p.m. UTC | #1
Hi Seth,

On Wed, Aug 17, 2011 at 09:03:13AM -0500, Seth Forshee wrote:
> Adds basic v3 hardware support for newer devices not currently
> supported by the driver.
> 
> Thanks to Tom Lin <tom_lin@emc.com.tw> and Mark A. Stratman
> <stratman@gmail.com>, whose work on a previous patch revealed many
> of the details of the v3 protocol.

Excellent work, thank you.

Apparently Elantech is now also interested in supporting ther devices
on Linux and is preparing some patches adding support for the new
hardware so it woudl be great if we coordinated efforts. I am CC-ing
JJ Ding and Aaron Huang...

Thanks.

> 
> Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
> ---
>  drivers/input/mouse/elantech.c |  255 ++++++++++++++++++++++++++++++++++------
>  drivers/input/mouse/elantech.h |    4 +
>  2 files changed, 223 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
> index 3250356..e708a75 100644
> --- a/drivers/input/mouse/elantech.c
> +++ b/drivers/input/mouse/elantech.c
> @@ -22,6 +22,10 @@
>  #include "psmouse.h"
>  #include "elantech.h"
>  
> +#define FW_VERSION_MAJOR_MASK	0xff0000
> +#define FW_VERSION_MINOR_MASK	0x00ff00
> +#define FW_VERSION_MICRO_MASK	0x0000ff
> +
>  #define elantech_debug(fmt, ...)					\
>  	do {								\
>  		if (etd->debug)						\
> @@ -82,6 +86,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
>  {
>  	struct elantech_data *etd = psmouse->private;
>  	unsigned char param[3];
> +	unsigned char command;
>  	int rc = 0;
>  
>  	if (reg < 0x10 || reg > 0x26)
> @@ -90,9 +95,11 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
>  	if (reg > 0x11 && reg < 0x20)
>  		return -1;
>  
> +	command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_READ;
> +
>  	switch (etd->hw_version) {
>  	case 1:
> -		if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
> +		if (psmouse_sliced_command(psmouse, command) ||
>  		    psmouse_sliced_command(psmouse, reg) ||
>  		    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
>  			rc = -1;
> @@ -100,8 +107,9 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
>  		break;
>  
>  	case 2:
> +	case 3:
>  		if (elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
> -		    elantech_ps2_command(psmouse,  NULL, ETP_REGISTER_READ) ||
> +		    elantech_ps2_command(psmouse,  NULL, command) ||
>  		    elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
>  		    elantech_ps2_command(psmouse,  NULL, reg) ||
>  		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
> @@ -125,6 +133,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
>  				unsigned char val)
>  {
>  	struct elantech_data *etd = psmouse->private;
> +	unsigned char command;
>  	int rc = 0;
>  
>  	if (reg < 0x10 || reg > 0x26)
> @@ -133,9 +142,11 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
>  	if (reg > 0x11 && reg < 0x20)
>  		return -1;
>  
> +	command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_WRITE;
> +
>  	switch (etd->hw_version) {
>  	case 1:
> -		if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
> +		if (psmouse_sliced_command(psmouse, command) ||
>  		    psmouse_sliced_command(psmouse, reg) ||
>  		    psmouse_sliced_command(psmouse, val) ||
>  		    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
> @@ -145,7 +156,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
>  
>  	case 2:
>  		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
> -		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
> +		    elantech_ps2_command(psmouse, NULL, command) ||
>  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
>  		    elantech_ps2_command(psmouse, NULL, reg) ||
>  		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
> @@ -164,6 +175,41 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
>  }
>  
>  /*
> + * Set the elantech touchpad mode byte via special commands
> + */
> +static int elantech_mode_command(struct psmouse *psmouse, unsigned char reg,
> +				unsigned char val)
> +{
> +	struct elantech_data *etd = psmouse->private;
> +	unsigned char param[3];
> +	int write_cmd, read_cmd;
> +
> +	if (etd->hw_version == 3) {
> +		write_cmd = ETP_REGISTER_RW;
> +		read_cmd = ETP_REGISTER_RW;
> +	} else {
> +		write_cmd = ETP_REGISTER_READ;
> +		read_cmd = ETP_REGISTER_WRITE;
> +	}
> +
> +	if (psmouse_sliced_command(psmouse, write_cmd) ||
> +	    psmouse_sliced_command(psmouse, reg) ||
> +	    psmouse_sliced_command(psmouse, val) ||
> +	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
> +		return -1;
> +
> +	if (psmouse_sliced_command(psmouse, read_cmd) ||
> +	    psmouse_sliced_command(psmouse, reg) ||
> +	    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
> +		return -1;
> +
> +	if (val != param[0])
> +		return -1;
> +
> +	return 0;
> +}
> +
> +/*
>   * Dump a complete mouse movement packet to the syslog
>   */
>  static void elantech_packet_dump(unsigned char *packet, int size)
> @@ -223,7 +269,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
>  		input_report_abs(dev, ABS_X,
>  			((packet[1] & 0x0c) << 6) | packet[2]);
>  		input_report_abs(dev, ABS_Y,
> -			ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3]));
> +			etd->y_max - (((packet[1] & 0x03) << 8) | packet[3]));
>  	}
>  
>  	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
> @@ -298,7 +344,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
>  		 * byte 4:  .   .   .   .   .   .  y9  y8
>  		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
>  		 */
> -		y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
> +		y1 = etd->y_max - (((packet[4] & 0x03) << 8) | packet[5]);
>  
>  		input_report_abs(dev, ABS_X, x1);
>  		input_report_abs(dev, ABS_Y, y1);
> @@ -316,14 +362,14 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
>  		 */
>  		x1 = ((packet[0] & 0x10) << 4) | packet[1];
>  		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
> -		y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
> +		y1 = etd->y_max_2ft - (((packet[0] & 0x20) << 3) | packet[2]);
>  		/*
>  		 * byte 3:  .   .  by8 bx8  .   .   .   .
>  		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
>  		 */
>  		x2 = ((packet[3] & 0x10) << 4) | packet[4];
>  		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
> -		y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
> +		y2 = etd->y_max_2ft - (((packet[3] & 0x20) << 3) | packet[5]);
>  		/*
>  		 * For compatibility with the X Synaptics driver scale up
>  		 * one coordinate and report as ordinary mouse movent
> @@ -352,6 +398,83 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
>  	input_sync(dev);
>  }
>  
> +/*
> + * Interpret complete data packets and report absolute mode input events for
> + * hardware version 3. (6 byte packets)
> + */
> +static psmouse_ret_t elantech_report_absolute_v3(struct psmouse *psmouse)
> +{
> +	struct elantech_data *etd = psmouse->private;
> +	struct input_dev *dev = psmouse->dev;
> +	unsigned char *packet1 = psmouse->packet;
> +	unsigned char *packet2 = NULL;
> +	unsigned int fingers, width = 0, pres = 0;
> +	unsigned int x1, y1, x2 = 0, y2 = 0;
> +
> +	/* byte 0: n1  n0   .   .   .   .   R   L */
> +	fingers = (packet1[0] & 0xc0) >> 6;
> +
> +	/*
> +	 * Need to receive two packets for 2-touch events. If this
> +	 * is the first packet, store it away and wait for the second.
> +	 * We don't support parity in v3, so use that storage.
> +	 */
> +	if (fingers == 2 && (packet1[0] & 0x0c) == 0x04) {
> +		memcpy(etd->parity, packet1, psmouse->pktsize);
> +		return PSMOUSE_FULL_PACKET;
> +	}
> +
> +	/*
> +	 * If this the second packet of a two-fingered touch event we
> +	 * need to process the previous packet stashed away in
> +	 * etd->parity
> +	 */
> +	if (fingers == 2) {
> +		packet1 = etd->parity;
> +		packet2 = psmouse->packet;
> +	}
> +
> +	x1 = ((packet1[1] & 0x0f) << 8) | packet1[2];
> +	y1 = etd->y_max - (((packet1[4] & 0x0f) << 8) | packet1[5]);
> +
> +	if (packet2) {
> +		x2 = ((packet2[1] & 0x0f) << 8) | packet2[2];
> +		y2 = etd->y_max - (((packet2[4] & 0x0f) << 8) | packet2[5]);
> +	}
> +
> +	/*
> +	 * Reject crazy values we sometimes get from the hardware
> +	 */
> +	if (fingers != 0 && (x1 > etd->x_max || y1 > etd->y_max))
> +		return PSMOUSE_FULL_PACKET;
> +
> +	pres = (packet1[1] & 0xf0) | ((packet1[4] & 0xf0) >> 4);
> +	width = ((packet1[0] & 0x30) >> 2) | ((packet1[3] & 0x30) >> 4);
> +
> +	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
> +
> +	input_report_key(dev, BTN_TOUCH, fingers != 0);
> +	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
> +	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
> +	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
> +
> +	if (etd->reports_pressure) {
> +		input_report_abs(dev, ABS_PRESSURE, pres);
> +		input_report_abs(dev, ABS_TOOL_WIDTH, width);
> +	}
> +
> +	if (fingers != 0) {
> +		input_report_abs(dev, ABS_X, x1);
> +		input_report_abs(dev, ABS_Y, y1);
> +	}
> +
> +	input_report_key(dev, BTN_LEFT, packet1[0] & 0x01);
> +	input_report_key(dev, BTN_RIGHT, packet1[0] & 0x02);
> +
> +	input_sync(dev);
> +	return PSMOUSE_FULL_PACKET;
> +}
> +
>  static int elantech_check_parity_v1(struct psmouse *psmouse)
>  {
>  	struct elantech_data *etd = psmouse->private;
> @@ -401,6 +524,9 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
>  		/* We don't know how to check parity in protocol v2 */
>  		elantech_report_absolute_v2(psmouse);
>  		break;
> +	case 3:
> +		/* We don't know how to check parity in protocol v3 */
> +		return elantech_report_absolute_v3(psmouse);
>  	}
>  
>  	return PSMOUSE_FULL_PACKET;
> @@ -433,13 +559,16 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
>  		etd->reg_21 = 0x60;	/* 0x00 */
>  		if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
>  		    elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
> -		    elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
> +		    elantech_write_reg(psmouse, 0x21, etd->reg_21))
>  			rc = -1;
> -			break;
> -		}
> +		break;
> +	case 3:
> +		etd->reg_10 = 0x0f;
> +		rc = elantech_mode_command(psmouse, 0x10, etd->reg_10);
> +		break;
>  	}
>  
> -	if (rc == 0) {
> +	if (etd->hw_version != 3 && rc == 0) {
>  		/*
>  		 * Read back reg 0x10. For hardware version 1 we must make
>  		 * sure the absolute mode bit is set. For hardware version 2
> @@ -473,10 +602,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
>  /*
>   * Set the appropriate event bits for the input subsystem
>   */
> -static void elantech_set_input_params(struct psmouse *psmouse)
> +static int elantech_set_input_params(struct psmouse *psmouse)
>  {
>  	struct input_dev *dev = psmouse->dev;
>  	struct elantech_data *etd = psmouse->private;
> +	unsigned char param[3];
>  
>  	__set_bit(EV_KEY, dev->evbit);
>  	__set_bit(EV_ABS, dev->evbit);
> @@ -492,20 +622,41 @@ static void elantech_set_input_params(struct psmouse *psmouse)
>  
>  	switch (etd->hw_version) {
>  	case 1:
> +		etd->x_max = ETP_XMAX_V1;
> +		etd->y_max = ETP_YMAX_V1;
> +
>  		/* Rocker button */
>  		if (etd->fw_version < 0x020000 &&
>  		    (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
>  			__set_bit(BTN_FORWARD, dev->keybit);
>  			__set_bit(BTN_BACK, dev->keybit);
>  		}
> -		input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0);
> -		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0);
> +		input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, etd->x_max, 0, 0);
> +		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, etd->y_max, 0, 0);
>  		break;
>  
>  	case 2:
> +	case 3:
> +		if (etd->hw_version == 3) {
> +			if (ps2_command(&psmouse->ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
> +			    ps2_command(&psmouse->ps2dev, NULL, 0x00) ||
> +			    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
> +				return -1;
> +
> +			etd->x_max = (param[0] & 0xf) << 8 | param[1];
> +			etd->y_max = (param[0] & 0xf0) << 4 | param[2];
> +			etd->y_max_2ft = etd->y_max;
> +			elantech_debug("x_max = %d, y_max = %d\n",
> +				       etd->x_max, etd->y_max);
> +		} else {
> +			etd->x_max = ETP_XMAX_V2;
> +			etd->y_max = ETP_YMAX_V2;
> +			etd->y_max_2ft = ETP_2FT_YMAX;
> +		}
> +
>  		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
> -		input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
> -		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
> +		input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, etd->x_max, 0, 0);
> +		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, etd->y_max, 0, 0);
>  		if (etd->reports_pressure) {
>  			input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
>  					     ETP_PMAX_V2, 0, 0);
> @@ -514,10 +665,12 @@ static void elantech_set_input_params(struct psmouse *psmouse)
>  		}
>  		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
>  		input_mt_init_slots(dev, 2);
> -		input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
> -		input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
> +		input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, etd->x_max, 0, 0);
> +		input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, etd->y_max, 0, 0);
>  		break;
>  	}
> +
> +	return 0;
>  }
>  
>  struct elantech_attr_data {
> @@ -639,10 +792,17 @@ static bool elantech_is_signature_valid(const unsigned char *param)
>  /*
>   * Use magic knock to detect Elantech touchpad
>   */
> +
> +static const char elantech_magic_knocks[][3] = {
> +	{ '\x3c', '\x03', '\xc8' },
> +	{ '\x3c', '\x03', '\x00' },
> +};
> +
>  int elantech_detect(struct psmouse *psmouse, bool set_properties)
>  {
>  	struct ps2dev *ps2dev = &psmouse->ps2dev;
>  	unsigned char param[3];
> +	int i;
>  
>  	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
>  
> @@ -655,11 +815,15 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
>  		return -1;
>  	}
>  
> -	/*
> -	 * Report this in case there are Elantech models that use a different
> -	 * set of magic numbers
> -	 */
> -	if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
> +	for (i = 0; i < ARRAY_SIZE(elantech_magic_knocks); i++) {
> +		if (!memcmp(param, elantech_magic_knocks[i], sizeof(param)))
> +			break;
> +	}
> +	if (i >= ARRAY_SIZE(elantech_magic_knocks)) {
> +		/*
> +		 * Report this in case there are Elantech models that use a
> +		 * different set of magic numbers
> +		 */
>  		pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
>  			 param[0], param[1], param[2]);
>  		return -1;
> @@ -749,23 +913,38 @@ int elantech_init(struct psmouse *psmouse)
>  
>  	etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
>  
> -	/*
> -	 * Assume every version greater than this is new EeePC style
> -	 * hardware with 6 byte packets
> -	 */
> -	if (etd->fw_version >= 0x020030) {
> +	if (etd->fw_version < 0x020030) {
> +		etd->hw_version = 1;
> +		etd->paritycheck = 1;
> +	} else if ((etd->fw_version & ~FW_VERSION_MICRO_MASK) <= 0x140000) {
>  		etd->hw_version = 2;
>  		/* For now show extra debug information */
>  		etd->debug = 1;
> -		/* Don't know how to do parity checking for version 2 */
> -		etd->paritycheck = 0;
>  
>  		if (etd->fw_version >= 0x020800)
>  			etd->reports_pressure = true;
> -
>  	} else {
> -		etd->hw_version = 1;
> -		etd->paritycheck = 1;
> +		unsigned char dummy[3];
> +		error = 0;
> +		if (ps2_command(&psmouse->ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
> +		    ps2_command(&psmouse->ps2dev, NULL, 0x01) ||
> +		    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
> +			error = -1;
> +		else if (
> +		    ps2_command(&psmouse->ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
> +		    ps2_command(&psmouse->ps2dev, NULL, 0x04) ||
> +		    ps2_command(&psmouse->ps2dev, dummy, PSMOUSE_CMD_GETINFO))
> +			error = -1;
> +
> +		if (!error &&
> +		    (param[0] & 0x0f) >= 5 && (param[1] & 0x0f) >= 0x06) {
> +			etd->hw_version = 3;
> +			etd->debug = 1;
> +			etd->reports_pressure = true;
> +		} else {
> +			pr_debug("did not detect elantech firmware version\n");
> +			goto init_fail;
> +		}
>  	}
>  
>  	pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
> @@ -794,7 +973,11 @@ int elantech_init(struct psmouse *psmouse)
>  		goto init_fail;
>  	}
>  
> -	elantech_set_input_params(psmouse);
> +	error = elantech_set_input_params(psmouse);
> +	if (error) {
> +		pr_err("failed to set input parameters\n");
> +		goto init_fail;
> +	}
>  
>  	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
>  				   &elantech_attr_group);
> @@ -806,7 +989,7 @@ int elantech_init(struct psmouse *psmouse)
>  	psmouse->protocol_handler = elantech_process_byte;
>  	psmouse->disconnect = elantech_disconnect;
>  	psmouse->reconnect = elantech_reconnect;
> -	psmouse->pktsize = etd->hw_version == 2 ? 6 : 4;
> +	psmouse->pktsize = etd->hw_version == 1 ? 4 : 6;
>  
>  	return 0;
>  
> diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
> index fabb2b9..b896cc8 100644
> --- a/drivers/input/mouse/elantech.h
> +++ b/drivers/input/mouse/elantech.h
> @@ -22,6 +22,7 @@
>  /*
>   * Command values for register reading or writing
>   */
> +#define ETP_REGISTER_RW			0x00
>  #define ETP_REGISTER_READ		0x10
>  #define ETP_REGISTER_WRITE		0x11
>  
> @@ -110,6 +111,9 @@ struct elantech_data {
>  	bool reports_pressure;
>  	unsigned char hw_version;
>  	unsigned int fw_version;
> +	unsigned int x_max;
> +	unsigned int y_max;
> +	unsigned int y_max_2ft;
>  	unsigned int single_finger_reports;
>  	unsigned char parity[256];
>  };
> -- 
> 1.7.4.1
>
Seth Forshee Aug. 17, 2011, 6:35 p.m. UTC | #2
On Wed, Aug 17, 2011 at 10:31:11AM -0700, Dmitry Torokhov wrote:
> Hi Seth,
> 
> On Wed, Aug 17, 2011 at 09:03:13AM -0500, Seth Forshee wrote:
> > Adds basic v3 hardware support for newer devices not currently
> > supported by the driver.
> > 
> > Thanks to Tom Lin <tom_lin@emc.com.tw> and Mark A. Stratman
> > <stratman@gmail.com>, whose work on a previous patch revealed many
> > of the details of the v3 protocol.
> 
> Excellent work, thank you.
> 
> Apparently Elantech is now also interested in supporting ther devices
> on Linux and is preparing some patches adding support for the new
> hardware so it woudl be great if we coordinated efforts. I am CC-ing
> JJ Ding and Aaron Huang...

Sure, I'd be happy to coordinate.

Where does that leave this patch? Do you want to take it now and
integrate improvments from working with Elantech as they come, or hold
off? I'd personally like to see this patch get merged unless there are
objections to it, as it works well and gives us at least some support
for these touchpads right now. It's been tested so far on the following
laptop models:

  Samsung NF210
  Samsung NF310
  Samsung QX410
  Asus X93S

It would certainly be great to get it reviewed by the folks from
Elantech to make sure all the details of the hardware interaction are
correct though.

Thanks,
Seth
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
JJ Ding Aug. 18, 2011, 1:38 a.m. UTC | #3
Hi Seth,

Thank you for your work.
I am preparing my patches for community review, and will submit very
soon.
Looking forward to your feedback.

Thanks.

jj

On Wed, 17 Aug 2011 13:35:44 -0500, Seth Forshee <seth.forshee@canonical.com> wrote:
> On Wed, Aug 17, 2011 at 10:31:11AM -0700, Dmitry Torokhov wrote:
> > Hi Seth,
> > 
> > On Wed, Aug 17, 2011 at 09:03:13AM -0500, Seth Forshee wrote:
> > > Adds basic v3 hardware support for newer devices not currently
> > > supported by the driver.
> > > 
> > > Thanks to Tom Lin <tom_lin@emc.com.tw> and Mark A. Stratman
> > > <stratman@gmail.com>, whose work on a previous patch revealed many
> > > of the details of the v3 protocol.
> > 
> > Excellent work, thank you.
> > 
> > Apparently Elantech is now also interested in supporting ther devices
> > on Linux and is preparing some patches adding support for the new
> > hardware so it woudl be great if we coordinated efforts. I am CC-ing
> > JJ Ding and Aaron Huang...
> 
> Sure, I'd be happy to coordinate.
> 
> Where does that leave this patch? Do you want to take it now and
> integrate improvments from working with Elantech as they come, or hold
> off? I'd personally like to see this patch get merged unless there are
> objections to it, as it works well and gives us at least some support
> for these touchpads right now. It's been tested so far on the following
> laptop models:
> 
>   Samsung NF210
>   Samsung NF310
>   Samsung QX410
>   Asus X93S
> 
> It would certainly be great to get it reviewed by the folks from
> Elantech to make sure all the details of the hardware interaction are
> correct though.
> 
> Thanks,
> Seth
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 3250356..e708a75 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -22,6 +22,10 @@ 
 #include "psmouse.h"
 #include "elantech.h"
 
+#define FW_VERSION_MAJOR_MASK	0xff0000
+#define FW_VERSION_MINOR_MASK	0x00ff00
+#define FW_VERSION_MICRO_MASK	0x0000ff
+
 #define elantech_debug(fmt, ...)					\
 	do {								\
 		if (etd->debug)						\
@@ -82,6 +86,7 @@  static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
 {
 	struct elantech_data *etd = psmouse->private;
 	unsigned char param[3];
+	unsigned char command;
 	int rc = 0;
 
 	if (reg < 0x10 || reg > 0x26)
@@ -90,9 +95,11 @@  static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
 	if (reg > 0x11 && reg < 0x20)
 		return -1;
 
+	command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_READ;
+
 	switch (etd->hw_version) {
 	case 1:
-		if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
+		if (psmouse_sliced_command(psmouse, command) ||
 		    psmouse_sliced_command(psmouse, reg) ||
 		    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
 			rc = -1;
@@ -100,8 +107,9 @@  static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
 		break;
 
 	case 2:
+	case 3:
 		if (elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
-		    elantech_ps2_command(psmouse,  NULL, ETP_REGISTER_READ) ||
+		    elantech_ps2_command(psmouse,  NULL, command) ||
 		    elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
 		    elantech_ps2_command(psmouse,  NULL, reg) ||
 		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
@@ -125,6 +133,7 @@  static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
 				unsigned char val)
 {
 	struct elantech_data *etd = psmouse->private;
+	unsigned char command;
 	int rc = 0;
 
 	if (reg < 0x10 || reg > 0x26)
@@ -133,9 +142,11 @@  static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
 	if (reg > 0x11 && reg < 0x20)
 		return -1;
 
+	command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_WRITE;
+
 	switch (etd->hw_version) {
 	case 1:
-		if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
+		if (psmouse_sliced_command(psmouse, command) ||
 		    psmouse_sliced_command(psmouse, reg) ||
 		    psmouse_sliced_command(psmouse, val) ||
 		    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
@@ -145,7 +156,7 @@  static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
 
 	case 2:
 		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
-		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
+		    elantech_ps2_command(psmouse, NULL, command) ||
 		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
 		    elantech_ps2_command(psmouse, NULL, reg) ||
 		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
@@ -164,6 +175,41 @@  static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
 }
 
 /*
+ * Set the elantech touchpad mode byte via special commands
+ */
+static int elantech_mode_command(struct psmouse *psmouse, unsigned char reg,
+				unsigned char val)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char param[3];
+	int write_cmd, read_cmd;
+
+	if (etd->hw_version == 3) {
+		write_cmd = ETP_REGISTER_RW;
+		read_cmd = ETP_REGISTER_RW;
+	} else {
+		write_cmd = ETP_REGISTER_READ;
+		read_cmd = ETP_REGISTER_WRITE;
+	}
+
+	if (psmouse_sliced_command(psmouse, write_cmd) ||
+	    psmouse_sliced_command(psmouse, reg) ||
+	    psmouse_sliced_command(psmouse, val) ||
+	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+		return -1;
+
+	if (psmouse_sliced_command(psmouse, read_cmd) ||
+	    psmouse_sliced_command(psmouse, reg) ||
+	    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+
+	if (val != param[0])
+		return -1;
+
+	return 0;
+}
+
+/*
  * Dump a complete mouse movement packet to the syslog
  */
 static void elantech_packet_dump(unsigned char *packet, int size)
@@ -223,7 +269,7 @@  static void elantech_report_absolute_v1(struct psmouse *psmouse)
 		input_report_abs(dev, ABS_X,
 			((packet[1] & 0x0c) << 6) | packet[2]);
 		input_report_abs(dev, ABS_Y,
-			ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3]));
+			etd->y_max - (((packet[1] & 0x03) << 8) | packet[3]));
 	}
 
 	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
@@ -298,7 +344,7 @@  static void elantech_report_absolute_v2(struct psmouse *psmouse)
 		 * byte 4:  .   .   .   .   .   .  y9  y8
 		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
 		 */
-		y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
+		y1 = etd->y_max - (((packet[4] & 0x03) << 8) | packet[5]);
 
 		input_report_abs(dev, ABS_X, x1);
 		input_report_abs(dev, ABS_Y, y1);
@@ -316,14 +362,14 @@  static void elantech_report_absolute_v2(struct psmouse *psmouse)
 		 */
 		x1 = ((packet[0] & 0x10) << 4) | packet[1];
 		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
-		y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
+		y1 = etd->y_max_2ft - (((packet[0] & 0x20) << 3) | packet[2]);
 		/*
 		 * byte 3:  .   .  by8 bx8  .   .   .   .
 		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
 		 */
 		x2 = ((packet[3] & 0x10) << 4) | packet[4];
 		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
-		y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
+		y2 = etd->y_max_2ft - (((packet[3] & 0x20) << 3) | packet[5]);
 		/*
 		 * For compatibility with the X Synaptics driver scale up
 		 * one coordinate and report as ordinary mouse movent
@@ -352,6 +398,83 @@  static void elantech_report_absolute_v2(struct psmouse *psmouse)
 	input_sync(dev);
 }
 
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 3. (6 byte packets)
+ */
+static psmouse_ret_t elantech_report_absolute_v3(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	struct input_dev *dev = psmouse->dev;
+	unsigned char *packet1 = psmouse->packet;
+	unsigned char *packet2 = NULL;
+	unsigned int fingers, width = 0, pres = 0;
+	unsigned int x1, y1, x2 = 0, y2 = 0;
+
+	/* byte 0: n1  n0   .   .   .   .   R   L */
+	fingers = (packet1[0] & 0xc0) >> 6;
+
+	/*
+	 * Need to receive two packets for 2-touch events. If this
+	 * is the first packet, store it away and wait for the second.
+	 * We don't support parity in v3, so use that storage.
+	 */
+	if (fingers == 2 && (packet1[0] & 0x0c) == 0x04) {
+		memcpy(etd->parity, packet1, psmouse->pktsize);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	/*
+	 * If this the second packet of a two-fingered touch event we
+	 * need to process the previous packet stashed away in
+	 * etd->parity
+	 */
+	if (fingers == 2) {
+		packet1 = etd->parity;
+		packet2 = psmouse->packet;
+	}
+
+	x1 = ((packet1[1] & 0x0f) << 8) | packet1[2];
+	y1 = etd->y_max - (((packet1[4] & 0x0f) << 8) | packet1[5]);
+
+	if (packet2) {
+		x2 = ((packet2[1] & 0x0f) << 8) | packet2[2];
+		y2 = etd->y_max - (((packet2[4] & 0x0f) << 8) | packet2[5]);
+	}
+
+	/*
+	 * Reject crazy values we sometimes get from the hardware
+	 */
+	if (fingers != 0 && (x1 > etd->x_max || y1 > etd->y_max))
+		return PSMOUSE_FULL_PACKET;
+
+	pres = (packet1[1] & 0xf0) | ((packet1[4] & 0xf0) >> 4);
+	width = ((packet1[0] & 0x30) >> 2) | ((packet1[3] & 0x30) >> 4);
+
+	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+
+	input_report_key(dev, BTN_TOUCH, fingers != 0);
+	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+
+	if (etd->reports_pressure) {
+		input_report_abs(dev, ABS_PRESSURE, pres);
+		input_report_abs(dev, ABS_TOOL_WIDTH, width);
+	}
+
+	if (fingers != 0) {
+		input_report_abs(dev, ABS_X, x1);
+		input_report_abs(dev, ABS_Y, y1);
+	}
+
+	input_report_key(dev, BTN_LEFT, packet1[0] & 0x01);
+	input_report_key(dev, BTN_RIGHT, packet1[0] & 0x02);
+
+	input_sync(dev);
+	return PSMOUSE_FULL_PACKET;
+}
+
 static int elantech_check_parity_v1(struct psmouse *psmouse)
 {
 	struct elantech_data *etd = psmouse->private;
@@ -401,6 +524,9 @@  static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
 		/* We don't know how to check parity in protocol v2 */
 		elantech_report_absolute_v2(psmouse);
 		break;
+	case 3:
+		/* We don't know how to check parity in protocol v3 */
+		return elantech_report_absolute_v3(psmouse);
 	}
 
 	return PSMOUSE_FULL_PACKET;
@@ -433,13 +559,16 @@  static int elantech_set_absolute_mode(struct psmouse *psmouse)
 		etd->reg_21 = 0x60;	/* 0x00 */
 		if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
 		    elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
-		    elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
+		    elantech_write_reg(psmouse, 0x21, etd->reg_21))
 			rc = -1;
-			break;
-		}
+		break;
+	case 3:
+		etd->reg_10 = 0x0f;
+		rc = elantech_mode_command(psmouse, 0x10, etd->reg_10);
+		break;
 	}
 
-	if (rc == 0) {
+	if (etd->hw_version != 3 && rc == 0) {
 		/*
 		 * Read back reg 0x10. For hardware version 1 we must make
 		 * sure the absolute mode bit is set. For hardware version 2
@@ -473,10 +602,11 @@  static int elantech_set_absolute_mode(struct psmouse *psmouse)
 /*
  * Set the appropriate event bits for the input subsystem
  */
-static void elantech_set_input_params(struct psmouse *psmouse)
+static int elantech_set_input_params(struct psmouse *psmouse)
 {
 	struct input_dev *dev = psmouse->dev;
 	struct elantech_data *etd = psmouse->private;
+	unsigned char param[3];
 
 	__set_bit(EV_KEY, dev->evbit);
 	__set_bit(EV_ABS, dev->evbit);
@@ -492,20 +622,41 @@  static void elantech_set_input_params(struct psmouse *psmouse)
 
 	switch (etd->hw_version) {
 	case 1:
+		etd->x_max = ETP_XMAX_V1;
+		etd->y_max = ETP_YMAX_V1;
+
 		/* Rocker button */
 		if (etd->fw_version < 0x020000 &&
 		    (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
 			__set_bit(BTN_FORWARD, dev->keybit);
 			__set_bit(BTN_BACK, dev->keybit);
 		}
-		input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0);
-		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0);
+		input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, etd->x_max, 0, 0);
+		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, etd->y_max, 0, 0);
 		break;
 
 	case 2:
+	case 3:
+		if (etd->hw_version == 3) {
+			if (ps2_command(&psmouse->ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+			    ps2_command(&psmouse->ps2dev, NULL, 0x00) ||
+			    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+				return -1;
+
+			etd->x_max = (param[0] & 0xf) << 8 | param[1];
+			etd->y_max = (param[0] & 0xf0) << 4 | param[2];
+			etd->y_max_2ft = etd->y_max;
+			elantech_debug("x_max = %d, y_max = %d\n",
+				       etd->x_max, etd->y_max);
+		} else {
+			etd->x_max = ETP_XMAX_V2;
+			etd->y_max = ETP_YMAX_V2;
+			etd->y_max_2ft = ETP_2FT_YMAX;
+		}
+
 		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
-		input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
-		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+		input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, etd->x_max, 0, 0);
+		input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, etd->y_max, 0, 0);
 		if (etd->reports_pressure) {
 			input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
 					     ETP_PMAX_V2, 0, 0);
@@ -514,10 +665,12 @@  static void elantech_set_input_params(struct psmouse *psmouse)
 		}
 		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
 		input_mt_init_slots(dev, 2);
-		input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
-		input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, etd->x_max, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, etd->y_max, 0, 0);
 		break;
 	}
+
+	return 0;
 }
 
 struct elantech_attr_data {
@@ -639,10 +792,17 @@  static bool elantech_is_signature_valid(const unsigned char *param)
 /*
  * Use magic knock to detect Elantech touchpad
  */
+
+static const char elantech_magic_knocks[][3] = {
+	{ '\x3c', '\x03', '\xc8' },
+	{ '\x3c', '\x03', '\x00' },
+};
+
 int elantech_detect(struct psmouse *psmouse, bool set_properties)
 {
 	struct ps2dev *ps2dev = &psmouse->ps2dev;
 	unsigned char param[3];
+	int i;
 
 	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
 
@@ -655,11 +815,15 @@  int elantech_detect(struct psmouse *psmouse, bool set_properties)
 		return -1;
 	}
 
-	/*
-	 * Report this in case there are Elantech models that use a different
-	 * set of magic numbers
-	 */
-	if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
+	for (i = 0; i < ARRAY_SIZE(elantech_magic_knocks); i++) {
+		if (!memcmp(param, elantech_magic_knocks[i], sizeof(param)))
+			break;
+	}
+	if (i >= ARRAY_SIZE(elantech_magic_knocks)) {
+		/*
+		 * Report this in case there are Elantech models that use a
+		 * different set of magic numbers
+		 */
 		pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
 			 param[0], param[1], param[2]);
 		return -1;
@@ -749,23 +913,38 @@  int elantech_init(struct psmouse *psmouse)
 
 	etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
 
-	/*
-	 * Assume every version greater than this is new EeePC style
-	 * hardware with 6 byte packets
-	 */
-	if (etd->fw_version >= 0x020030) {
+	if (etd->fw_version < 0x020030) {
+		etd->hw_version = 1;
+		etd->paritycheck = 1;
+	} else if ((etd->fw_version & ~FW_VERSION_MICRO_MASK) <= 0x140000) {
 		etd->hw_version = 2;
 		/* For now show extra debug information */
 		etd->debug = 1;
-		/* Don't know how to do parity checking for version 2 */
-		etd->paritycheck = 0;
 
 		if (etd->fw_version >= 0x020800)
 			etd->reports_pressure = true;
-
 	} else {
-		etd->hw_version = 1;
-		etd->paritycheck = 1;
+		unsigned char dummy[3];
+		error = 0;
+		if (ps2_command(&psmouse->ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    ps2_command(&psmouse->ps2dev, NULL, 0x01) ||
+		    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+			error = -1;
+		else if (
+		    ps2_command(&psmouse->ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    ps2_command(&psmouse->ps2dev, NULL, 0x04) ||
+		    ps2_command(&psmouse->ps2dev, dummy, PSMOUSE_CMD_GETINFO))
+			error = -1;
+
+		if (!error &&
+		    (param[0] & 0x0f) >= 5 && (param[1] & 0x0f) >= 0x06) {
+			etd->hw_version = 3;
+			etd->debug = 1;
+			etd->reports_pressure = true;
+		} else {
+			pr_debug("did not detect elantech firmware version\n");
+			goto init_fail;
+		}
 	}
 
 	pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
@@ -794,7 +973,11 @@  int elantech_init(struct psmouse *psmouse)
 		goto init_fail;
 	}
 
-	elantech_set_input_params(psmouse);
+	error = elantech_set_input_params(psmouse);
+	if (error) {
+		pr_err("failed to set input parameters\n");
+		goto init_fail;
+	}
 
 	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
 				   &elantech_attr_group);
@@ -806,7 +989,7 @@  int elantech_init(struct psmouse *psmouse)
 	psmouse->protocol_handler = elantech_process_byte;
 	psmouse->disconnect = elantech_disconnect;
 	psmouse->reconnect = elantech_reconnect;
-	psmouse->pktsize = etd->hw_version == 2 ? 6 : 4;
+	psmouse->pktsize = etd->hw_version == 1 ? 4 : 6;
 
 	return 0;
 
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index fabb2b9..b896cc8 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -22,6 +22,7 @@ 
 /*
  * Command values for register reading or writing
  */
+#define ETP_REGISTER_RW			0x00
 #define ETP_REGISTER_READ		0x10
 #define ETP_REGISTER_WRITE		0x11
 
@@ -110,6 +111,9 @@  struct elantech_data {
 	bool reports_pressure;
 	unsigned char hw_version;
 	unsigned int fw_version;
+	unsigned int x_max;
+	unsigned int y_max;
+	unsigned int y_max_2ft;
 	unsigned int single_finger_reports;
 	unsigned char parity[256];
 };