diff options
Diffstat (limited to 'drivers/input/mouse')
34 files changed, 8575 insertions, 1280 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index bf5fd7f6a31..366fc7ad5eb 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -17,7 +17,7 @@ config MOUSE_PS2  	default y  	select SERIO  	select SERIO_LIBPS2 -	select SERIO_I8042 if X86 +	select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO  	select SERIO_GSCPS2 if GSC  	help  	  Say Y here if you have a PS/2 mouse connected to your system. This @@ -39,7 +39,7 @@ config MOUSE_PS2  	  module will be called psmouse.  config MOUSE_PS2_ALPS -	bool "ALPS PS/2 mouse protocol extension" if EMBEDDED +	bool "ALPS PS/2 mouse protocol extension" if EXPERT  	default y  	depends on MOUSE_PS2  	help @@ -49,17 +49,17 @@ config MOUSE_PS2_ALPS  	  If unsure, say Y.  config MOUSE_PS2_LOGIPS2PP -	bool "Logitech PS/2++ mouse protocol extension" if EMBEDDED +	bool "Logitech PS/2++ mouse protocol extension" if EXPERT  	default y  	depends on MOUSE_PS2  	help -	  Say Y here if you have a Logictech PS/2++ mouse connected to +	  Say Y here if you have a Logitech PS/2++ mouse connected to  	  your system.  	  If unsure, say Y.  config MOUSE_PS2_SYNAPTICS -	bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED +	bool "Synaptics PS/2 mouse protocol extension" if EXPERT  	default y  	depends on MOUSE_PS2  	help @@ -68,8 +68,18 @@ 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 EMBEDDED +	bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT  	default y  	depends on MOUSE_PS2 && X86 && DMI  	help @@ -79,7 +89,7 @@ config MOUSE_PS2_LIFEBOOK  	  If unsure, say Y.  config MOUSE_PS2_TRACKPOINT -	bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED +	bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT  	default y  	depends on MOUSE_PS2  	help @@ -193,6 +203,18 @@ config MOUSE_BCM5974  	  To compile this driver as a module, choose M here: the  	  module will be called bcm5974. +config MOUSE_CYAPA +	tristate "Cypress APA I2C Trackpad support" +	depends on I2C +	help +	  This driver adds support for Cypress All Points Addressable (APA) +	  I2C Trackpads, including the ones used in 2012 Samsung Chromebooks. + +	  Say Y here if you have a Cypress APA I2C Trackpad. + +	  To compile this driver as a module, choose M here: the module will be +	  called cyapa. +  config MOUSE_INPORT  	tristate "InPort/MS/ATIXL busmouse"  	depends on ISA @@ -273,7 +295,7 @@ config MOUSE_VSXXXAA  config MOUSE_GPIO  	tristate "GPIO mouse" -	depends on GENERIC_GPIO +	depends on GPIOLIB  	select INPUT_POLLDEV  	help  	  This driver simulates a mouse on GPIO lines of various CPUs (and some @@ -322,4 +344,33 @@ config MOUSE_SYNAPTICS_I2C  	  To compile this driver as a module, choose M here: the  	  module will be called synaptics_i2c. +config MOUSE_SYNAPTICS_USB +	tristate "Synaptics USB device support" +	depends on USB_ARCH_HAS_HCD +	select USB +	help +	  Say Y here if you want to use a Synaptics USB touchpad or pointing +	  stick. + +	  While these devices emulate an USB mouse by default and can be used +	  with standard usbhid driver, this driver, together with its X.Org +	  counterpart, allows you to fully utilize capabilities of the device. +	  More information can be found at: +	  <http://jan-steinhoff.de/linux/synaptics-usb.html> + +	  To compile this driver as a module, choose M here: the +	  module will be called synaptics_usb. + +config MOUSE_NAVPOINT_PXA27x +	tristate "Synaptics NavPoint (PXA27x SSP/SPI)" +	depends on PXA27x && PXA_SSP +	help +	  This driver adds support for the Synaptics NavPoint touchpad connected +	  to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; +	  a tap or tap-and-a-half drag gesture emulates the left mouse button. +	  For example, use the xf86-input-evdev driver for an X pointing device. + +	  To compile this driver as a module, choose M here: the +	  module will be called navpoint. +  endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 570c84a4a65..c25efdb3f28 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -8,16 +8,19 @@ obj-$(CONFIG_MOUSE_AMIGA)		+= amimouse.o  obj-$(CONFIG_MOUSE_APPLETOUCH)		+= appletouch.o  obj-$(CONFIG_MOUSE_ATARI)		+= atarimouse.o  obj-$(CONFIG_MOUSE_BCM5974)		+= bcm5974.o +obj-$(CONFIG_MOUSE_CYAPA)		+= cyapa.o  obj-$(CONFIG_MOUSE_GPIO)		+= gpio_mouse.o  obj-$(CONFIG_MOUSE_INPORT)		+= inport.o  obj-$(CONFIG_MOUSE_LOGIBM)		+= logibm.o  obj-$(CONFIG_MOUSE_MAPLE)		+= maplemouse.o +obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x)	+= navpoint.o  obj-$(CONFIG_MOUSE_PC110PAD)		+= pc110pad.o  obj-$(CONFIG_MOUSE_PS2)			+= psmouse.o  obj-$(CONFIG_MOUSE_PXA930_TRKBALL)	+= pxa930_trkball.o  obj-$(CONFIG_MOUSE_RISCPC)		+= rpcmouse.o  obj-$(CONFIG_MOUSE_SERIAL)		+= sermouse.o  obj-$(CONFIG_MOUSE_SYNAPTICS_I2C)	+= synaptics_i2c.o +obj-$(CONFIG_MOUSE_SYNAPTICS_USB)	+= synaptics_usb.o  obj-$(CONFIG_MOUSE_VSXXXAA)		+= vsxxxaa.o  psmouse-objs := psmouse-base.o synaptics.o @@ -30,3 +33,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/alps.c b/drivers/input/mouse/alps.c index 99d58764ef0..fb15c64ffb9 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -17,20 +17,79 @@  #include <linux/slab.h>  #include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/serio.h>  #include <linux/libps2.h>  #include "psmouse.h"  #include "alps.h" -#undef DEBUG -#ifdef DEBUG -#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg) -#else -#define dbg(format, arg...) do {} while (0) -#endif +/* + * Definitions for ALPS version 3 and 4 command mode protocol + */ +#define ALPS_CMD_NIBBLE_10	0x01f2 + +#define ALPS_REG_BASE_RUSHMORE	0xc2c0 +#define ALPS_REG_BASE_PINNACLE	0x0000 + +static const struct alps_nibble_commands alps_v3_nibble_commands[] = { +	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */ +	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */ +	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* 2 */ +	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 3 */ +	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 4 */ +	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 5 */ +	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 6 */ +	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 7 */ +	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 8 */ +	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 9 */ +	{ ALPS_CMD_NIBBLE_10,		0x00 }, /* a */ +	{ PSMOUSE_CMD_SETRES,		0x00 }, /* b */ +	{ PSMOUSE_CMD_SETRES,		0x01 }, /* c */ +	{ PSMOUSE_CMD_SETRES,		0x02 }, /* d */ +	{ PSMOUSE_CMD_SETRES,		0x03 }, /* e */ +	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */ +}; + +static const struct alps_nibble_commands alps_v4_nibble_commands[] = { +	{ PSMOUSE_CMD_ENABLE,		0x00 }, /* 0 */ +	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */ +	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* 2 */ +	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 3 */ +	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 4 */ +	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 5 */ +	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 6 */ +	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 7 */ +	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 8 */ +	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 9 */ +	{ ALPS_CMD_NIBBLE_10,		0x00 }, /* a */ +	{ PSMOUSE_CMD_SETRES,		0x00 }, /* b */ +	{ PSMOUSE_CMD_SETRES,		0x01 }, /* c */ +	{ PSMOUSE_CMD_SETRES,		0x02 }, /* d */ +	{ PSMOUSE_CMD_SETRES,		0x03 }, /* e */ +	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */ +}; + +static const struct alps_nibble_commands alps_v6_nibble_commands[] = { +	{ PSMOUSE_CMD_ENABLE,		0x00 }, /* 0 */ +	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 1 */ +	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 2 */ +	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 3 */ +	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 4 */ +	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 5 */ +	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 6 */ +	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 7 */ +	{ PSMOUSE_CMD_GETID,		0x00 }, /* 8 */ +	{ PSMOUSE_CMD_GETINFO,		0x00 }, /* 9 */ +	{ PSMOUSE_CMD_SETRES,		0x00 }, /* a */ +	{ PSMOUSE_CMD_SETRES,		0x01 }, /* b */ +	{ PSMOUSE_CMD_SETRES,		0x02 }, /* c */ +	{ PSMOUSE_CMD_SETRES,		0x03 }, /* d */ +	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* e */ +	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */ +}; + -#define ALPS_OLDPROTO		0x01	/* old style input */  #define ALPS_DUALPOINT		0x02	/* touchpad has trackstick */  #define ALPS_PASS		0x04	/* device has a pass-through port */ @@ -42,79 +101,51 @@  					   6-byte ALPS packet */  static const struct alps_model_info alps_model_data[] = { -	{ { 0x32, 0x02, 0x14 },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ -	{ { 0x33, 0x02, 0x0a },	0x88, 0xf8, ALPS_OLDPROTO },		  /* UMAX-530T */ -	{ { 0x53, 0x02, 0x0a },	0xf8, 0xf8, 0 }, -	{ { 0x53, 0x02, 0x14 },	0xf8, 0xf8, 0 }, -	{ { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 },			  /* HP ze1115 */ -	{ { 0x63, 0x02, 0x0a },	0xf8, 0xf8, 0 }, -	{ { 0x63, 0x02, 0x14 },	0xf8, 0xf8, 0 }, -	{ { 0x63, 0x02, 0x28 },	0xf8, 0xf8, ALPS_FW_BK_2 },		  /* Fujitsu Siemens S6010 */ -	{ { 0x63, 0x02, 0x3c },	0x8f, 0x8f, ALPS_WHEEL },		  /* Toshiba Satellite S2400-103 */ -	{ { 0x63, 0x02, 0x50 },	0xef, 0xef, ALPS_FW_BK_1 },		  /* NEC Versa L320 */ -	{ { 0x63, 0x02, 0x64 },	0xf8, 0xf8, 0 }, -	{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ -	{ { 0x73, 0x00, 0x0a },	0xf8, 0xf8, ALPS_DUALPOINT },		  /* ThinkPad R61 8918-5QG */ -	{ { 0x73, 0x02, 0x0a },	0xf8, 0xf8, 0 }, -	{ { 0x73, 0x02, 0x14 },	0xf8, 0xf8, ALPS_FW_BK_2 },		  /* Ahtec Laptop */ -	{ { 0x20, 0x02, 0x0e },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ -	{ { 0x22, 0x02, 0x0a },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, -	{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ +	{ { 0x32, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Toshiba Salellite Pro M10 */ +	{ { 0x33, 0x02, 0x0a },	0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 },				/* UMAX-530T */ +	{ { 0x53, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, +	{ { 0x53, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, +	{ { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },				/* HP ze1115 */ +	{ { 0x63, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, +	{ { 0x63, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, +	{ { 0x63, 0x02, 0x28 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 },		/* Fujitsu Siemens S6010 */ +	{ { 0x63, 0x02, 0x3c },	0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL },			/* Toshiba Satellite S2400-103 */ +	{ { 0x63, 0x02, 0x50 },	0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 },		/* NEC Versa L320 */ +	{ { 0x63, 0x02, 0x64 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, +	{ { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Dell Latitude D800 */ +	{ { 0x73, 0x00, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT },		/* ThinkPad R61 8918-5QG */ +	{ { 0x73, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, +	{ { 0x73, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 },		/* Ahtec Laptop */ +	{ { 0x20, 0x02, 0x0e },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* XXX */ +	{ { 0x22, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, +	{ { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT },	/* Dell Latitude D600 */  	/* Dell Latitude E5500, E6400, E6500, Precision M4400 */ -	{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, +	{ { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,  		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, -	{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },	  /* Dell Vostro 1400 */ -	{ { 0x52, 0x01, 0x14 }, 0xff, 0xff, -		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },	  /* Toshiba Tecra A11-11L */ +	{ { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT },		/* Dell XT2 */ +	{ { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },		/* Dell Vostro 1400 */ +	{ { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, +		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },				/* Toshiba Tecra A11-11L */ +	{ { 0x73, 0x02, 0x64 },	0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 },  }; +static void alps_set_abs_params_st(struct alps_data *priv, +				   struct input_dev *dev1); +static void alps_set_abs_params_mt(struct alps_data *priv, +				   struct input_dev *dev1); +  /*   * XXX - this entry is suspicious. First byte has zero lower nibble,   * which is what a normal mouse would report. Also, the value 0x0e   * isn't valid per PS/2 spec.   */ -/* - * PS/2 packet format - * - * byte 0:  0    0 YSGN XSGN    1    M    R    L - * byte 1: X7   X6   X5   X4   X3   X2   X1   X0 - * byte 2: Y7   Y6   Y5   Y4   Y3   Y2   Y1   Y0 - * - * Note that the device never signals overflow condition. - * - * ALPS absolute Mode - new format - * - * byte 0:  1    ?    ?    ?    1    ?    ?    ? - * byte 1:  0   x6   x5   x4   x3   x2   x1   x0 - * byte 2:  0  x10   x9   x8   x7    ?  fin  ges - * byte 3:  0   y9   y8   y7    1    M    R    L - * byte 4:  0   y6   y5   y4   y3   y2   y1   y0 - * byte 5:  0   z6   z5   z4   z3   z2   z1   z0 - * - * Dualpoint device -- interleaved packet format - * - * byte 0:    1    1    0    0    1    1    1    1 - * byte 1:    0   x6   x5   x4   x3   x2   x1   x0 - * byte 2:    0  x10   x9   x8   x7    0  fin  ges - * byte 3:    0    0 YSGN XSGN    1    1    1    1 - * byte 4:   X7   X6   X5   X4   X3   X2   X1   X0 - * byte 5:   Y7   Y6   Y5   Y4   Y3   Y2   Y1   Y0 - * byte 6:    0   y9   y8   y7    1    m    r    l - * byte 7:    0   y6   y5   y4   y3   y2   y1   y0 - * byte 8:    0   z6   z5   z4   z3   z2   z1   z0 - * - * CAPITALS = stick, miniscules = touchpad - * - * ?'s can have different meanings on different models, - * such as wheel rotation, extra buttons, stick buttons - * on a dualpoint, etc. - */ +/* Packet formats are described in Documentation/input/alps.txt */ -static bool alps_is_valid_first_byte(const struct alps_model_info *model, +static bool alps_is_valid_first_byte(struct alps_data *priv,  				     unsigned char data)  { -	return (data & model->mask0) == model->byte0; +	return (data & priv->mask0) == priv->byte0;  }  static void alps_report_buttons(struct psmouse *psmouse, @@ -144,17 +175,16 @@ static void alps_report_buttons(struct psmouse *psmouse,  	input_sync(dev2);  } -static void alps_process_packet(struct psmouse *psmouse) +static void alps_process_packet_v1_v2(struct psmouse *psmouse)  {  	struct alps_data *priv = psmouse->private; -	const struct alps_model_info *model = priv->i;  	unsigned char *packet = psmouse->packet;  	struct input_dev *dev = psmouse->dev;  	struct input_dev *dev2 = priv->dev2;  	int x, y, z, ges, fin, left, right, middle;  	int back = 0, forward = 0; -	if (model->flags & ALPS_OLDPROTO) { +	if (priv->proto_version == ALPS_PROTO_V1) {  		left = packet[2] & 0x10;  		right = packet[2] & 0x08;  		middle = 0; @@ -170,12 +200,12 @@ static void alps_process_packet(struct psmouse *psmouse)  		z = packet[5];  	} -	if (model->flags & ALPS_FW_BK_1) { +	if (priv->flags & ALPS_FW_BK_1) {  		back = packet[0] & 0x10;  		forward = packet[2] & 4;  	} -	if (model->flags & ALPS_FW_BK_2) { +	if (priv->flags & ALPS_FW_BK_2) {  		back = packet[3] & 4;  		forward = packet[2] & 4;  		if ((middle = forward && back)) @@ -185,7 +215,7 @@ static void alps_process_packet(struct psmouse *psmouse)  	ges = packet[2] & 1;  	fin = packet[2] & 2; -	if ((model->flags & ALPS_DUALPOINT) && z == 127) { +	if ((priv->flags & ALPS_DUALPOINT) && z == 127) {  		input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));  		input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); @@ -228,15 +258,15 @@ static void alps_process_packet(struct psmouse *psmouse)  	input_report_abs(dev, ABS_PRESSURE, z);  	input_report_key(dev, BTN_TOOL_FINGER, z > 0); -	if (model->flags & ALPS_WHEEL) +	if (priv->flags & ALPS_WHEEL)  		input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); -	if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { +	if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {  		input_report_key(dev, BTN_FORWARD, forward);  		input_report_key(dev, BTN_BACK, back);  	} -	if (model->flags & ALPS_FOUR_BUTTONS) { +	if (priv->flags & ALPS_FOUR_BUTTONS) {  		input_report_key(dev, BTN_0, packet[2] & 4);  		input_report_key(dev, BTN_1, packet[0] & 0x10);  		input_report_key(dev, BTN_2, packet[3] & 4); @@ -246,6 +276,646 @@ static void alps_process_packet(struct psmouse *psmouse)  	input_sync(dev);  } +/* + * Process bitmap data for V5 protocols. Return value is null. + * + * The bitmaps don't have enough data to track fingers, so this function + * only generates points representing a bounding box of at most two contacts. + * These two points are returned in x1, y1, x2, and y2. + */ +static void alps_process_bitmap_dolphin(struct alps_data *priv, +					struct alps_fields *fields, +					int *x1, int *y1, int *x2, int *y2) +{ +	int box_middle_x, box_middle_y; +	unsigned int x_map, y_map; +	unsigned char start_bit, end_bit; +	unsigned char x_msb, x_lsb, y_msb, y_lsb; + +	x_map = fields->x_map; +	y_map = fields->y_map; + +	if (!x_map || !y_map) +		return; + +	/* Get Most-significant and Least-significant bit */ +	x_msb = fls(x_map); +	x_lsb = ffs(x_map); +	y_msb = fls(y_map); +	y_lsb = ffs(y_map); + +	/* Most-significant bit should never exceed max sensor line number */ +	if (x_msb > priv->x_bits || y_msb > priv->y_bits) +		return; + +	*x1 = *y1 = *x2 = *y2 = 0; + +	if (fields->fingers > 1) { +		start_bit = priv->x_bits - x_msb; +		end_bit = priv->x_bits - x_lsb; +		box_middle_x = (priv->x_max * (start_bit + end_bit)) / +				(2 * (priv->x_bits - 1)); + +		start_bit = y_lsb - 1; +		end_bit = y_msb - 1; +		box_middle_y = (priv->y_max * (start_bit + end_bit)) / +				(2 * (priv->y_bits - 1)); +		*x1 = fields->x; +		*y1 = fields->y; +		*x2 = 2 * box_middle_x - *x1; +		*y2 = 2 * box_middle_y - *y1; +	} +} + +/* + * Process bitmap data from v3 and v4 protocols. Returns the number of + * fingers detected. A return value of 0 means at least one of the + * bitmaps was empty. + * + * The bitmaps don't have enough data to track fingers, so this function + * only generates points representing a bounding box of all contacts. + * These points are returned in x1, y1, x2, and y2 when the return value + * is greater than 0. + */ +static int alps_process_bitmap(struct alps_data *priv, +			       unsigned int x_map, unsigned int y_map, +			       int *x1, int *y1, int *x2, int *y2) +{ +	struct alps_bitmap_point { +		int start_bit; +		int num_bits; +	}; + +	int fingers_x = 0, fingers_y = 0, fingers; +	int i, bit, prev_bit; +	struct alps_bitmap_point x_low = {0,}, x_high = {0,}; +	struct alps_bitmap_point y_low = {0,}, y_high = {0,}; +	struct alps_bitmap_point *point; + +	if (!x_map || !y_map) +		return 0; + +	*x1 = *y1 = *x2 = *y2 = 0; + +	prev_bit = 0; +	point = &x_low; +	for (i = 0; x_map != 0; i++, x_map >>= 1) { +		bit = x_map & 1; +		if (bit) { +			if (!prev_bit) { +				point->start_bit = i; +				fingers_x++; +			} +			point->num_bits++; +		} else { +			if (prev_bit) +				point = &x_high; +			else +				point->num_bits = 0; +		} +		prev_bit = bit; +	} + +	/* +	 * y bitmap is reversed for what we need (lower positions are in +	 * higher bits), so we process from the top end. +	 */ +	y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits); +	prev_bit = 0; +	point = &y_low; +	for (i = 0; y_map != 0; i++, y_map <<= 1) { +		bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1)); +		if (bit) { +			if (!prev_bit) { +				point->start_bit = i; +				fingers_y++; +			} +			point->num_bits++; +		} else { +			if (prev_bit) +				point = &y_high; +			else +				point->num_bits = 0; +		} +		prev_bit = bit; +	} + +	/* +	 * Fingers can overlap, so we use the maximum count of fingers +	 * on either axis as the finger count. +	 */ +	fingers = max(fingers_x, fingers_y); + +	/* +	 * If total fingers is > 1 but either axis reports only a single +	 * contact, we have overlapping or adjacent fingers. For the +	 * purposes of creating a bounding box, divide the single contact +	 * (roughly) equally between the two points. +	 */ +	if (fingers > 1) { +		if (fingers_x == 1) { +			i = x_low.num_bits / 2; +			x_low.num_bits = x_low.num_bits - i; +			x_high.start_bit = x_low.start_bit + i; +			x_high.num_bits = max(i, 1); +		} else if (fingers_y == 1) { +			i = y_low.num_bits / 2; +			y_low.num_bits = y_low.num_bits - i; +			y_high.start_bit = y_low.start_bit + i; +			y_high.num_bits = max(i, 1); +		} +	} + +	*x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / +	      (2 * (priv->x_bits - 1)); +	*y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / +	      (2 * (priv->y_bits - 1)); + +	if (fingers > 1) { +		*x2 = (priv->x_max * +		       (2 * x_high.start_bit + x_high.num_bits - 1)) / +		      (2 * (priv->x_bits - 1)); +		*y2 = (priv->y_max * +		       (2 * y_high.start_bit + y_high.num_bits - 1)) / +		      (2 * (priv->y_bits - 1)); +	} + +	return fingers; +} + +static void alps_set_slot(struct input_dev *dev, int slot, bool active, +			  int x, int y) +{ +	input_mt_slot(dev, slot); +	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); +	if (active) { +		input_report_abs(dev, ABS_MT_POSITION_X, x); +		input_report_abs(dev, ABS_MT_POSITION_Y, y); +	} +} + +static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, +				     int x1, int y1, int x2, int y2) +{ +	alps_set_slot(dev, 0, num_fingers != 0, x1, y1); +	alps_set_slot(dev, 1, num_fingers == 2, x2, y2); +} + +static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) +{ +	struct alps_data *priv = psmouse->private; +	unsigned char *packet = psmouse->packet; +	struct input_dev *dev = priv->dev2; +	int x, y, z, left, right, middle; + +	/* Sanity check packet */ +	if (!(packet[0] & 0x40)) { +		psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); +		return; +	} + +	/* +	 * There's a special packet that seems to indicate the end +	 * of a stream of trackstick data. Filter these out. +	 */ +	if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) +		return; + +	x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); +	y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); +	z = (packet[4] & 0x7c) >> 2; + +	/* +	 * The x and y values tend to be quite large, and when used +	 * alone the trackstick is difficult to use. Scale them down +	 * to compensate. +	 */ +	x /= 8; +	y /= 8; + +	input_report_rel(dev, REL_X, x); +	input_report_rel(dev, REL_Y, -y); + +	/* +	 * Most ALPS models report the trackstick buttons in the touchpad +	 * packets, but a few report them here. No reliable way has been +	 * found to differentiate between the models upfront, so we enable +	 * the quirk in response to seeing a button press in the trackstick +	 * packet. +	 */ +	left = packet[3] & 0x01; +	right = packet[3] & 0x02; +	middle = packet[3] & 0x04; + +	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && +	    (left || right || middle)) +		priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; + +	if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { +		input_report_key(dev, BTN_LEFT, left); +		input_report_key(dev, BTN_RIGHT, right); +		input_report_key(dev, BTN_MIDDLE, middle); +	} + +	input_sync(dev); +	return; +} + +static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) +{ +	f->left = !!(p[3] & 0x01); +	f->right = !!(p[3] & 0x02); +	f->middle = !!(p[3] & 0x04); + +	f->ts_left = !!(p[3] & 0x10); +	f->ts_right = !!(p[3] & 0x20); +	f->ts_middle = !!(p[3] & 0x40); +} + +static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, +				 struct psmouse *psmouse) +{ +	f->first_mp = !!(p[4] & 0x40); +	f->is_mp = !!(p[0] & 0x40); + +	f->fingers = (p[5] & 0x3) + 1; +	f->x_map = ((p[4] & 0x7e) << 8) | +		   ((p[1] & 0x7f) << 2) | +		   ((p[0] & 0x30) >> 4); +	f->y_map = ((p[3] & 0x70) << 4) | +		   ((p[2] & 0x7f) << 1) | +		   (p[4] & 0x01); + +	f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | +	       ((p[0] & 0x30) >> 4); +	f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); +	f->z = p[5] & 0x7f; + +	alps_decode_buttons_v3(f, p); +} + +static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p, +				 struct psmouse *psmouse) +{ +	alps_decode_pinnacle(f, p, psmouse); + +	f->x_map |= (p[5] & 0x10) << 11; +	f->y_map |= (p[5] & 0x20) << 6; +} + +static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, +				struct psmouse *psmouse) +{ +	u64 palm_data = 0; +	struct alps_data *priv = psmouse->private; + +	f->first_mp = !!(p[0] & 0x02); +	f->is_mp = !!(p[0] & 0x20); + +	if (!f->is_mp) { +		f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); +		f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); +		f->z = (p[0] & 4) ? 0 : p[5] & 0x7f; +		alps_decode_buttons_v3(f, p); +	} else { +		f->fingers = ((p[0] & 0x6) >> 1 | +		     (p[0] & 0x10) >> 2); + +		palm_data = (p[1] & 0x7f) | +			    ((p[2] & 0x7f) << 7) | +			    ((p[4] & 0x7f) << 14) | +			    ((p[5] & 0x7f) << 21) | +			    ((p[3] & 0x07) << 28) | +			    (((u64)p[3] & 0x70) << 27) | +			    (((u64)p[0] & 0x01) << 34); + +		/* Y-profile is stored in P(0) to p(n-1), n = y_bits; */ +		f->y_map = palm_data & (BIT(priv->y_bits) - 1); + +		/* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */ +		f->x_map = (palm_data >> priv->y_bits) & +			   (BIT(priv->x_bits) - 1); +	} +} + +static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) +{ +	struct alps_data *priv = psmouse->private; +	unsigned char *packet = psmouse->packet; +	struct input_dev *dev = psmouse->dev; +	struct input_dev *dev2 = priv->dev2; +	int x1 = 0, y1 = 0, x2 = 0, y2 = 0; +	int fingers = 0, bmap_fn; +	struct alps_fields f = {0}; + +	priv->decode_fields(&f, packet, psmouse); + +	/* +	 * There's no single feature of touchpad position and bitmap packets +	 * that can be used to distinguish between them. We rely on the fact +	 * that a bitmap packet should always follow a position packet with +	 * bit 6 of packet[4] set. +	 */ +	if (priv->multi_packet) { +		/* +		 * Sometimes a position packet will indicate a multi-packet +		 * sequence, but then what follows is another position +		 * packet. Check for this, and when it happens process the +		 * position packet as usual. +		 */ +		if (f.is_mp) { +			fingers = f.fingers; +			if (priv->proto_version == ALPS_PROTO_V3) { +				bmap_fn = alps_process_bitmap(priv, f.x_map, +							      f.y_map, &x1, &y1, +							      &x2, &y2); + +				/* +				 * We shouldn't report more than one finger if +				 * we don't have two coordinates. +				 */ +				if (fingers > 1 && bmap_fn < 2) +					fingers = bmap_fn; + +				/* Now process position packet */ +				priv->decode_fields(&f, priv->multi_data, +						    psmouse); +			} else { +				/* +				 * Because Dolphin uses position packet's +				 * coordinate data as Pt1 and uses it to +				 * calculate Pt2, so we need to do position +				 * packet decode first. +				 */ +				priv->decode_fields(&f, priv->multi_data, +						    psmouse); + +				/* +				 * Since Dolphin's finger number is reliable, +				 * there is no need to compare with bmap_fn. +				 */ +				alps_process_bitmap_dolphin(priv, &f, &x1, &y1, +							    &x2, &y2); +			} +		} else { +			priv->multi_packet = 0; +		} +	} + +	/* +	 * Bit 6 of byte 0 is not usually set in position packets. The only +	 * times it seems to be set is in situations where the data is +	 * suspect anyway, e.g. a palm resting flat on the touchpad. Given +	 * this combined with the fact that this bit is useful for filtering +	 * out misidentified bitmap packets, we reject anything with this +	 * bit set. +	 */ +	if (f.is_mp) +		return; + +	if (!priv->multi_packet && f.first_mp) { +		priv->multi_packet = 1; +		memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); +		return; +	} + +	priv->multi_packet = 0; + +	/* +	 * Sometimes the hardware sends a single packet with z = 0 +	 * in the middle of a stream. Real releases generate packets +	 * with x, y, and z all zero, so these seem to be flukes. +	 * Ignore them. +	 */ +	if (f.x && f.y && !f.z) +		return; + +	/* +	 * If we don't have MT data or the bitmaps were empty, we have +	 * to rely on ST data. +	 */ +	if (!fingers) { +		x1 = f.x; +		y1 = f.y; +		fingers = f.z > 0 ? 1 : 0; +	} + +	if (f.z >= 64) +		input_report_key(dev, BTN_TOUCH, 1); +	else +		input_report_key(dev, BTN_TOUCH, 0); + +	alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + +	input_mt_report_finger_count(dev, fingers); + +	input_report_key(dev, BTN_LEFT, f.left); +	input_report_key(dev, BTN_RIGHT, f.right); +	input_report_key(dev, BTN_MIDDLE, f.middle); + +	if (f.z > 0) { +		input_report_abs(dev, ABS_X, f.x); +		input_report_abs(dev, ABS_Y, f.y); +	} +	input_report_abs(dev, ABS_PRESSURE, f.z); + +	input_sync(dev); + +	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { +		input_report_key(dev2, BTN_LEFT, f.ts_left); +		input_report_key(dev2, BTN_RIGHT, f.ts_right); +		input_report_key(dev2, BTN_MIDDLE, f.ts_middle); +		input_sync(dev2); +	} +} + +static void alps_process_packet_v3(struct psmouse *psmouse) +{ +	unsigned char *packet = psmouse->packet; + +	/* +	 * v3 protocol packets come in three types, two representing +	 * touchpad data and one representing trackstick data. +	 * Trackstick packets seem to be distinguished by always +	 * having 0x3f in the last byte. This value has never been +	 * observed in the last byte of either of the other types +	 * of packets. +	 */ +	if (packet[5] == 0x3f) { +		alps_process_trackstick_packet_v3(psmouse); +		return; +	} + +	alps_process_touchpad_packet_v3_v5(psmouse); +} + +static void alps_process_packet_v6(struct psmouse *psmouse) +{ +	struct alps_data *priv = psmouse->private; +	unsigned char *packet = psmouse->packet; +	struct input_dev *dev = psmouse->dev; +	struct input_dev *dev2 = priv->dev2; +	int x, y, z, left, right, middle; + +	/* +	 * We can use Byte5 to distinguish if the packet is from Touchpad +	 * or Trackpoint. +	 * Touchpad:	0 - 0x7E +	 * Trackpoint:	0x7F +	 */ +	if (packet[5] == 0x7F) { +		/* It should be a DualPoint when received Trackpoint packet */ +		if (!(priv->flags & ALPS_DUALPOINT)) +			return; + +		/* Trackpoint packet */ +		x = packet[1] | ((packet[3] & 0x20) << 2); +		y = packet[2] | ((packet[3] & 0x40) << 1); +		z = packet[4]; +		left = packet[3] & 0x01; +		right = packet[3] & 0x02; +		middle = packet[3] & 0x04; + +		/* To prevent the cursor jump when finger lifted */ +		if (x == 0x7F && y == 0x7F && z == 0x7F) +			x = y = z = 0; + +		/* Divide 4 since trackpoint's speed is too fast */ +		input_report_rel(dev2, REL_X, (char)x / 4); +		input_report_rel(dev2, REL_Y, -((char)y / 4)); + +		input_report_key(dev2, BTN_LEFT, left); +		input_report_key(dev2, BTN_RIGHT, right); +		input_report_key(dev2, BTN_MIDDLE, middle); + +		input_sync(dev2); +		return; +	} + +	/* Touchpad packet */ +	x = packet[1] | ((packet[3] & 0x78) << 4); +	y = packet[2] | ((packet[4] & 0x78) << 4); +	z = packet[5]; +	left = packet[3] & 0x01; +	right = packet[3] & 0x02; + +	if (z > 30) +		input_report_key(dev, BTN_TOUCH, 1); +	if (z < 25) +		input_report_key(dev, BTN_TOUCH, 0); + +	if (z > 0) { +		input_report_abs(dev, ABS_X, x); +		input_report_abs(dev, ABS_Y, y); +	} + +	input_report_abs(dev, ABS_PRESSURE, z); +	input_report_key(dev, BTN_TOOL_FINGER, z > 0); + +	/* v6 touchpad does not have middle button */ +	input_report_key(dev, BTN_LEFT, left); +	input_report_key(dev, BTN_RIGHT, right); + +	input_sync(dev); +} + +static void alps_process_packet_v4(struct psmouse *psmouse) +{ +	struct alps_data *priv = psmouse->private; +	unsigned char *packet = psmouse->packet; +	struct input_dev *dev = psmouse->dev; +	int offset; +	int x, y, z; +	int left, right; +	int x1, y1, x2, y2; +	int fingers = 0; +	unsigned int x_bitmap, y_bitmap; + +	/* +	 * v4 has a 6-byte encoding for bitmap data, but this data is +	 * broken up between 3 normal packets. Use priv->multi_packet to +	 * track our position in the bitmap packet. +	 */ +	if (packet[6] & 0x40) { +		/* sync, reset position */ +		priv->multi_packet = 0; +	} + +	if (WARN_ON_ONCE(priv->multi_packet > 2)) +		return; + +	offset = 2 * priv->multi_packet; +	priv->multi_data[offset] = packet[6]; +	priv->multi_data[offset + 1] = packet[7]; + +	if (++priv->multi_packet > 2) { +		priv->multi_packet = 0; + +		x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) | +			   ((priv->multi_data[3] & 0x60) << 3) | +			   ((priv->multi_data[0] & 0x3f) << 2) | +			   ((priv->multi_data[1] & 0x60) >> 5); +		y_bitmap = ((priv->multi_data[5] & 0x01) << 10) | +			   ((priv->multi_data[3] & 0x1f) << 5) | +			    (priv->multi_data[1] & 0x1f); + +		fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap, +					      &x1, &y1, &x2, &y2); + +		/* Store MT data.*/ +		priv->fingers = fingers; +		priv->x1 = x1; +		priv->x2 = x2; +		priv->y1 = y1; +		priv->y2 = y2; +	} + +	left = packet[4] & 0x01; +	right = packet[4] & 0x02; + +	x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | +	    ((packet[0] & 0x30) >> 4); +	y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); +	z = packet[5] & 0x7f; + +	/* +	 * If there were no contacts in the bitmap, use ST +	 * points in MT reports. +	 * If there were two contacts or more, report MT data. +	 */ +	if (priv->fingers < 2) { +		x1 = x; +		y1 = y; +		fingers = z > 0 ? 1 : 0; +	} else { +		fingers = priv->fingers; +		x1 = priv->x1; +		x2 = priv->x2; +		y1 = priv->y1; +		y2 = priv->y2; +	} + +	if (z >= 64) +		input_report_key(dev, BTN_TOUCH, 1); +	else +		input_report_key(dev, BTN_TOUCH, 0); + +	alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + +	input_mt_report_finger_count(dev, fingers); + +	input_report_key(dev, BTN_LEFT, left); +	input_report_key(dev, BTN_RIGHT, right); + +	if (z > 0) { +		input_report_abs(dev, ABS_X, x); +		input_report_abs(dev, ABS_Y, y); +	} +	input_report_abs(dev, ABS_PRESSURE, z); + +	input_sync(dev); +} +  static void alps_report_bare_ps2_packet(struct psmouse *psmouse,  					unsigned char packet[],  					bool report_buttons) @@ -296,15 +966,14 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)  		if (((psmouse->packet[3] |  		      psmouse->packet[4] |  		      psmouse->packet[5]) & 0x80) || -		    (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { -			dbg("refusing packet %x %x %x %x " -			    "(suspected interleaved ps/2)\n", -			    psmouse->packet[3], psmouse->packet[4], -			    psmouse->packet[5], psmouse->packet[6]); +		    (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) { +			psmouse_dbg(psmouse, +				    "refusing packet %4ph (suspected interleaved ps/2)\n", +				    psmouse->packet + 3);  			return PSMOUSE_BAD_DATA;  		} -		alps_process_packet(psmouse); +		priv->process_packet(psmouse);  		/* Continue with the next packet */  		psmouse->packet[0] = psmouse->packet[6]; @@ -319,13 +988,13 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)  		 * There is also possibility that we got 6-byte ALPS  		 * packet followed  by 3-byte packet from trackpoint. We  		 * can not distinguish between these 2 scenarios but -		 * becase the latter is unlikely to happen in course of +		 * because the latter is unlikely to happen in course of  		 * normal operation (user would need to press all  		 * buttons on the pad and start moving trackpoint  		 * without touching the pad surface) we assume former.  		 * Even if we are wrong the wost thing that would happen  		 * the cursor would jump but we should not get protocol -		 * desynchronization. +		 * de-synchronization.  		 */  		alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], @@ -348,10 +1017,11 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)  static void alps_flush_packet(unsigned long data)  {  	struct psmouse *psmouse = (struct psmouse *)data; +	struct alps_data *priv = psmouse->private;  	serio_pause_rx(psmouse->ps2dev.serio); -	if (psmouse->pktcnt == 6) { +	if (psmouse->pktcnt == psmouse->pktsize) {  		/*  		 * We did not any more data in reasonable amount of time. @@ -361,12 +1031,11 @@ static void alps_flush_packet(unsigned long data)  		if ((psmouse->packet[3] |  		     psmouse->packet[4] |  		     psmouse->packet[5]) & 0x80) { -			dbg("refusing packet %x %x %x " -			    "(suspected interleaved ps/2)\n", -			    psmouse->packet[3], psmouse->packet[4], -			    psmouse->packet[5]); +			psmouse_dbg(psmouse, +				    "refusing packet %3ph (suspected interleaved ps/2)\n", +				    psmouse->packet + 3);  		} else { -			alps_process_packet(psmouse); +			priv->process_packet(psmouse);  		}  		psmouse->pktcnt = 0;  	} @@ -377,7 +1046,6 @@ static void alps_flush_packet(unsigned long data)  static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)  {  	struct alps_data *priv = psmouse->private; -	const struct alps_model_info *model = priv->i;  	if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */  		if (psmouse->pktcnt == 3) { @@ -390,89 +1058,164 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)  	/* Check for PS/2 packet stuffed in the middle of ALPS packet. */ -	if ((model->flags & ALPS_PS2_INTERLEAVED) && +	if ((priv->flags & ALPS_PS2_INTERLEAVED) &&  	    psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {  		return alps_handle_interleaved_ps2(psmouse);  	} -	if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { -		dbg("refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", -		    psmouse->packet[0], model->mask0, model->byte0); +	if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) { +		psmouse_dbg(psmouse, +			    "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", +			    psmouse->packet[0], priv->mask0, priv->byte0);  		return PSMOUSE_BAD_DATA;  	} -	/* Bytes 2 - 6 should have 0 in the highest bit */ -	if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 && +	/* Bytes 2 - pktsize should have 0 in the highest bit */ +	if ((priv->proto_version < ALPS_PROTO_V5) && +	    psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&  	    (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { -		dbg("refusing packet[%i] = %x\n", -		    psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]); +		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", +			    psmouse->pktcnt - 1, +			    psmouse->packet[psmouse->pktcnt - 1]);  		return PSMOUSE_BAD_DATA;  	} -	if (psmouse->pktcnt == 6) { -		alps_process_packet(psmouse); +	if (psmouse->pktcnt == psmouse->pktsize) { +		priv->process_packet(psmouse);  		return PSMOUSE_FULL_PACKET;  	}  	return PSMOUSE_GOOD_DATA;  } -static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) +static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	struct alps_data *priv = psmouse->private; +	int command; +	unsigned char *param; +	unsigned char dummy[4]; + +	BUG_ON(nibble > 0xf); + +	command = priv->nibble_commands[nibble].command; +	param = (command & 0x0f00) ? +		dummy : (unsigned char *)&priv->nibble_commands[nibble].data; + +	if (ps2_command(ps2dev, param, command)) +		return -1; + +	return 0; +} + +static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	struct alps_data *priv = psmouse->private; +	int i, nibble; + +	if (ps2_command(ps2dev, NULL, priv->addr_command)) +		return -1; + +	for (i = 12; i >= 0; i -= 4) { +		nibble = (addr >> i) & 0xf; +		if (alps_command_mode_send_nibble(psmouse, nibble)) +			return -1; +	} + +	return 0; +} + +static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr)  {  	struct ps2dev *ps2dev = &psmouse->ps2dev; -	static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };  	unsigned char param[4]; -	int i; + +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) +		return -1;  	/* -	 * First try "E6 report". -	 * ALPS should return 0,0,10 or 0,0,100 +	 * The address being read is returned in the first two bytes +	 * of the result. Check that this address matches the expected +	 * address.  	 */ -	param[0] = 0; -	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || -	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) || -	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) || -	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11)) -		return NULL; +	if (addr != ((param[0] << 8) | param[1])) +		return -1; -	param[0] = param[1] = param[2] = 0xff; -	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) -		return NULL; +	return param[2]; +} -	dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); +static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr) +{ +	if (alps_command_mode_set_addr(psmouse, addr)) +		return -1; +	return __alps_command_mode_read_reg(psmouse, addr); +} -	if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) -		return NULL; +static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value) +{ +	if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) +		return -1; +	if (alps_command_mode_send_nibble(psmouse, value & 0xf)) +		return -1; +	return 0; +} + +static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, +				       u8 value) +{ +	if (alps_command_mode_set_addr(psmouse, addr)) +		return -1; +	return __alps_command_mode_write_reg(psmouse, value); +} + +static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, +			int repeated_command, unsigned char *param) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; -	/* -	 * Now try "E7 report". Allowed responses are in -	 * alps_model_data[].signature -	 */  	param[0] = 0; -	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || -	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) || -	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) || -	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21)) -		return NULL; +	if (init_command && ps2_command(ps2dev, param, init_command)) +		return -EIO; + +	if (ps2_command(ps2dev,  NULL, repeated_command) || +	    ps2_command(ps2dev,  NULL, repeated_command) || +	    ps2_command(ps2dev,  NULL, repeated_command)) +		return -EIO;  	param[0] = param[1] = param[2] = 0xff;  	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) -		return NULL; +		return -EIO; + +	psmouse_dbg(psmouse, "%2.2X report: %3ph\n", +		    repeated_command, param); +	return 0; +} -	dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); +static int alps_enter_command_mode(struct psmouse *psmouse) +{ +	unsigned char param[4]; -	if (version) { -		for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) -			/* empty */; -		*version = (param[0] << 8) | (param[1] << 4) | i; +	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) { +		psmouse_err(psmouse, "failed to enter command mode\n"); +		return -1;  	} -	for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) -		if (!memcmp(param, alps_model_data[i].signature, -			    sizeof(alps_model_data[i].signature))) -			return alps_model_data + i; +	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) && +	    param[0] != 0x73) { +		psmouse_dbg(psmouse, +			    "unknown response while entering command mode\n"); +		return -1; +	} +	return 0; +} -	return NULL; +static inline int alps_exit_command_mode(struct psmouse *psmouse) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) +		return -1; +	return 0;  }  /* @@ -480,7 +1223,7 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int   * subsequent commands. It looks like glidepad is behind stickpointer,   * I'd thought it would be other way around...   */ -static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) +static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable)  {  	struct ps2dev *ps2dev = &psmouse->ps2dev;  	int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; @@ -497,7 +1240,7 @@ static int alps_passthrough_mode(struct psmouse *psmouse, bool enable)  	return 0;  } -static int alps_absolute_mode(struct psmouse *psmouse) +static int alps_absolute_mode_v1_v2(struct psmouse *psmouse)  {  	struct ps2dev *ps2dev = &psmouse->ps2dev; @@ -516,18 +1259,85 @@ static int alps_absolute_mode(struct psmouse *psmouse)  	return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);  } -static int alps_get_status(struct psmouse *psmouse, char *param) +static int alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word) +{ +	int i, nibble; + +	/* +	 * b0-b11 are valid bits, send sequence is inverse. +	 * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1 +	 */ +	for (i = 0; i <= 8; i += 4) { +		nibble = (word >> i) & 0xf; +		if (alps_command_mode_send_nibble(psmouse, nibble)) +			return -1; +	} + +	return 0; +} + +static int alps_monitor_mode_write_reg(struct psmouse *psmouse, +				       u16 addr, u16 value)  {  	struct ps2dev *ps2dev = &psmouse->ps2dev; -	/* Get status: 0xF5 0xF5 0xF5 0xE9 */ -	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || -	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || -	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || -	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) +	/* 0x0A0 is the command to write the word */ +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE) || +	    alps_monitor_mode_send_word(psmouse, 0x0A0) || +	    alps_monitor_mode_send_word(psmouse, addr) || +	    alps_monitor_mode_send_word(psmouse, value) || +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))  		return -1; -	dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); +	return 0; +} + +static int alps_monitor_mode(struct psmouse *psmouse, bool enable) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; + +	if (enable) { +		/* EC E9 F5 F5 E7 E6 E7 E9 to enter monitor mode */ +		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO)) +			return -1; +	} else { +		/* EC to exit monitor mode */ +		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP)) +			return -1; +	} + +	return 0; +} + +static int alps_absolute_mode_v6(struct psmouse *psmouse) +{ +	u16 reg_val = 0x181; +	int ret = -1; + +	/* enter monitor mode, to write the register */ +	if (alps_monitor_mode(psmouse, true)) +		return -1; + +	ret = alps_monitor_mode_write_reg(psmouse, 0x000, reg_val); + +	if (alps_monitor_mode(psmouse, false)) +		ret = -1; + +	return ret; +} + +static int alps_get_status(struct psmouse *psmouse, char *param) +{ +	/* Get status: 0xF5 0xF5 0xF5 0xE9 */ +	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param)) +		return -1;  	return 0;  } @@ -567,19 +1377,19 @@ static int alps_tap_mode(struct psmouse *psmouse, int enable)  static int alps_poll(struct psmouse *psmouse)  {  	struct alps_data *priv = psmouse->private; -	unsigned char buf[6]; +	unsigned char buf[sizeof(psmouse->packet)];  	bool poll_failed; -	if (priv->i->flags & ALPS_PASS) -		alps_passthrough_mode(psmouse, true); +	if (priv->flags & ALPS_PASS) +		alps_passthrough_mode_v2(psmouse, true);  	poll_failed = ps2_command(&psmouse->ps2dev, buf,  				  PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; -	if (priv->i->flags & ALPS_PASS) -		alps_passthrough_mode(psmouse, false); +	if (priv->flags & ALPS_PASS) +		alps_passthrough_mode_v2(psmouse, false); -	if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) +	if (poll_failed || (buf[0] & priv->mask0) != priv->byte0)  		return -1;  	if ((psmouse->badbyte & 0xc8) == 0x08) { @@ -594,51 +1404,624 @@ static int alps_poll(struct psmouse *psmouse)  	return 0;  } -static int alps_hw_init(struct psmouse *psmouse) +static int alps_hw_init_v1_v2(struct psmouse *psmouse)  {  	struct alps_data *priv = psmouse->private; -	const struct alps_model_info *model = priv->i; -	if ((model->flags & ALPS_PASS) && -	    alps_passthrough_mode(psmouse, true)) { +	if ((priv->flags & ALPS_PASS) && +	    alps_passthrough_mode_v2(psmouse, true)) {  		return -1;  	}  	if (alps_tap_mode(psmouse, true)) { -		printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); +		psmouse_warn(psmouse, "Failed to enable hardware tapping\n");  		return -1;  	} -	if (alps_absolute_mode(psmouse)) { -		printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); +	if (alps_absolute_mode_v1_v2(psmouse)) { +		psmouse_err(psmouse, "Failed to enable absolute mode\n");  		return -1;  	} -	if ((model->flags & ALPS_PASS) && -	    alps_passthrough_mode(psmouse, false)) { +	if ((priv->flags & ALPS_PASS) && +	    alps_passthrough_mode_v2(psmouse, false)) {  		return -1;  	}  	/* ALPS needs stream mode, otherwise it won't report any data */  	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { -		printk(KERN_ERR "alps.c: Failed to enable stream mode\n"); +		psmouse_err(psmouse, "Failed to enable stream mode\n");  		return -1;  	}  	return 0;  } -static int alps_reconnect(struct psmouse *psmouse) +static int alps_hw_init_v6(struct psmouse *psmouse) +{ +	unsigned char param[2] = {0xC8, 0x14}; + +	/* Enter passthrough mode to let trackpoint enter 6byte raw mode */ +	if (alps_passthrough_mode_v2(psmouse, true)) +		return -1; + +	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +	    ps2_command(&psmouse->ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || +	    ps2_command(&psmouse->ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) +		return -1; + +	if (alps_passthrough_mode_v2(psmouse, false)) +		return -1; + +	if (alps_absolute_mode_v6(psmouse)) { +		psmouse_err(psmouse, "Failed to enable absolute mode\n"); +		return -1; +	} + +	return 0; +} + +/* + * Enable or disable passthrough mode to the trackstick. + */ +static int alps_passthrough_mode_v3(struct psmouse *psmouse, +				    int reg_base, bool enable) +{ +	int reg_val, ret = -1; + +	if (alps_enter_command_mode(psmouse)) +		return -1; + +	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008); +	if (reg_val == -1) +		goto error; + +	if (enable) +		reg_val |= 0x01; +	else +		reg_val &= ~0x01; + +	ret = __alps_command_mode_write_reg(psmouse, reg_val); + +error: +	if (alps_exit_command_mode(psmouse)) +		ret = -1; +	return ret; +} + +/* Must be in command mode when calling this function */ +static int alps_absolute_mode_v3(struct psmouse *psmouse) +{ +	int reg_val; + +	reg_val = alps_command_mode_read_reg(psmouse, 0x0004); +	if (reg_val == -1) +		return -1; + +	reg_val |= 0x06; +	if (__alps_command_mode_write_reg(psmouse, reg_val)) +		return -1; + +	return 0; +} + +static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base) +{ +	int ret = -EIO, reg_val; + +	if (alps_enter_command_mode(psmouse)) +		goto error; + +	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08); +	if (reg_val == -1) +		goto error; + +	/* bit 7: trackstick is present */ +	ret = reg_val & 0x80 ? 0 : -ENODEV; + +error: +	alps_exit_command_mode(psmouse); +	return ret; +} + +static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	int ret = 0; +	unsigned char param[4]; + +	if (alps_passthrough_mode_v3(psmouse, reg_base, true)) +		return -EIO; + +	/* +	 * E7 report for the trackstick +	 * +	 * There have been reports of failures to seem to trace back +	 * to the above trackstick check failing. When these occur +	 * this E7 report fails, so when that happens we continue +	 * with the assumption that there isn't a trackstick after +	 * all. +	 */ +	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) { +		psmouse_warn(psmouse, "trackstick E7 report failed\n"); +		ret = -ENODEV; +	} else { +		psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param); + +		/* +		 * Not sure what this does, but it is absolutely +		 * essential. Without it, the touchpad does not +		 * work at all and the trackstick just emits normal +		 * PS/2 packets. +		 */ +		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || +		    alps_command_mode_send_nibble(psmouse, 0x9) || +		    alps_command_mode_send_nibble(psmouse, 0x4)) { +			psmouse_err(psmouse, +				    "Error sending magic E6 sequence\n"); +			ret = -EIO; +			goto error; +		} + +		/* +		 * This ensures the trackstick packets are in the format +		 * supported by this driver. If bit 1 isn't set the packet +		 * format is different. +		 */ +		if (alps_enter_command_mode(psmouse) || +		    alps_command_mode_write_reg(psmouse, +						reg_base + 0x08, 0x82) || +		    alps_exit_command_mode(psmouse)) +			ret = -EIO; +	} + +error: +	if (alps_passthrough_mode_v3(psmouse, reg_base, false)) +		ret = -EIO; + +	return ret; +} + +static int alps_hw_init_v3(struct psmouse *psmouse) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	int reg_val; +	unsigned char param[4]; + +	reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); +	if (reg_val == -EIO) +		goto error; + +	if (reg_val == 0 && +	    alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) +		goto error; + +	if (alps_enter_command_mode(psmouse) || +	    alps_absolute_mode_v3(psmouse)) { +		psmouse_err(psmouse, "Failed to enter absolute mode\n"); +		goto error; +	} + +	reg_val = alps_command_mode_read_reg(psmouse, 0x0006); +	if (reg_val == -1) +		goto error; +	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) +		goto error; + +	reg_val = alps_command_mode_read_reg(psmouse, 0x0007); +	if (reg_val == -1) +		goto error; +	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) +		goto error; + +	if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) +		goto error; +	if (__alps_command_mode_write_reg(psmouse, 0x04)) +		goto error; + +	if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) +		goto error; +	if (__alps_command_mode_write_reg(psmouse, 0x03)) +		goto error; + +	if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) +		goto error; +	if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) +		goto error; + +	if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) +		goto error; +	if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) +		goto error; + +	alps_exit_command_mode(psmouse); + +	/* Set rate and enable data reporting */ +	param[0] = 0x64; +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { +		psmouse_err(psmouse, "Failed to enable data reporting\n"); +		return -1; +	} + +	return 0; + +error: +	/* +	 * Leaving the touchpad in command mode will essentially render +	 * it unusable until the machine reboots, so exit it here just +	 * to be safe +	 */ +	alps_exit_command_mode(psmouse); +	return -1; +} + +static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) +{ +	struct alps_data *priv = psmouse->private; +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	int reg_val, ret = -1; + +	if (priv->flags & ALPS_DUALPOINT) { +		reg_val = alps_setup_trackstick_v3(psmouse, +						   ALPS_REG_BASE_RUSHMORE); +		if (reg_val == -EIO) +			goto error; +		if (reg_val == -ENODEV) +			priv->flags &= ~ALPS_DUALPOINT; +	} + +	if (alps_enter_command_mode(psmouse) || +	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 || +	    alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00)) +		goto error; + +	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6); +	if (reg_val == -1) +		goto error; +	if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) +		goto error; + +	/* enter absolute mode */ +	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); +	if (reg_val == -1) +		goto error; +	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) +		goto error; + +	alps_exit_command_mode(psmouse); +	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: +	alps_exit_command_mode(psmouse); +	return ret; +} + +/* Must be in command mode when calling this function */ +static int alps_absolute_mode_v4(struct psmouse *psmouse) +{ +	int reg_val; + +	reg_val = alps_command_mode_read_reg(psmouse, 0x0004); +	if (reg_val == -1) +		return -1; + +	reg_val |= 0x02; +	if (__alps_command_mode_write_reg(psmouse, reg_val)) +		return -1; + +	return 0; +} + +static int alps_hw_init_v4(struct psmouse *psmouse) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	unsigned char param[4]; + +	if (alps_enter_command_mode(psmouse)) +		goto error; + +	if (alps_absolute_mode_v4(psmouse)) { +		psmouse_err(psmouse, "Failed to enter absolute mode\n"); +		goto error; +	} + +	if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03)) +		goto error; + +	if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03)) +		goto error; + +	alps_exit_command_mode(psmouse); + +	/* +	 * This sequence changes the output from a 9-byte to an +	 * 8-byte format. All the same data seems to be present, +	 * just in a more compact format. +	 */ +	param[0] = 0xc8; +	param[1] = 0x64; +	param[2] = 0x50; +	if (ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, ¶m[2], PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) +		return -1; + +	/* Set rate and enable data reporting */ +	param[0] = 0x64; +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { +		psmouse_err(psmouse, "Failed to enable data reporting\n"); +		return -1; +	} + +	return 0; + +error: +	/* +	 * Leaving the touchpad in command mode will essentially render +	 * it unusable until the machine reboots, so exit it here just +	 * to be safe +	 */ +	alps_exit_command_mode(psmouse); +	return -1; +} + +static int alps_dolphin_get_device_area(struct psmouse *psmouse, +					struct alps_data *priv) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	unsigned char param[4] = {0}; +	int num_x_electrode, num_y_electrode; + +	if (alps_enter_command_mode(psmouse)) +		return -1; + +	param[0] = 0x0a; +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) || +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) || +	    ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE)) +		return -1; + +	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) +		return -1; + +	/* +	 * Dolphin's sensor line number is not fixed. It can be calculated +	 * by adding the device's register value with DOLPHIN_PROFILE_X/YOFFSET. +	 * Further more, we can get device's x_max and y_max by multiplying +	 * sensor line number with DOLPHIN_COUNT_PER_ELECTRODE. +	 * +	 * e.g. When we get register's sensor_x = 11 & sensor_y = 8, +	 *	real sensor line number X = 11 + 8 = 19, and +	 *	real sensor line number Y = 8 + 1 = 9. +	 *	So, x_max = (19 - 1) * 64 = 1152, and +	 *	    y_max = (9 - 1) * 64 = 512. +	 */ +	num_x_electrode = DOLPHIN_PROFILE_XOFFSET + (param[2] & 0x0F); +	num_y_electrode = DOLPHIN_PROFILE_YOFFSET + ((param[2] >> 4) & 0x0F); +	priv->x_bits = num_x_electrode; +	priv->y_bits = num_y_electrode; +	priv->x_max = (num_x_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE; +	priv->y_max = (num_y_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE; + +	if (alps_exit_command_mode(psmouse)) +		return -1; + +	return 0; +} + +static int alps_hw_init_dolphin_v1(struct psmouse *psmouse) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	unsigned char param[2]; + +	/* This is dolphin "v1" as empirically defined by florin9doi */ +	param[0] = 0x64; +	param[1] = 0x28; + +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || +	    ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) +		return -1; + +	return 0; +} + +static void alps_set_defaults(struct alps_data *priv) +{ +	priv->byte0 = 0x8f; +	priv->mask0 = 0x8f; +	priv->flags = ALPS_DUALPOINT; + +	priv->x_max = 2000; +	priv->y_max = 1400; +	priv->x_bits = 15; +	priv->y_bits = 11; + +	switch (priv->proto_version) { +	case ALPS_PROTO_V1: +	case ALPS_PROTO_V2: +		priv->hw_init = alps_hw_init_v1_v2; +		priv->process_packet = alps_process_packet_v1_v2; +		priv->set_abs_params = alps_set_abs_params_st; +		priv->x_max = 1023; +		priv->y_max = 767; +		break; +	case ALPS_PROTO_V3: +		priv->hw_init = alps_hw_init_v3; +		priv->process_packet = alps_process_packet_v3; +		priv->set_abs_params = alps_set_abs_params_mt; +		priv->decode_fields = alps_decode_pinnacle; +		priv->nibble_commands = alps_v3_nibble_commands; +		priv->addr_command = PSMOUSE_CMD_RESET_WRAP; +		break; +	case ALPS_PROTO_V4: +		priv->hw_init = alps_hw_init_v4; +		priv->process_packet = alps_process_packet_v4; +		priv->set_abs_params = alps_set_abs_params_mt; +		priv->nibble_commands = alps_v4_nibble_commands; +		priv->addr_command = PSMOUSE_CMD_DISABLE; +		break; +	case ALPS_PROTO_V5: +		priv->hw_init = alps_hw_init_dolphin_v1; +		priv->process_packet = alps_process_touchpad_packet_v3_v5; +		priv->decode_fields = alps_decode_dolphin; +		priv->set_abs_params = alps_set_abs_params_mt; +		priv->nibble_commands = alps_v3_nibble_commands; +		priv->addr_command = PSMOUSE_CMD_RESET_WRAP; +		priv->byte0 = 0xc8; +		priv->mask0 = 0xd8; +		priv->flags = 0; +		priv->x_max = 1360; +		priv->y_max = 660; +		priv->x_bits = 23; +		priv->y_bits = 12; +		break; +	case ALPS_PROTO_V6: +		priv->hw_init = alps_hw_init_v6; +		priv->process_packet = alps_process_packet_v6; +		priv->set_abs_params = alps_set_abs_params_st; +		priv->nibble_commands = alps_v6_nibble_commands; +		priv->x_max = 2047; +		priv->y_max = 1535; +		break; +	} +} + +static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv, +			    unsigned char *e7, unsigned char *ec)  {  	const struct alps_model_info *model; +	int i; + +	for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { +		model = &alps_model_data[i]; + +		if (!memcmp(e7, model->signature, sizeof(model->signature)) && +		    (!model->command_mode_resp || +		     model->command_mode_resp == ec[2])) { + +			priv->proto_version = model->proto_version; +			alps_set_defaults(priv); + +			priv->flags = model->flags; +			priv->byte0 = model->byte0; +			priv->mask0 = model->mask0; + +			return 0; +		} +	} + +	return -EINVAL; +} + +static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) +{ +	unsigned char e6[4], e7[4], ec[4]; + +	/* +	 * First try "E6 report". +	 * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. +	 * The bits 0-2 of the first byte will be 1s if some buttons are +	 * pressed. +	 */ +	if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, +			 PSMOUSE_CMD_SETSCALE11, e6)) +		return -EIO; + +	if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100)) +		return -EINVAL; + +	/* +	 * Now get the "E7" and "EC" reports.  These will uniquely identify +	 * most ALPS touchpads. +	 */ +	if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, +			 PSMOUSE_CMD_SETSCALE21, e7) || +	    alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, +			 PSMOUSE_CMD_RESET_WRAP, ec) || +	    alps_exit_command_mode(psmouse)) +		return -EIO; + +	if (alps_match_table(psmouse, priv, e7, ec) == 0) { +		return 0; +	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 && +		   ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) { +		priv->proto_version = ALPS_PROTO_V5; +		alps_set_defaults(priv); +		if (alps_dolphin_get_device_area(psmouse, priv)) +			return -EIO; +		else +			return 0; +	} else if (ec[0] == 0x88 && ec[1] == 0x08) { +		priv->proto_version = ALPS_PROTO_V3; +		alps_set_defaults(priv); + +		priv->hw_init = alps_hw_init_rushmore_v3; +		priv->decode_fields = alps_decode_rushmore; +		priv->x_bits = 16; +		priv->y_bits = 12; + +		/* hack to make addr_command, nibble_command available */ +		psmouse->private = priv; + +		if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE)) +			priv->flags &= ~ALPS_DUALPOINT; + +		return 0; +	} else if (ec[0] == 0x88 && ec[1] == 0x07 && +		   ec[2] >= 0x90 && ec[2] <= 0x9d) { +		priv->proto_version = ALPS_PROTO_V3; +		alps_set_defaults(priv); + +		return 0; +	} + +	psmouse_info(psmouse, +		     "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); + +	return -EINVAL; +} + +static int alps_reconnect(struct psmouse *psmouse) +{ +	struct alps_data *priv = psmouse->private;  	psmouse_reset(psmouse); -	model = alps_get_model(psmouse, NULL); -	if (!model) +	if (alps_identify(psmouse, priv) < 0)  		return -1; -	return alps_hw_init(psmouse); +	return priv->hw_init(psmouse);  }  static void alps_disconnect(struct psmouse *psmouse) @@ -651,12 +2034,33 @@ static void alps_disconnect(struct psmouse *psmouse)  	kfree(priv);  } +static void alps_set_abs_params_st(struct alps_data *priv, +				   struct input_dev *dev1) +{ +	input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); +	input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); +} + +static void alps_set_abs_params_mt(struct alps_data *priv, +				   struct input_dev *dev1) +{ +	set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); +	input_mt_init_slots(dev1, 2, 0); +	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); +	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); + +	set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); +	set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); +	set_bit(BTN_TOOL_QUADTAP, dev1->keybit); + +	input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); +	input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); +} +  int alps_init(struct psmouse *psmouse)  {  	struct alps_data *priv; -	const struct alps_model_info *model;  	struct input_dev *dev1 = psmouse->dev, *dev2; -	int version;  	priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);  	dev2 = input_allocate_device(); @@ -668,13 +2072,12 @@ int alps_init(struct psmouse *psmouse)  	psmouse->private = priv; -	model = alps_get_model(psmouse, &version); -	if (!model) -		goto init_fail; +	psmouse_reset(psmouse); -	priv->i = model; +	if (alps_identify(psmouse, priv) < 0) +		goto init_fail; -	if (alps_hw_init(psmouse)) +	if (priv->hw_init(psmouse))  		goto init_fail;  	/* @@ -695,21 +2098,21 @@ int alps_init(struct psmouse *psmouse)  		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);  	dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); -	input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); -	input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); + +	priv->set_abs_params(priv, dev1);  	input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); -	if (model->flags & ALPS_WHEEL) { +	if (priv->flags & ALPS_WHEEL) {  		dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);  		dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);  	} -	if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { +	if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {  		dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);  		dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);  	} -	if (model->flags & ALPS_FOUR_BUTTONS) { +	if (priv->flags & ALPS_FOUR_BUTTONS) {  		dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);  		dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);  		dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); @@ -720,7 +2123,8 @@ int alps_init(struct psmouse *psmouse)  	snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);  	dev2->phys = priv->phys; -	dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; +	dev2->name = (priv->flags & ALPS_DUALPOINT) ? +		     "DualPoint Stick" : "ALPS PS/2 Device";  	dev2->id.bustype = BUS_I8042;  	dev2->id.vendor  = 0x0002;  	dev2->id.product = PSMOUSE_ALPS; @@ -739,7 +2143,7 @@ int alps_init(struct psmouse *psmouse)  	psmouse->poll = alps_poll;  	psmouse->disconnect = alps_disconnect;  	psmouse->reconnect = alps_reconnect; -	psmouse->pktsize = 6; +	psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6;  	/* We are having trouble resyncing ALPS touchpads so disable it for now */  	psmouse->resync_time = 0; @@ -756,18 +2160,16 @@ init_fail:  int alps_detect(struct psmouse *psmouse, bool set_properties)  { -	int version; -	const struct alps_model_info *model; +	struct alps_data dummy; -	model = alps_get_model(psmouse, &version); -	if (!model) +	if (alps_identify(psmouse, &dummy) < 0)  		return -1;  	if (set_properties) {  		psmouse->vendor = "ALPS"; -		psmouse->name = model->flags & ALPS_DUALPOINT ? +		psmouse->name = dummy.flags & ALPS_DUALPOINT ?  				"DualPoint TouchPad" : "GlidePoint"; -		psmouse->model = version; +		psmouse->model = dummy.proto_version << 8;  	}  	return 0;  } diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 904ed8b3c8b..03f88b6940c 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,20 +12,159 @@  #ifndef _ALPS_H  #define _ALPS_H +#define ALPS_PROTO_V1	1 +#define ALPS_PROTO_V2	2 +#define ALPS_PROTO_V3	3 +#define ALPS_PROTO_V4	4 +#define ALPS_PROTO_V5	5 +#define ALPS_PROTO_V6	6 + +#define DOLPHIN_COUNT_PER_ELECTRODE	64 +#define DOLPHIN_PROFILE_XOFFSET		8	/* x-electrode offset */ +#define DOLPHIN_PROFILE_YOFFSET		1	/* y-electrode offset */ + +/** + * struct alps_model_info - touchpad ID table + * @signature: E7 response string to match. + * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response + *   (aka command mode response) identifies the firmware minor version.  This + *   can be used to distinguish different hardware models which are not + *   uniquely identifiable through their E7 responses. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + *   known format for this model.  The first byte of the report, ANDed with + *   mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * + * Many (but not all) ALPS touchpads can be identified by looking at the + * values returned in the "E7 report" and/or the "EC report."  This table + * lists a number of such touchpads. + */  struct alps_model_info { -        unsigned char signature[3]; -        unsigned char byte0, mask0; -        unsigned char flags; +	unsigned char signature[3]; +	unsigned char command_mode_resp; +	unsigned char proto_version; +	unsigned char byte0, mask0; +	unsigned char flags; +}; + +/** + * struct alps_nibble_commands - encodings for register accesses + * @command: PS/2 command used for the nibble + * @data: Data supplied as an argument to the PS/2 command, if applicable + * + * The ALPS protocol uses magic sequences to transmit binary data to the + * touchpad, as it is generally not OK to send arbitrary bytes out the + * PS/2 port.  Each of the sequences in this table sends one nibble of the + * register address or (write) data.  Different versions of the ALPS protocol + * use slightly different encodings. + */ +struct alps_nibble_commands { +	int command; +	unsigned char data; +}; + +/** + * struct alps_fields - decoded version of the report packet + * @x_map: Bitmap of active X positions for MT. + * @y_map: Bitmap of active Y positions for MT. + * @fingers: Number of fingers for MT. + * @x: X position for ST. + * @y: Y position for ST. + * @z: Z position for ST. + * @first_mp: Packet is the first of a multi-packet report. + * @is_mp: Packet is part of a multi-packet report. + * @left: Left touchpad button is active. + * @right: Right touchpad button is active. + * @middle: Middle touchpad button is active. + * @ts_left: Left trackstick button is active. + * @ts_right: Right trackstick button is active. + * @ts_middle: Middle trackstick button is active. + */ +struct alps_fields { +	unsigned int x_map; +	unsigned int y_map; +	unsigned int fingers; +	unsigned int x; +	unsigned int y; +	unsigned int z; +	unsigned int first_mp:1; +	unsigned int is_mp:1; + +	unsigned int left:1; +	unsigned int right:1; +	unsigned int middle:1; + +	unsigned int ts_left:1; +	unsigned int ts_right:1; +	unsigned int ts_middle:1;  }; +/** + * struct alps_data - private data structure for the ALPS driver + * @dev2: "Relative" device used to report trackstick or mouse activity. + * @phys: Physical path for the relative device. + * @nibble_commands: Command mapping used for touchpad register accesses. + * @addr_command: Command used to tell the touchpad that a register address + *   follows. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + *   known format for this model.  The first byte of the report, ANDed with + *   mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * @x_max: Largest possible X position value. + * @y_max: Largest possible Y position value. + * @x_bits: Number of X bits in the MT bitmap. + * @y_bits: Number of Y bits in the MT bitmap. + * @hw_init: Protocol-specific hardware init function. + * @process_packet: Protocol-specific function to process a report packet. + * @decode_fields: Protocol-specific function to read packet bitfields. + * @set_abs_params: Protocol-specific function to configure the input_dev. + * @prev_fin: Finger bit from previous packet. + * @multi_packet: Multi-packet data in progress. + * @multi_data: Saved multi-packet data. + * @x1: First X coordinate from last MT report. + * @x2: Second X coordinate from last MT report. + * @y1: First Y coordinate from last MT report. + * @y2: Second Y coordinate from last MT report. + * @fingers: Number of fingers from last MT report. + * @quirks: Bitmap of ALPS_QUIRK_*. + * @timer: Timer for flushing out the final report packet in the stream. + */  struct alps_data { -	struct input_dev *dev2;		/* Relative device */ -	char phys[32];			/* Phys */ -	const struct alps_model_info *i;/* Info */ -	int prev_fin;			/* Finger bit from previous packet */ +	struct input_dev *dev2; +	char phys[32]; + +	/* these are autodetected when the device is identified */ +	const struct alps_nibble_commands *nibble_commands; +	int addr_command; +	unsigned char proto_version; +	unsigned char byte0, mask0; +	unsigned char flags; +	int x_max; +	int y_max; +	int x_bits; +	int y_bits; + +	int (*hw_init)(struct psmouse *psmouse); +	void (*process_packet)(struct psmouse *psmouse); +	void (*decode_fields)(struct alps_fields *f, unsigned char *p, +			      struct psmouse *psmouse); +	void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1); + +	int prev_fin; +	int multi_packet; +	unsigned char multi_data[6]; +	int x1, x2, y1, y2; +	int fingers; +	u8 quirks;  	struct timer_list timer;  }; +#define ALPS_QUIRK_TRACKSTICK_BUTTONS	1 /* trakcstick buttons in trackstick packet */ +  #ifdef CONFIG_MOUSE_PS2_ALPS  int alps_detect(struct psmouse *psmouse, bool set_properties);  int alps_init(struct psmouse *psmouse); diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index ff5f61a0fd3..62ec52b2e34 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -25,7 +25,6 @@  #include <asm/irq.h>  #include <asm/setup.h> -#include <asm/system.h>  #include <asm/uaccess.h>  #include <asm/amigahw.h>  #include <asm/amigaints.h> @@ -134,7 +133,6 @@ static int __exit amimouse_remove(struct platform_device *pdev)  {  	struct input_dev *dev = platform_get_drvdata(pdev); -	platform_set_drvdata(pdev, NULL);  	input_unregister_device(dev);  	return 0;  } @@ -147,18 +145,6 @@ static struct platform_driver amimouse_driver = {  	},  }; -static int __init amimouse_init(void) -{ -	return platform_driver_probe(&amimouse_driver, amimouse_probe); -} - -module_init(amimouse_init); - -static void __exit amimouse_exit(void) -{ -	platform_driver_unregister(&amimouse_driver); -} - -module_exit(amimouse_exit); +module_platform_driver_probe(amimouse_driver, amimouse_probe);  MODULE_ALIAS("platform:amiga-mouse"); diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index b77f9991278..ef234c9b2f2 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -30,7 +30,6 @@  #include <linux/kernel.h>  #include <linux/errno.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/usb/input.h> @@ -49,6 +48,7 @@ struct atp_info {  	int yfact;				/* Y multiplication factor */  	int datalen;				/* size of USB transfers */  	void (*callback)(struct urb *);		/* callback function */ +	int fuzz;				/* fuzz touchpad generates */  };  static void atp_complete_geyser_1_2(struct urb *urb); @@ -62,6 +62,7 @@ static const struct atp_info fountain_info = {  	.yfact		= 43,  	.datalen	= 81,  	.callback	= atp_complete_geyser_1_2, +	.fuzz		= 16,  };  static const struct atp_info geyser1_info = { @@ -72,6 +73,7 @@ static const struct atp_info geyser1_info = {  	.yfact		= 43,  	.datalen	= 81,  	.callback	= atp_complete_geyser_1_2, +	.fuzz		= 16,  };  static const struct atp_info geyser2_info = { @@ -82,6 +84,7 @@ static const struct atp_info geyser2_info = {  	.yfact		= 43,  	.datalen	= 64,  	.callback	= atp_complete_geyser_1_2, +	.fuzz		= 0,  };  static const struct atp_info geyser3_info = { @@ -91,6 +94,7 @@ static const struct atp_info geyser3_info = {  	.yfact		= 64,  	.datalen	= 64,  	.callback	= atp_complete_geyser_3_4, +	.fuzz		= 0,  };  static const struct atp_info geyser4_info = { @@ -100,6 +104,7 @@ static const struct atp_info geyser4_info = {  	.yfact		= 64,  	.datalen	= 64,  	.callback	= atp_complete_geyser_3_4, +	.fuzz		= 0,  };  #define ATP_DEVICE(prod, info)					\ @@ -156,8 +161,11 @@ MODULE_DEVICE_TABLE(usb, atp_table);  #define ATP_XSENSORS	26  #define ATP_YSENSORS	16 -/* amount of fuzz this touchpad generates */ -#define ATP_FUZZ	16 +/* + * The largest possible bank of sensors with additional buffer of 4 extra values + * on either side, for an array of smoothed sensor values. + */ +#define ATP_SMOOTHSIZE	34  /* maximum pressure this driver will report */  #define ATP_PRESSURE	300 @@ -166,7 +174,13 @@ MODULE_DEVICE_TABLE(usb, atp_table);   * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is   * ignored.   */ -#define ATP_THRESHOLD	 5 +#define ATP_THRESHOLD	5 + +/* + * How far we'll bitshift our sensor values before averaging them. Mitigates + * rounding errors. + */ +#define ATP_SCALE	12  /* Geyser initialization constants */  #define ATP_GEYSER_MODE_READ_REQUEST_ID		1 @@ -195,6 +209,7 @@ enum atp_status_bits {  struct atp {  	char			phys[64];  	struct usb_device	*udev;		/* usb device */ +	struct usb_interface	*intf;		/* usb interface */  	struct urb		*urb;		/* usb request block */  	u8			*data;		/* transferred data */  	struct input_dev	*input;		/* input dev */ @@ -203,11 +218,14 @@ struct atp {  	bool			valid;		/* are the samples valid? */  	bool			size_detect_done;  	bool			overflow_warned; +	int			fingers_old;	/* last reported finger count */  	int			x_old;		/* last reported x/y, */  	int			y_old;		/* used for smoothing */  	signed char		xy_cur[ATP_XSENSORS + ATP_YSENSORS];  	signed char		xy_old[ATP_XSENSORS + ATP_YSENSORS];  	int			xy_acc[ATP_XSENSORS + ATP_YSENSORS]; +	int			smooth[ATP_SMOOTHSIZE]; +	int			smooth_tmp[ATP_SMOOTHSIZE];  	int			idlecount;	/* number of empty packets */  	struct work_struct	work;  }; @@ -253,8 +271,9 @@ MODULE_PARM_DESC(debug, "Activate debugging output");   * packets (Report ID 2). This code changes device mode, so it   * sends raw sensor reports (Report ID 5).   */ -static int atp_geyser_init(struct usb_device *udev) +static int atp_geyser_init(struct atp *dev)  { +	struct usb_device *udev = dev->udev;  	char *data;  	int size;  	int i; @@ -262,7 +281,7 @@ static int atp_geyser_init(struct usb_device *udev)  	data = kmalloc(8, GFP_KERNEL);  	if (!data) { -		err("Out of memory"); +		dev_err(&dev->intf->dev, "Out of memory\n");  		return -ENOMEM;  	} @@ -277,7 +296,7 @@ static int atp_geyser_init(struct usb_device *udev)  		for (i = 0; i < 8; i++)  			dprintk("appletouch[%d]: %d\n", i, data[i]); -		err("Failed to read mode from device."); +		dev_err(&dev->intf->dev, "Failed to read mode from device.\n");  		ret = -EIO;  		goto out_free;  	} @@ -296,7 +315,7 @@ static int atp_geyser_init(struct usb_device *udev)  		for (i = 0; i < 8; i++)  			dprintk("appletouch[%d]: %d\n", i, data[i]); -		err("Failed to request geyser raw mode"); +		dev_err(&dev->intf->dev, "Failed to request geyser raw mode\n");  		ret = -EIO;  		goto out_free;  	} @@ -313,22 +332,29 @@ out_free:  static void atp_reinit(struct work_struct *work)  {  	struct atp *dev = container_of(work, struct atp, work); -	struct usb_device *udev = dev->udev;  	int retval;  	dprintk("appletouch: putting appletouch to sleep (reinit)\n"); -	atp_geyser_init(udev); +	atp_geyser_init(dev);  	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);  	if (retval) -		err("atp_reinit: usb_submit_urb failed with error %d", -		    retval); +		dev_err(&dev->intf->dev, +			"atp_reinit: usb_submit_urb failed with error %d\n", +			retval);  } -static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, -			     int *z, int *fingers) +static int atp_calculate_abs(struct atp *dev, int offset, int nb_sensors, +			     int fact, int *z, int *fingers)  { -	int i; +	int i, pass; + +	/* +	 * Use offset to point xy_sensors at the first value in dev->xy_acc +	 * for whichever dimension we're looking at this particular go-round. +	 */ +	int *xy_sensors = dev->xy_acc + offset; +  	/* values to calculate mean */  	int pcum = 0, psum = 0;  	int is_increasing = 0; @@ -340,9 +366,6 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,  			if (is_increasing)  				is_increasing = 0; -			continue; -		} -  		/*  		 * Makes the finger detection more versatile.  For example,  		 * two fingers with no gap will be detected.  Also, my @@ -357,27 +380,63 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,  		 *  		 * - Jason Parekh <jasonparekh@gmail.com>  		 */ -		if (i < 1 || + +		} else if (i < 1 ||  		    (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {  			(*fingers)++;  			is_increasing = 1;  		} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {  			is_increasing = 0;  		} +	} + +	if (*fingers < 1)     /* No need to continue if no fingers are found. */ +		return 0; + +	/* +	 * Use a smoothed version of sensor data for movement calculations, to +	 * combat noise without needing to rely so heavily on a threshold. +	 * This improves tracking. +	 * +	 * The smoothed array is bigger than the original so that the smoothing +	 * doesn't result in edge values being truncated. +	 */ + +	memset(dev->smooth, 0, 4 * sizeof(dev->smooth[0])); +	/* Pull base values, scaled up to help avoid truncation errors. */ +	for (i = 0; i < nb_sensors; i++) +		dev->smooth[i + 4] = xy_sensors[i] << ATP_SCALE; +	memset(&dev->smooth[nb_sensors + 4], 0, 4 * sizeof(dev->smooth[0])); +	for (pass = 0; pass < 4; pass++) { +		/* Handle edge. */ +		dev->smooth_tmp[0] = (dev->smooth[0] + dev->smooth[1]) / 2; + +		/* Average values with neighbors. */ +		for (i = 1; i < nb_sensors + 7; i++) +			dev->smooth_tmp[i] = (dev->smooth[i - 1] + +					      dev->smooth[i] * 2 + +					      dev->smooth[i + 1]) / 4; + +		/* Handle other edge. */ +		dev->smooth_tmp[i] = (dev->smooth[i - 1] + dev->smooth[i]) / 2; + +		memcpy(dev->smooth, dev->smooth_tmp, sizeof(dev->smooth)); +	} + +	for (i = 0; i < nb_sensors + 8; i++) {  		/* -		 * Subtracts threshold so a high sensor that just passes the -		 * threshold won't skew the calculated absolute coordinate. -		 * Fixes an issue where slowly moving the mouse would -		 * occasionally jump a number of pixels (slowly moving the -		 * finger makes this issue most apparent.) +		 * Skip values if they're small enough to be truncated to 0 +		 * by scale. Mostly noise.  		 */ -		pcum += (xy_sensors[i] - threshold) * i; -		psum += (xy_sensors[i] - threshold); +		if ((dev->smooth[i] >> ATP_SCALE) > 0) { +			pcum += dev->smooth[i] * i; +			psum += dev->smooth[i]; +		}  	}  	if (psum > 0) { -		*z = psum; +		*z = psum >> ATP_SCALE;        /* Scale down pressure output. */  		return pcum * fact / psum;  	} @@ -400,6 +459,7 @@ static inline void atp_report_fingers(struct input_dev *input, int fingers)  static int atp_status_check(struct urb *urb)  {  	struct atp *dev = urb->context; +	struct usb_interface *intf = dev->intf;  	switch (urb->status) {  	case 0: @@ -407,8 +467,8 @@ static int atp_status_check(struct urb *urb)  		break;  	case -EOVERFLOW:  		if (!dev->overflow_warned) { -			printk(KERN_WARNING "appletouch: OVERFLOW with data " -				"length %d, actual length is %d\n", +			dev_warn(&intf->dev, +				"appletouch: OVERFLOW with data length %d, actual length is %d\n",  				dev->info->datalen, dev->urb->actual_length);  			dev->overflow_warned = true;  		} @@ -416,13 +476,15 @@ static int atp_status_check(struct urb *urb)  	case -ENOENT:  	case -ESHUTDOWN:  		/* This urb is terminated, clean up */ -		dbg("atp_complete: urb shutting down with status: %d", -		    urb->status); +		dev_dbg(&intf->dev, +			"atp_complete: urb shutting down with status: %d\n", +			urb->status);  		return ATP_URB_STATUS_ERROR_FATAL;  	default: -		dbg("atp_complete: nonzero urb status received: %d", -		    urb->status); +		dev_dbg(&intf->dev, +			"atp_complete: nonzero urb status received: %d\n", +			urb->status);  		return ATP_URB_STATUS_ERROR;  	} @@ -445,12 +507,13 @@ static void atp_detect_size(struct atp *dev)  	for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {  		if (dev->xy_cur[i]) { -			printk(KERN_INFO "appletouch: 17\" model detected.\n"); +			dev_info(&dev->intf->dev, +				"appletouch: 17\" model detected.\n");  			input_set_abs_params(dev->input, ABS_X, 0,  					     (dev->info->xsensors_17 - 1) *  							dev->info->xfact - 1, -					     ATP_FUZZ, 0); +					     dev->info->fuzz, 0);  			break;  		}  	} @@ -466,7 +529,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)  {  	int x, y, x_z, y_z, x_f, y_f;  	int retval, i, j; -	int key; +	int key, fingers;  	struct atp *dev = urb->context;  	int status = atp_status_check(urb); @@ -543,16 +606,18 @@ static void atp_complete_geyser_1_2(struct urb *urb)  	dbg_dump("accumulator", dev->xy_acc); -	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, +	x = atp_calculate_abs(dev, 0, ATP_XSENSORS,  			      dev->info->xfact, &x_z, &x_f); -	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, +	y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,  			      dev->info->yfact, &y_z, &y_f);  	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; -	if (x && y) { +	fingers = max(x_f, y_f); + +	if (x && y && fingers == dev->fingers_old) {  		if (dev->x_old != -1) { -			x = (dev->x_old * 3 + x) >> 2; -			y = (dev->y_old * 3 + y) >> 2; +			x = (dev->x_old * 7 + x) >> 3; +			y = (dev->y_old * 7 + y) >> 3;  			dev->x_old = x;  			dev->y_old = y; @@ -566,7 +631,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)  			input_report_abs(dev->input, ABS_Y, y);  			input_report_abs(dev->input, ABS_PRESSURE,  					 min(ATP_PRESSURE, x_z + y_z)); -			atp_report_fingers(dev->input, max(x_f, y_f)); +			atp_report_fingers(dev->input, fingers);  		}  		dev->x_old = x;  		dev->y_old = y; @@ -574,6 +639,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)  	} else if (!x && !y) {  		dev->x_old = dev->y_old = -1; +		dev->fingers_old = 0;  		input_report_key(dev->input, BTN_TOUCH, 0);  		input_report_abs(dev->input, ABS_PRESSURE, 0);  		atp_report_fingers(dev->input, 0); @@ -582,14 +648,19 @@ static void atp_complete_geyser_1_2(struct urb *urb)  		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));  	} +	if (fingers != dev->fingers_old) +		dev->x_old = dev->y_old = -1; +	dev->fingers_old = fingers; +  	input_report_key(dev->input, BTN_LEFT, key);  	input_sync(dev->input);   exit:  	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);  	if (retval) -		err("atp_complete: usb_submit_urb failed with result %d", -		    retval); +		dev_err(&dev->intf->dev, +			"atp_complete: usb_submit_urb failed with result %d\n", +			retval);  }  /* Interrupt function for older touchpads: GEYSER3/GEYSER4 */ @@ -598,7 +669,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)  {  	int x, y, x_z, y_z, x_f, y_f;  	int retval, i, j; -	int key; +	int key, fingers;  	struct atp *dev = urb->context;  	int status = atp_status_check(urb); @@ -654,16 +725,19 @@ static void atp_complete_geyser_3_4(struct urb *urb)  	dbg_dump("accumulator", dev->xy_acc); -	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, +	x = atp_calculate_abs(dev, 0, ATP_XSENSORS,  			      dev->info->xfact, &x_z, &x_f); -	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, +	y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,  			      dev->info->yfact, &y_z, &y_f); +  	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; -	if (x && y) { +	fingers = max(x_f, y_f); + +	if (x && y && fingers == dev->fingers_old) {  		if (dev->x_old != -1) { -			x = (dev->x_old * 3 + x) >> 2; -			y = (dev->y_old * 3 + y) >> 2; +			x = (dev->x_old * 7 + x) >> 3; +			y = (dev->y_old * 7 + y) >> 3;  			dev->x_old = x;  			dev->y_old = y; @@ -677,7 +751,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)  			input_report_abs(dev->input, ABS_Y, y);  			input_report_abs(dev->input, ABS_PRESSURE,  					 min(ATP_PRESSURE, x_z + y_z)); -			atp_report_fingers(dev->input, max(x_f, y_f)); +			atp_report_fingers(dev->input, fingers);  		}  		dev->x_old = x;  		dev->y_old = y; @@ -685,6 +759,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)  	} else if (!x && !y) {  		dev->x_old = dev->y_old = -1; +		dev->fingers_old = 0;  		input_report_key(dev->input, BTN_TOUCH, 0);  		input_report_abs(dev->input, ABS_PRESSURE, 0);  		atp_report_fingers(dev->input, 0); @@ -693,6 +768,10 @@ static void atp_complete_geyser_3_4(struct urb *urb)  		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));  	} +	if (fingers != dev->fingers_old) +		dev->x_old = dev->y_old = -1; +	dev->fingers_old = fingers; +  	input_report_key(dev->input, BTN_LEFT, key);  	input_sync(dev->input); @@ -722,8 +801,9 @@ static void atp_complete_geyser_3_4(struct urb *urb)   exit:  	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);  	if (retval) -		err("atp_complete: usb_submit_urb failed with result %d", -		    retval); +		dev_err(&dev->intf->dev, +			"atp_complete: usb_submit_urb failed with result %d\n", +			retval);  }  static int atp_open(struct input_dev *input) @@ -748,14 +828,12 @@ static void atp_close(struct input_dev *input)  static int atp_handle_geyser(struct atp *dev)  { -	struct usb_device *udev = dev->udev; -  	if (dev->info != &fountain_info) {  		/* switch to raw sensor mode */ -		if (atp_geyser_init(udev)) +		if (atp_geyser_init(dev))  			return -EIO; -		printk(KERN_INFO "appletouch: Geyser mode initialized.\n"); +		dev_info(&dev->intf->dev, "Geyser mode initialized.\n");  	}  	return 0; @@ -785,7 +863,7 @@ static int atp_probe(struct usb_interface *iface,  		}  	}  	if (!int_in_endpointAddr) { -		err("Could not find int-in endpoint"); +		dev_err(&iface->dev, "Could not find int-in endpoint\n");  		return -EIO;  	} @@ -793,11 +871,12 @@ static int atp_probe(struct usb_interface *iface,  	dev = kzalloc(sizeof(struct atp), GFP_KERNEL);  	input_dev = input_allocate_device();  	if (!dev || !input_dev) { -		err("Out of memory"); +		dev_err(&iface->dev, "Out of memory\n");  		goto err_free_devs;  	}  	dev->udev = udev; +	dev->intf = iface;  	dev->input = input_dev;  	dev->info = info;  	dev->overflow_warned = false; @@ -837,10 +916,10 @@ static int atp_probe(struct usb_interface *iface,  	input_set_abs_params(input_dev, ABS_X, 0,  			     (dev->info->xsensors - 1) * dev->info->xfact - 1, -			     ATP_FUZZ, 0); +			     dev->info->fuzz, 0);  	input_set_abs_params(input_dev, ABS_Y, 0,  			     (dev->info->ysensors - 1) * dev->info->yfact - 1, -			     ATP_FUZZ, 0); +			     dev->info->fuzz, 0);  	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);  	set_bit(EV_KEY, input_dev->evbit); @@ -886,7 +965,7 @@ static void atp_disconnect(struct usb_interface *iface)  		usb_free_urb(dev->urb);  		kfree(dev);  	} -	printk(KERN_INFO "input: appletouch disconnected\n"); +	dev_info(&iface->dev, "input: appletouch disconnected\n");  }  static int atp_recover(struct atp *dev) @@ -938,15 +1017,4 @@ static struct usb_driver atp_driver = {  	.id_table	= atp_table,  }; -static int __init atp_init(void) -{ -	return usb_register(&atp_driver); -} - -static void __exit atp_exit(void) -{ -	usb_deregister(&atp_driver); -} - -module_init(atp_init); -module_exit(atp_exit); +module_usb_driver(atp_driver); diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c index adf45b3040e..d1c43236b12 100644 --- a/drivers/input/mouse/atarimouse.c +++ b/drivers/input/mouse/atarimouse.c @@ -47,7 +47,6 @@  #include <asm/irq.h>  #include <asm/setup.h> -#include <asm/system.h>  #include <asm/uaccess.h>  #include <asm/atarihw.h>  #include <asm/atarikb.h> @@ -77,15 +76,15 @@ static void atamouse_interrupt(char *buf)  #endif  	/* only relative events get here */ -	dx =  buf[1]; -	dy = -buf[2]; +	dx = buf[1]; +	dy = buf[2];  	input_report_rel(atamouse_dev, REL_X, dx);  	input_report_rel(atamouse_dev, REL_Y, dy); -	input_report_key(atamouse_dev, BTN_LEFT,   buttons & 0x1); +	input_report_key(atamouse_dev, BTN_LEFT,   buttons & 0x4);  	input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2); -	input_report_key(atamouse_dev, BTN_RIGHT,  buttons & 0x4); +	input_report_key(atamouse_dev, BTN_RIGHT,  buttons & 0x1);  	input_sync(atamouse_dev); @@ -108,7 +107,7 @@ static int atamouse_open(struct input_dev *dev)  static void atamouse_close(struct input_dev *dev)  {  	ikbd_mouse_disable(); -	atari_mouse_interrupt_hook = NULL; +	atari_input_mouse_interrupt_hook = NULL;  }  static int __init atamouse_init(void) @@ -118,8 +117,9 @@ static int __init atamouse_init(void)  	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))  		return -ENODEV; -	if (!atari_keyb_init()) -		return -ENODEV; +	error = atari_keyb_init(); +	if (error) +		return error;  	atamouse_dev = input_allocate_device();  	if (!atamouse_dev) diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index b9523176391..c329cdb0b91 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -34,12 +34,12 @@  #include <linux/kernel.h>  #include <linux/errno.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/usb/input.h>  #include <linux/hid.h>  #include <linux/mutex.h> +#include <linux/input/mt.h>  #define USB_VENDOR_ID_APPLE		0x05ac @@ -55,6 +55,42 @@  #define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI	0x0236  #define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO	0x0237  #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS	0x0238 +/* MacbookAir3,2 (unibody), aka wellspring5 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI	0x023f +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO	0x0240 +#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS	0x0241 +/* MacbookAir3,1 (unibody), aka wellspring4 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI	0x0242 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO	0x0243 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS	0x0244 +/* Macbook8 (unibody, March 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI	0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO	0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS	0x0247 +/* MacbookAir4,1 (unibody, July 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI	0x0249 +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO	0x024a +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS	0x024b +/* MacbookAir4,2 (unibody, July 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI	0x024c +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO	0x024d +#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS	0x024e +/* Macbook8,2 (unibody) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI	0x0252 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO	0x0253 +#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS	0x0254 +/* MacbookPro10,1 (unibody, June 2012) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI	0x0262 +#define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO	0x0263 +#define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS	0x0264 +/* MacbookPro10,2 (unibody, October 2012) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI	0x0259 +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO	0x025a +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS	0x025b +/* MacbookAir6,2 (unibody, June 2013) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI	0x0290 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO	0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS	0x0292  #define BCM5974_DEVICE(prod) {					\  	.match_flags = (USB_DEVICE_ID_MATCH_DEVICE |		\ @@ -80,6 +116,42 @@ static const struct usb_device_id bcm5974_table[] = {  	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),  	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),  	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), +	/* MacbookAir3,2 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), +	/* MacbookAir3,1 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), +	/* MacbookPro8 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), +	/* MacbookAir4,1 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS), +	/* MacbookAir4,2 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS), +	/* MacbookPro8,2 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS), +	/* MacbookPro10,1 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_JIS), +	/* MacbookPro10,2 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), +	/* MacbookAir6,2 */ +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), +	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),  	/* Terminating entry */  	{}  }; @@ -107,15 +179,18 @@ struct bt_data {  /* trackpad header types */  enum tp_type {  	TYPE1,			/* plain trackpad */ -	TYPE2			/* button integrated in trackpad */ +	TYPE2,			/* button integrated in trackpad */ +	TYPE3			/* additional header fields since June 2013 */  };  /* trackpad finger data offsets, le16-aligned */  #define FINGER_TYPE1		(13 * sizeof(__le16))  #define FINGER_TYPE2		(15 * sizeof(__le16)) +#define FINGER_TYPE3		(19 * sizeof(__le16))  /* trackpad button data offsets */  #define BUTTON_TYPE2		15 +#define BUTTON_TYPE3		23  /* list of device capability bits */  #define HAS_INTEGRATED_BUTTON	1 @@ -127,26 +202,26 @@ struct tp_finger {  	__le16 abs_y;		/* absolute y coodinate */  	__le16 rel_x;		/* relative x coodinate */  	__le16 rel_y;		/* relative y coodinate */ -	__le16 size_major;	/* finger size, major axis? */ -	__le16 size_minor;	/* finger size, minor axis? */ +	__le16 tool_major;	/* tool area, major axis */ +	__le16 tool_minor;	/* tool area, minor axis */  	__le16 orientation;	/* 16384 when point, else 15 bit angle */ -	__le16 force_major;	/* trackpad force, major axis? */ -	__le16 force_minor;	/* trackpad force, minor axis? */ +	__le16 touch_major;	/* touch area, major axis */ +	__le16 touch_minor;	/* touch area, minor axis */  	__le16 unused[3];	/* zeros */  	__le16 multi;		/* one finger: varies, more fingers: constant */  } __attribute__((packed,aligned(2)));  /* trackpad finger data size, empirically at least ten fingers */ +#define MAX_FINGERS		16  #define SIZEOF_FINGER		sizeof(struct tp_finger) -#define SIZEOF_ALL_FINGERS	(16 * SIZEOF_FINGER) +#define SIZEOF_ALL_FINGERS	(MAX_FINGERS * SIZEOF_FINGER)  #define MAX_FINGER_ORIENTATION	16384  /* device-specific parameters */  struct bcm5974_param { -	int dim;		/* logical dimension */ -	int fuzz;		/* logical noise value */ -	int devmin;		/* device minimum reading */ -	int devmax;		/* device maximum reading */ +	int snratio;		/* signal-to-noise ratio */ +	int min;		/* device minimum reading */ +	int max;		/* device maximum reading */  };  /* device-specific configuration */ @@ -163,6 +238,7 @@ struct bcm5974_config {  	struct bcm5974_param w;	/* finger width limits */  	struct bcm5974_param x;	/* horizontal limits */  	struct bcm5974_param y;	/* vertical limits */ +	struct bcm5974_param o;	/* orientation limits */  };  /* logical device structure */ @@ -178,23 +254,16 @@ struct bcm5974 {  	struct bt_data *bt_data;	/* button transferred data */  	struct urb *tp_urb;		/* trackpad usb request block */  	u8 *tp_data;			/* trackpad transferred data */ -	int fingers;			/* number of fingers on trackpad */ +	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */ +	struct input_mt_pos pos[MAX_FINGERS];		/* position array */ +	int slots[MAX_FINGERS];				/* slot assignments */  }; -/* logical dimensions */ -#define DIM_PRESSURE	256		/* maximum finger pressure */ -#define DIM_WIDTH	16		/* maximum finger width */ -#define DIM_X		1280		/* maximum trackpad x value */ -#define DIM_Y		800		/* maximum trackpad y value */ -  /* logical signal quality */  #define SN_PRESSURE	45		/* pressure signal-to-noise ratio */ -#define SN_WIDTH	100		/* width signal-to-noise ratio */ +#define SN_WIDTH	25		/* width signal-to-noise ratio */  #define SN_COORD	250		/* coordinate signal-to-noise ratio */ - -/* pressure thresholds */ -#define PRESSURE_LOW	(2 * DIM_PRESSURE / SN_PRESSURE) -#define PRESSURE_HIGH	(3 * PRESSURE_LOW) +#define SN_ORIENT	10		/* orientation signal-to-noise ratio */  /* device constants */  static const struct bcm5974_config bcm5974_config_table[] = { @@ -205,10 +274,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {  		0,  		0x84, sizeof(struct bt_data),  		0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, -		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 }, -		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, -		{ DIM_X, DIM_X / SN_COORD, -4824, 5342 }, -		{ DIM_Y, DIM_Y / SN_COORD, -172, 5820 } +		{ SN_PRESSURE, 0, 256 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4824, 5342 }, +		{ SN_COORD, -172, 5820 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }  	},  	{  		USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, @@ -217,10 +287,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {  		0,  		0x84, sizeof(struct bt_data),  		0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, -		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 }, -		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, -		{ DIM_X, DIM_X / SN_COORD, -4824, 4824 }, -		{ DIM_Y, DIM_Y / SN_COORD, -172, 4290 } +		{ SN_PRESSURE, 0, 256 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4824, 4824 }, +		{ SN_COORD, -172, 4290 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }  	},  	{  		USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI, @@ -229,10 +300,128 @@ static const struct bcm5974_config bcm5974_config_table[] = {  		HAS_INTEGRATED_BUTTON,  		0x84, sizeof(struct bt_data),  		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, -		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, -		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, -		{ DIM_X, DIM_X / SN_COORD, -4460, 5166 }, -		{ DIM_Y, DIM_Y / SN_COORD, -75, 6700 } +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4460, 5166 }, +		{ SN_COORD, -75, 6700 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING4_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING4_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4620, 5140 }, +		{ SN_COORD, -150, 6600 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4616, 5112 }, +		{ SN_COORD, -142, 5234 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING5_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4415, 5050 }, +		{ SN_COORD, -55, 6680 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING6_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING6_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4620, 5140 }, +		{ SN_COORD, -150, 6600 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4750, 5280 }, +		{ SN_COORD, -150, 6730 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4620, 5140 }, +		{ SN_COORD, -150, 6600 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING7_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING7_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4750, 5280 }, +		{ SN_COORD, -150, 6730 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS, +		HAS_INTEGRATED_BUTTON, +		0x84, sizeof(struct bt_data), +		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4750, 5280 }, +		{ SN_COORD, -150, 6730 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } +	}, +	{ +		USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI, +		USB_DEVICE_ID_APPLE_WELLSPRING8_ISO, +		USB_DEVICE_ID_APPLE_WELLSPRING8_JIS, +		HAS_INTEGRATED_BUTTON, +		0, sizeof(struct bt_data), +		0x83, TYPE3, FINGER_TYPE3, FINGER_TYPE3 + SIZEOF_ALL_FINGERS, +		{ SN_PRESSURE, 0, 300 }, +		{ SN_WIDTH, 0, 2048 }, +		{ SN_COORD, -4620, 5140 }, +		{ SN_COORD, -150, 6600 }, +		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }  	},  	{}  }; @@ -256,18 +445,11 @@ static inline int raw2int(__le16 x)  	return (signed short)le16_to_cpu(x);  } -/* scale device data to logical dimensions (asserts devmin < devmax) */ -static inline int int2scale(const struct bcm5974_param *p, int x) +static void set_abs(struct input_dev *input, unsigned int code, +		    const struct bcm5974_param *p)  { -	return x * p->dim / (p->devmax - p->devmin); -} - -/* all logical value ranges are [0,dim). */ -static inline int int2bound(const struct bcm5974_param *p, int x) -{ -	int s = int2scale(p, x); - -	return clamp_val(s, 0, p->dim - 1); +	int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0; +	input_set_abs_params(input, code, p->min, p->max, fuzz, 0);  }  /* setup which logical events to report */ @@ -276,44 +458,30 @@ static void setup_events_to_report(struct input_dev *input_dev,  {  	__set_bit(EV_ABS, input_dev->evbit); -	input_set_abs_params(input_dev, ABS_PRESSURE, -				0, cfg->p.dim, cfg->p.fuzz, 0); -	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, -				0, cfg->w.dim, cfg->w.fuzz, 0); -	input_set_abs_params(input_dev, ABS_X, -				0, cfg->x.dim, cfg->x.fuzz, 0); -	input_set_abs_params(input_dev, ABS_Y, -				0, cfg->y.dim, cfg->y.fuzz, 0); +	/* for synaptics only */ +	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0); +	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);  	/* finger touch area */ -	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, -			     cfg->w.devmin, cfg->w.devmax, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, -			     cfg->w.devmin, cfg->w.devmax, 0, 0); +	set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w); +	set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);  	/* finger approach area */ -	input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, -			     cfg->w.devmin, cfg->w.devmax, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, -			     cfg->w.devmin, cfg->w.devmax, 0, 0); +	set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w); +	set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);  	/* finger orientation */ -	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, -			     -MAX_FINGER_ORIENTATION, -			     MAX_FINGER_ORIENTATION, 0, 0); +	set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);  	/* finger position */ -	input_set_abs_params(input_dev, ABS_MT_POSITION_X, -			     cfg->x.devmin, cfg->x.devmax, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, -			     cfg->y.devmin, cfg->y.devmax, 0, 0); +	set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x); +	set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);  	__set_bit(EV_KEY, input_dev->evbit); -	__set_bit(BTN_TOUCH, input_dev->keybit); -	__set_bit(BTN_TOOL_FINGER, input_dev->keybit); -	__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); -	__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); -	__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);  	__set_bit(BTN_LEFT, input_dev->keybit); -	input_set_events_per_packet(input_dev, 60); +	if (cfg->caps & HAS_INTEGRATED_BUTTON) +		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + +	input_mt_init_slots(input_dev, MAX_FINGERS, +		INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);  }  /* report button data as logical button state */ @@ -333,24 +501,44 @@ static int report_bt_state(struct bcm5974 *dev, int size)  	return 0;  } -static void report_finger_data(struct input_dev *input, -			       const struct bcm5974_config *cfg, +static void report_finger_data(struct input_dev *input, int slot, +			       const struct input_mt_pos *pos,  			       const struct tp_finger *f)  { +	input_mt_slot(input, slot); +	input_mt_report_slot_state(input, MT_TOOL_FINGER, true); +  	input_report_abs(input, ABS_MT_TOUCH_MAJOR, -			 raw2int(f->force_major) << 1); +			 raw2int(f->touch_major) << 1);  	input_report_abs(input, ABS_MT_TOUCH_MINOR, -			 raw2int(f->force_minor) << 1); +			 raw2int(f->touch_minor) << 1);  	input_report_abs(input, ABS_MT_WIDTH_MAJOR, -			 raw2int(f->size_major) << 1); +			 raw2int(f->tool_major) << 1);  	input_report_abs(input, ABS_MT_WIDTH_MINOR, -			 raw2int(f->size_minor) << 1); +			 raw2int(f->tool_minor) << 1);  	input_report_abs(input, ABS_MT_ORIENTATION,  			 MAX_FINGER_ORIENTATION - raw2int(f->orientation)); -	input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x)); -	input_report_abs(input, ABS_MT_POSITION_Y, -			 cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y)); -	input_mt_sync(input); +	input_report_abs(input, ABS_MT_POSITION_X, pos->x); +	input_report_abs(input, ABS_MT_POSITION_Y, pos->y); +} + +static void report_synaptics_data(struct input_dev *input, +				  const struct bcm5974_config *cfg, +				  const struct tp_finger *f, int raw_n) +{ +	int abs_p = 0, abs_w = 0; + +	if (raw_n) { +		int p = raw2int(f->touch_major); +		int w = raw2int(f->tool_major); +		if (p > 0 && raw2int(f->origin)) { +			abs_p = clamp_val(256 * p / cfg->p.max, 0, 255); +			abs_w = clamp_val(16 * w / cfg->w.max, 0, 15); +		} +	} + +	input_report_abs(input, ABS_PRESSURE, abs_p); +	input_report_abs(input, ABS_TOOL_WIDTH, abs_w);  }  /* report trackpad data as logical trackpad state */ @@ -359,9 +547,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)  	const struct bcm5974_config *c = &dev->cfg;  	const struct tp_finger *f;  	struct input_dev *input = dev->input; -	int raw_p, raw_w, raw_x, raw_y, raw_n, i; -	int ptest, origin, ibt = 0, nmin = 0, nmax = 0; -	int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0; +	int raw_n, i, n = 0;  	if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)  		return -EIO; @@ -370,76 +556,32 @@ static int report_tp_state(struct bcm5974 *dev, int size)  	f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);  	raw_n = (size - c->tp_offset) / SIZEOF_FINGER; -	/* always track the first finger; when detached, start over */ -	if (raw_n) { - -		/* report raw trackpad data */ -		for (i = 0; i < raw_n; i++) -			report_finger_data(input, c, &f[i]); - -		raw_p = raw2int(f->force_major); -		raw_w = raw2int(f->size_major); -		raw_x = raw2int(f->abs_x); -		raw_y = raw2int(f->abs_y); - -		dprintk(9, -			"bcm5974: " -			"raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n", -			raw_p, raw_w, raw_x, raw_y, raw_n); - -		ptest = int2bound(&c->p, raw_p); -		origin = raw2int(f->origin); - -		/* set the integrated button if applicable */ -		if (c->tp_type == TYPE2) -			ibt = raw2int(dev->tp_data[BUTTON_TYPE2]); - -		/* while tracking finger still valid, count all fingers */ -		if (ptest > PRESSURE_LOW && origin) { -			abs_p = ptest; -			abs_w = int2bound(&c->w, raw_w); -			abs_x = int2bound(&c->x, raw_x - c->x.devmin); -			abs_y = int2bound(&c->y, c->y.devmax - raw_y); -			while (raw_n--) { -				ptest = int2bound(&c->p, -						  raw2int(f->force_major)); -				if (ptest > PRESSURE_LOW) -					nmax++; -				if (ptest > PRESSURE_HIGH) -					nmin++; -				f++; -			} -		} +	for (i = 0; i < raw_n; i++) { +		if (raw2int(f[i].touch_major) == 0) +			continue; +		dev->pos[n].x = raw2int(f[i].abs_x); +		dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y); +		dev->index[n++] = &f[i];  	} -	if (dev->fingers < nmin) -		dev->fingers = nmin; -	if (dev->fingers > nmax) -		dev->fingers = nmax; - -	input_report_key(input, BTN_TOUCH, dev->fingers > 0); -	input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1); -	input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2); -	input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3); -	input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3); - -	input_report_abs(input, ABS_PRESSURE, abs_p); -	input_report_abs(input, ABS_TOOL_WIDTH, abs_w); +	input_mt_assign_slots(input, dev->slots, dev->pos, n); -	if (abs_p) { -		input_report_abs(input, ABS_X, abs_x); -		input_report_abs(input, ABS_Y, abs_y); +	for (i = 0; i < n; i++) +		report_finger_data(input, dev->slots[i], +				   &dev->pos[i], dev->index[i]); -		dprintk(8, -			"bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d " -			"nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w, -			abs_x, abs_y, nmin, nmax, dev->fingers, ibt); +	input_mt_sync_frame(input); -	} +	report_synaptics_data(input, c, f, raw_n);  	/* type 2 reports button events via ibt only */ -	if (c->tp_type == TYPE2) +	if (c->tp_type == TYPE2) { +		int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);  		input_report_key(input, BTN_LEFT, ibt); +	} + +	if (c->tp_type == TYPE3) +		input_report_key(input, BTN_LEFT, dev->tp_data[BUTTON_TYPE3]);  	input_sync(input); @@ -456,11 +598,16 @@ static int report_tp_state(struct bcm5974 *dev, int size)  static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)  { -	char *data = kmalloc(8, GFP_KERNEL);  	int retval = 0, size; +	char *data; + +	/* Type 3 does not require a mode switch */ +	if (dev->cfg.tp_type == TYPE3) +		return 0; +	data = kmalloc(8, GFP_KERNEL);  	if (!data) { -		err("bcm5974: out of memory"); +		dev_err(&dev->intf->dev, "out of memory\n");  		retval = -ENOMEM;  		goto out;  	} @@ -473,7 +620,7 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)  			BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);  	if (size != 8) { -		err("bcm5974: could not read from device"); +		dev_err(&dev->intf->dev, "could not read from device\n");  		retval = -EIO;  		goto out;  	} @@ -491,7 +638,7 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)  			BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);  	if (size != 8) { -		err("bcm5974: could not write to device"); +		dev_err(&dev->intf->dev, "could not write to device\n");  		retval = -EIO;  		goto out;  	} @@ -507,6 +654,7 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)  static void bcm5974_irq_button(struct urb *urb)  {  	struct bcm5974 *dev = urb->context; +	struct usb_interface *intf = dev->intf;  	int error;  	switch (urb->status) { @@ -516,10 +664,11 @@ static void bcm5974_irq_button(struct urb *urb)  	case -ECONNRESET:  	case -ENOENT:  	case -ESHUTDOWN: -		dbg("bcm5974: button urb shutting down: %d", urb->status); +		dev_dbg(&intf->dev, "button urb shutting down: %d\n", +			urb->status);  		return;  	default: -		dbg("bcm5974: button urb status: %d", urb->status); +		dev_dbg(&intf->dev, "button urb status: %d\n", urb->status);  		goto exit;  	} @@ -530,12 +679,13 @@ static void bcm5974_irq_button(struct urb *urb)  exit:  	error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);  	if (error) -		err("bcm5974: button urb failed: %d", error); +		dev_err(&intf->dev, "button urb failed: %d\n", error);  }  static void bcm5974_irq_trackpad(struct urb *urb)  {  	struct bcm5974 *dev = urb->context; +	struct usb_interface *intf = dev->intf;  	int error;  	switch (urb->status) { @@ -545,10 +695,11 @@ static void bcm5974_irq_trackpad(struct urb *urb)  	case -ECONNRESET:  	case -ENOENT:  	case -ESHUTDOWN: -		dbg("bcm5974: trackpad urb shutting down: %d", urb->status); +		dev_dbg(&intf->dev, "trackpad urb shutting down: %d\n", +			urb->status);  		return;  	default: -		dbg("bcm5974: trackpad urb status: %d", urb->status); +		dev_dbg(&intf->dev, "trackpad urb status: %d\n", urb->status);  		goto exit;  	} @@ -563,7 +714,7 @@ static void bcm5974_irq_trackpad(struct urb *urb)  exit:  	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);  	if (error) -		err("bcm5974: trackpad urb failed: %d", error); +		dev_err(&intf->dev, "trackpad urb failed: %d\n", error);  }  /* @@ -579,7 +730,7 @@ exit:   * device, resulting in trackpad malfunction under certain   * circumstances. To get around this problem, there is at least one   * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to - * recieve a reset_resume request rather than the normal resume. + * receive a reset_resume request rather than the normal resume.   * Since the implementation of reset_resume is equal to mode switch   * plus start_traffic, it seems easier to always do the switch when   * starting traffic on the device. @@ -594,9 +745,11 @@ static int bcm5974_start_traffic(struct bcm5974 *dev)  		goto err_out;  	} -	error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); -	if (error) -		goto err_reset_mode; +	if (dev->bt_urb) { +		error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); +		if (error) +			goto err_reset_mode; +	}  	error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);  	if (error) @@ -709,7 +862,7 @@ static int bcm5974_probe(struct usb_interface *iface,  	dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);  	input_dev = input_allocate_device();  	if (!dev || !input_dev) { -		err("bcm5974: out of memory"); +		dev_err(&iface->dev, "out of memory\n");  		goto err_free_devs;  	} @@ -720,19 +873,23 @@ static int bcm5974_probe(struct usb_interface *iface,  	mutex_init(&dev->pm_mutex);  	/* setup urbs */ -	dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); -	if (!dev->bt_urb) -		goto err_free_devs; +	if (cfg->tp_type == TYPE1) { +		dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); +		if (!dev->bt_urb) +			goto err_free_devs; +	}  	dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);  	if (!dev->tp_urb)  		goto err_free_bt_urb; -	dev->bt_data = usb_alloc_coherent(dev->udev, +	if (dev->bt_urb) { +		dev->bt_data = usb_alloc_coherent(dev->udev,  					  dev->cfg.bt_datalen, GFP_KERNEL,  					  &dev->bt_urb->transfer_dma); -	if (!dev->bt_data) -		goto err_free_urb; +		if (!dev->bt_data) +			goto err_free_urb; +	}  	dev->tp_data = usb_alloc_coherent(dev->udev,  					  dev->cfg.tp_datalen, GFP_KERNEL, @@ -740,10 +897,11 @@ static int bcm5974_probe(struct usb_interface *iface,  	if (!dev->tp_data)  		goto err_free_bt_buffer; -	usb_fill_int_urb(dev->bt_urb, udev, -			 usb_rcvintpipe(udev, cfg->bt_ep), -			 dev->bt_data, dev->cfg.bt_datalen, -			 bcm5974_irq_button, dev, 1); +	if (dev->bt_urb) +		usb_fill_int_urb(dev->bt_urb, udev, +				 usb_rcvintpipe(udev, cfg->bt_ep), +				 dev->bt_data, dev->cfg.bt_datalen, +				 bcm5974_irq_button, dev, 1);  	usb_fill_int_urb(dev->tp_urb, udev,  			 usb_rcvintpipe(udev, cfg->tp_ep), @@ -781,8 +939,9 @@ err_free_buffer:  	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,  		dev->tp_data, dev->tp_urb->transfer_dma);  err_free_bt_buffer: -	usb_free_coherent(dev->udev, dev->cfg.bt_datalen, -		dev->bt_data, dev->bt_urb->transfer_dma); +	if (dev->bt_urb) +		usb_free_coherent(dev->udev, dev->cfg.bt_datalen, +				  dev->bt_data, dev->bt_urb->transfer_dma);  err_free_urb:  	usb_free_urb(dev->tp_urb);  err_free_bt_urb: @@ -803,8 +962,9 @@ static void bcm5974_disconnect(struct usb_interface *iface)  	input_unregister_device(dev->input);  	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,  			  dev->tp_data, dev->tp_urb->transfer_dma); -	usb_free_coherent(dev->udev, dev->cfg.bt_datalen, -			  dev->bt_data, dev->bt_urb->transfer_dma); +	if (dev->bt_urb) +		usb_free_coherent(dev->udev, dev->cfg.bt_datalen, +				  dev->bt_data, dev->bt_urb->transfer_dma);  	usb_free_urb(dev->tp_urb);  	usb_free_urb(dev->bt_urb);  	kfree(dev); @@ -820,16 +980,4 @@ static struct usb_driver bcm5974_driver = {  	.supports_autosuspend	= 1,  }; -static int __init bcm5974_init(void) -{ -	return usb_register(&bcm5974_driver); -} - -static void __exit bcm5974_exit(void) -{ -	usb_deregister(&bcm5974_driver); -} - -module_init(bcm5974_init); -module_exit(bcm5974_exit); - +module_usb_driver(bcm5974_driver); diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c new file mode 100644 index 00000000000..b409c3d7d4f --- /dev/null +++ b/drivers/input/mouse/cyapa.c @@ -0,0 +1,973 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du <dudl@cypress.com> + * Further cleanup and restructuring by: + *   Daniel Kurtz <djkurtz@chromium.org> + *   Benson Leung <bleung@chromium.org> + * + * Copyright (C) 2011-2012 Cypress Semiconductor, Inc. + * Copyright (C) 2011-2012 Google, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +/* APA trackpad firmware generation */ +#define CYAPA_GEN3   0x03   /* support MT-protocol B with tracking ID. */ + +#define CYAPA_NAME   "Cypress APA Trackpad (cyapa)" + +/* commands for read/write registers of Cypress trackpad */ +#define CYAPA_CMD_SOFT_RESET       0x00 +#define CYAPA_CMD_POWER_MODE       0x01 +#define CYAPA_CMD_DEV_STATUS       0x02 +#define CYAPA_CMD_GROUP_DATA       0x03 +#define CYAPA_CMD_GROUP_CMD        0x04 +#define CYAPA_CMD_GROUP_QUERY      0x05 +#define CYAPA_CMD_BL_STATUS        0x06 +#define CYAPA_CMD_BL_HEAD          0x07 +#define CYAPA_CMD_BL_CMD           0x08 +#define CYAPA_CMD_BL_DATA          0x09 +#define CYAPA_CMD_BL_ALL           0x0a +#define CYAPA_CMD_BLK_PRODUCT_ID   0x0b +#define CYAPA_CMD_BLK_HEAD         0x0c + +/* report data start reg offset address. */ +#define DATA_REG_START_OFFSET  0x0000 + +#define BL_HEAD_OFFSET 0x00 +#define BL_DATA_OFFSET 0x10 + +/* + * Operational Device Status Register + * + * bit 7: Valid interrupt source + * bit 6 - 4: Reserved + * bit 3 - 2: Power status + * bit 1 - 0: Device status + */ +#define REG_OP_STATUS     0x00 +#define OP_STATUS_SRC     0x80 +#define OP_STATUS_POWER   0x0c +#define OP_STATUS_DEV     0x03 +#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV) + +/* + * Operational Finger Count/Button Flags Register + * + * bit 7 - 4: Number of touched finger + * bit 3: Valid data + * bit 2: Middle Physical Button + * bit 1: Right Physical Button + * bit 0: Left physical Button + */ +#define REG_OP_DATA1       0x01 +#define OP_DATA_VALID      0x08 +#define OP_DATA_MIDDLE_BTN 0x04 +#define OP_DATA_RIGHT_BTN  0x02 +#define OP_DATA_LEFT_BTN   0x01 +#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \ +			  OP_DATA_LEFT_BTN) + +/* + * Bootloader Status Register + * + * bit 7: Busy + * bit 6 - 5: Reserved + * bit 4: Bootloader running + * bit 3 - 1: Reserved + * bit 0: Checksum valid + */ +#define REG_BL_STATUS        0x01 +#define BL_STATUS_BUSY       0x80 +#define BL_STATUS_RUNNING    0x10 +#define BL_STATUS_DATA_VALID 0x08 +#define BL_STATUS_CSUM_VALID 0x01 + +/* + * Bootloader Error Register + * + * bit 7: Invalid + * bit 6: Invalid security key + * bit 5: Bootloading + * bit 4: Command checksum + * bit 3: Flash protection error + * bit 2: Flash checksum error + * bit 1 - 0: Reserved + */ +#define REG_BL_ERROR         0x02 +#define BL_ERROR_INVALID     0x80 +#define BL_ERROR_INVALID_KEY 0x40 +#define BL_ERROR_BOOTLOADING 0x20 +#define BL_ERROR_CMD_CSUM    0x10 +#define BL_ERROR_FLASH_PROT  0x08 +#define BL_ERROR_FLASH_CSUM  0x04 + +#define BL_STATUS_SIZE  3  /* length of bootloader status registers */ +#define BLK_HEAD_BYTES 32 + +#define PRODUCT_ID_SIZE  16 +#define QUERY_DATA_SIZE  27 +#define REG_PROTOCOL_GEN_QUERY_OFFSET  20 + +#define REG_OFFSET_DATA_BASE     0x0000 +#define REG_OFFSET_COMMAND_BASE  0x0028 +#define REG_OFFSET_QUERY_BASE    0x002a + +#define CAPABILITY_LEFT_BTN_MASK	(0x01 << 3) +#define CAPABILITY_RIGHT_BTN_MASK	(0x01 << 4) +#define CAPABILITY_MIDDLE_BTN_MASK	(0x01 << 5) +#define CAPABILITY_BTN_MASK  (CAPABILITY_LEFT_BTN_MASK | \ +			      CAPABILITY_RIGHT_BTN_MASK | \ +			      CAPABILITY_MIDDLE_BTN_MASK) + +#define CYAPA_OFFSET_SOFT_RESET  REG_OFFSET_COMMAND_BASE + +#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1) + +#define PWR_MODE_MASK   0xfc +#define PWR_MODE_FULL_ACTIVE (0x3f << 2) +#define PWR_MODE_IDLE        (0x05 << 2) /* default sleep time is 50 ms. */ +#define PWR_MODE_OFF         (0x00 << 2) + +#define PWR_STATUS_MASK      0x0c +#define PWR_STATUS_ACTIVE    (0x03 << 2) +#define PWR_STATUS_IDLE      (0x02 << 2) +#define PWR_STATUS_OFF       (0x00 << 2) + +/* + * CYAPA trackpad device states. + * Used in register 0x00, bit1-0, DeviceStatus field. + * Other values indicate device is in an abnormal state and must be reset. + */ +#define CYAPA_DEV_NORMAL  0x03 +#define CYAPA_DEV_BUSY    0x01 + +enum cyapa_state { +	CYAPA_STATE_OP, +	CYAPA_STATE_BL_IDLE, +	CYAPA_STATE_BL_ACTIVE, +	CYAPA_STATE_BL_BUSY, +	CYAPA_STATE_NO_DEVICE, +}; + + +struct cyapa_touch { +	/* +	 * high bits or x/y position value +	 * bit 7 - 4: high 4 bits of x position value +	 * bit 3 - 0: high 4 bits of y position value +	 */ +	u8 xy_hi; +	u8 x_lo;  /* low 8 bits of x position value. */ +	u8 y_lo;  /* low 8 bits of y position value. */ +	u8 pressure; +	/* id range is 1 - 15.  It is incremented with every new touch. */ +	u8 id; +} __packed; + +/* The touch.id is used as the MT slot id, thus max MT slot is 15 */ +#define CYAPA_MAX_MT_SLOTS  15 + +struct cyapa_reg_data { +	/* +	 * bit 0 - 1: device status +	 * bit 3 - 2: power mode +	 * bit 6 - 4: reserved +	 * bit 7: interrupt valid bit +	 */ +	u8 device_status; +	/* +	 * bit 7 - 4: number of fingers currently touching pad +	 * bit 3: valid data check bit +	 * bit 2: middle mechanism button state if exists +	 * bit 1: right mechanism button state if exists +	 * bit 0: left mechanism button state if exists +	 */ +	u8 finger_btn; +	/* CYAPA reports up to 5 touches per packet. */ +	struct cyapa_touch touches[5]; +} __packed; + +/* The main device structure */ +struct cyapa { +	enum cyapa_state state; + +	struct i2c_client *client; +	struct input_dev *input; +	char phys[32];	/* device physical location */ +	int irq; +	bool irq_wake;  /* irq wake is enabled */ +	bool smbus; + +	/* read from query data region. */ +	char product_id[16]; +	u8 btn_capability; +	u8 gen; +	int max_abs_x; +	int max_abs_y; +	int physical_size_x; +	int physical_size_y; +}; + +static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, +		0x04, 0x05, 0x06, 0x07 }; +static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04, +		0x05, 0x06, 0x07 }; + +struct cyapa_cmd_len { +	u8 cmd; +	u8 len; +}; + +#define CYAPA_ADAPTER_FUNC_NONE   0 +#define CYAPA_ADAPTER_FUNC_I2C    1 +#define CYAPA_ADAPTER_FUNC_SMBUS  2 +#define CYAPA_ADAPTER_FUNC_BOTH   3 + +/* + * macros for SMBus communication + */ +#define SMBUS_READ   0x01 +#define SMBUS_WRITE 0x00 +#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) +#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) +#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80 +#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40 + + /* for byte read/write command */ +#define CMD_RESET 0 +#define CMD_POWER_MODE 1 +#define CMD_DEV_STATUS 2 +#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) +#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) +#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) +#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) + + /* for group registers read/write command */ +#define REG_GROUP_DATA 0 +#define REG_GROUP_CMD 2 +#define REG_GROUP_QUERY 3 +#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) +#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) +#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) +#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) + + /* for register block read/write command */ +#define CMD_BL_STATUS 0 +#define CMD_BL_HEAD 1 +#define CMD_BL_CMD 2 +#define CMD_BL_DATA 3 +#define CMD_BL_ALL 4 +#define CMD_BLK_PRODUCT_ID 5 +#define CMD_BLK_HEAD 6 +#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) + +/* register block read/write command in bootloader mode */ +#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) +#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) +#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) +#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) +#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) + +/* register block read/write command in operational mode */ +#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) +#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) + +static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { +	{ CYAPA_OFFSET_SOFT_RESET, 1 }, +	{ REG_OFFSET_COMMAND_BASE + 1, 1 }, +	{ REG_OFFSET_DATA_BASE, 1 }, +	{ REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) }, +	{ REG_OFFSET_COMMAND_BASE, 0 }, +	{ REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, +	{ BL_HEAD_OFFSET, 3 }, +	{ BL_HEAD_OFFSET, 16 }, +	{ BL_HEAD_OFFSET, 16 }, +	{ BL_DATA_OFFSET, 16 }, +	{ BL_HEAD_OFFSET, 32 }, +	{ REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE }, +	{ REG_OFFSET_DATA_BASE, 32 } +}; + +static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { +	{ CYAPA_SMBUS_RESET, 1 }, +	{ CYAPA_SMBUS_POWER_MODE, 1 }, +	{ CYAPA_SMBUS_DEV_STATUS, 1 }, +	{ CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) }, +	{ CYAPA_SMBUS_GROUP_CMD, 2 }, +	{ CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE }, +	{ CYAPA_SMBUS_BL_STATUS, 3 }, +	{ CYAPA_SMBUS_BL_HEAD, 16 }, +	{ CYAPA_SMBUS_BL_CMD, 16 }, +	{ CYAPA_SMBUS_BL_DATA, 16 }, +	{ CYAPA_SMBUS_BL_ALL, 32 }, +	{ CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE }, +	{ CYAPA_SMBUS_BLK_HEAD, 16 }, +}; + +static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, +					u8 *values) +{ +	return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values); +} + +static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, +					 size_t len, const u8 *values) +{ +	return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); +} + +/* + * cyapa_smbus_read_block - perform smbus block read command + * @cyapa  - private data structure of the driver + * @cmd    - the properly encoded smbus command + * @len    - expected length of smbus command result + * @values - buffer to store smbus command result + * + * Returns negative errno, else the number of bytes written. + * + * Note: + * In trackpad device, the memory block allocated for I2C register map + * is 256 bytes, so the max read block for I2C bus is 256 bytes. + */ +static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, +				      u8 *values) +{ +	ssize_t ret; +	u8 index; +	u8 smbus_cmd; +	u8 *buf; +	struct i2c_client *client = cyapa->client; + +	if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd)) +		return -EINVAL; + +	if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) { +		/* read specific block registers command. */ +		smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); +		ret = i2c_smbus_read_block_data(client, smbus_cmd, values); +		goto out; +	} + +	ret = 0; +	for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) { +		smbus_cmd = SMBUS_ENCODE_IDX(cmd, index); +		smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ); +		buf = values + I2C_SMBUS_BLOCK_MAX * index; +		ret = i2c_smbus_read_block_data(client, smbus_cmd, buf); +		if (ret < 0) +			goto out; +	} + +out: +	return ret > 0 ? len : ret; +} + +static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) +{ +	u8 cmd; + +	if (cyapa->smbus) { +		cmd = cyapa_smbus_cmds[cmd_idx].cmd; +		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); +	} else { +		cmd = cyapa_i2c_cmds[cmd_idx].cmd; +	} +	return i2c_smbus_read_byte_data(cyapa->client, cmd); +} + +static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) +{ +	u8 cmd; + +	if (cyapa->smbus) { +		cmd = cyapa_smbus_cmds[cmd_idx].cmd; +		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE); +	} else { +		cmd = cyapa_i2c_cmds[cmd_idx].cmd; +	} +	return i2c_smbus_write_byte_data(cyapa->client, cmd, value); +} + +static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) +{ +	u8 cmd; +	size_t len; + +	if (cyapa->smbus) { +		cmd = cyapa_smbus_cmds[cmd_idx].cmd; +		len = cyapa_smbus_cmds[cmd_idx].len; +		return cyapa_smbus_read_block(cyapa, cmd, len, values); +	} else { +		cmd = cyapa_i2c_cmds[cmd_idx].cmd; +		len = cyapa_i2c_cmds[cmd_idx].len; +		return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); +	} +} + +/* + * Query device for its current operating state. + * + */ +static int cyapa_get_state(struct cyapa *cyapa) +{ +	int ret; +	u8 status[BL_STATUS_SIZE]; + +	cyapa->state = CYAPA_STATE_NO_DEVICE; + +	/* +	 * Get trackpad status by reading 3 registers starting from 0. +	 * If the device is in the bootloader, this will be BL_HEAD. +	 * If the device is in operation mode, this will be the DATA regs. +	 * +	 */ +	ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, +				       status); + +	/* +	 * On smbus systems in OP mode, the i2c_reg_read will fail with +	 * -ETIMEDOUT.  In this case, try again using the smbus equivalent +	 * command.  This should return a BL_HEAD indicating CYAPA_STATE_OP. +	 */ +	if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO)) +		ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status); + +	if (ret != BL_STATUS_SIZE) +		goto error; + +	if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) { +		switch (status[REG_OP_STATUS] & OP_STATUS_DEV) { +		case CYAPA_DEV_NORMAL: +		case CYAPA_DEV_BUSY: +			cyapa->state = CYAPA_STATE_OP; +			break; +		default: +			ret = -EAGAIN; +			goto error; +		} +	} else { +		if (status[REG_BL_STATUS] & BL_STATUS_BUSY) +			cyapa->state = CYAPA_STATE_BL_BUSY; +		else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) +			cyapa->state = CYAPA_STATE_BL_ACTIVE; +		else +			cyapa->state = CYAPA_STATE_BL_IDLE; +	} + +	return 0; +error: +	return (ret < 0) ? ret : -EAGAIN; +} + +/* + * Poll device for its status in a loop, waiting up to timeout for a response. + * + * When the device switches state, it usually takes ~300 ms. + * However, when running a new firmware image, the device must calibrate its + * sensors, which can take as long as 2 seconds. + * + * Note: The timeout has granularity of the polling rate, which is 100 ms. + * + * Returns: + *   0 when the device eventually responds with a valid non-busy state. + *   -ETIMEDOUT if device never responds (too many -EAGAIN) + *   < 0    other errors + */ +static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) +{ +	int ret; +	int tries = timeout / 100; + +	ret = cyapa_get_state(cyapa); +	while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) { +		msleep(100); +		ret = cyapa_get_state(cyapa); +	} +	return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret; +} + +static int cyapa_bl_deactivate(struct cyapa *cyapa) +{ +	int ret; + +	ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate), +					bl_deactivate); +	if (ret < 0) +		return ret; + +	/* wait for bootloader to switch to idle state; should take < 100ms */ +	msleep(100); +	ret = cyapa_poll_state(cyapa, 500); +	if (ret < 0) +		return ret; +	if (cyapa->state != CYAPA_STATE_BL_IDLE) +		return -EAGAIN; +	return 0; +} + +/* + * Exit bootloader + * + * Send bl_exit command, then wait 50 - 100 ms to let device transition to + * operational mode.  If this is the first time the device's firmware is + * running, it can take up to 2 seconds to calibrate its sensors.  So, poll + * the device's new state for up to 2 seconds. + * + * Returns: + *   -EIO    failure while reading from device + *   -EAGAIN device is stuck in bootloader, b/c it has invalid firmware + *   0       device is supported and in operational mode + */ +static int cyapa_bl_exit(struct cyapa *cyapa) +{ +	int ret; + +	ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit); +	if (ret < 0) +		return ret; + +	/* +	 * Wait for bootloader to exit, and operation mode to start. +	 * Normally, this takes at least 50 ms. +	 */ +	usleep_range(50000, 100000); +	/* +	 * In addition, when a device boots for the first time after being +	 * updated to new firmware, it must first calibrate its sensors, which +	 * can take up to an additional 2 seconds. +	 */ +	ret = cyapa_poll_state(cyapa, 2000); +	if (ret < 0) +		return ret; +	if (cyapa->state != CYAPA_STATE_OP) +		return -EAGAIN; + +	return 0; +} + +/* + * Set device power mode + * + */ +static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode) +{ +	struct device *dev = &cyapa->client->dev; +	int ret; +	u8 power; + +	if (cyapa->state != CYAPA_STATE_OP) +		return 0; + +	ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE); +	if (ret < 0) +		return ret; + +	power = ret & ~PWR_MODE_MASK; +	power |= power_mode & PWR_MODE_MASK; +	ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power); +	if (ret < 0) +		dev_err(dev, "failed to set power_mode 0x%02x err = %d\n", +			power_mode, ret); +	return ret; +} + +static int cyapa_get_query_data(struct cyapa *cyapa) +{ +	u8 query_data[QUERY_DATA_SIZE]; +	int ret; + +	if (cyapa->state != CYAPA_STATE_OP) +		return -EBUSY; + +	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data); +	if (ret < 0) +		return ret; +	if (ret != QUERY_DATA_SIZE) +		return -EIO; + +	memcpy(&cyapa->product_id[0], &query_data[0], 5); +	cyapa->product_id[5] = '-'; +	memcpy(&cyapa->product_id[6], &query_data[5], 6); +	cyapa->product_id[12] = '-'; +	memcpy(&cyapa->product_id[13], &query_data[11], 2); +	cyapa->product_id[15] = '\0'; + +	cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK; + +	cyapa->gen = query_data[20] & 0x0f; + +	cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22]; +	cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23]; + +	cyapa->physical_size_x = +		((query_data[24] & 0xf0) << 4) | query_data[25]; +	cyapa->physical_size_y = +		((query_data[24] & 0x0f) << 8) | query_data[26]; + +	return 0; +} + +/* + * Check if device is operational. + * + * An operational device is responding, has exited bootloader, and has + * firmware supported by this driver. + * + * Returns: + *   -EBUSY  no device or in bootloader + *   -EIO    failure while reading from device + *   -EAGAIN device is still in bootloader + *           if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware + *   -EINVAL device is in operational mode, but not supported by this driver + *   0       device is supported + */ +static int cyapa_check_is_operational(struct cyapa *cyapa) +{ +	struct device *dev = &cyapa->client->dev; +	static const char unique_str[] = "CYTRA"; +	int ret; + +	ret = cyapa_poll_state(cyapa, 2000); +	if (ret < 0) +		return ret; +	switch (cyapa->state) { +	case CYAPA_STATE_BL_ACTIVE: +		ret = cyapa_bl_deactivate(cyapa); +		if (ret) +			return ret; + +	/* Fallthrough state */ +	case CYAPA_STATE_BL_IDLE: +		ret = cyapa_bl_exit(cyapa); +		if (ret) +			return ret; + +	/* Fallthrough state */ +	case CYAPA_STATE_OP: +		ret = cyapa_get_query_data(cyapa); +		if (ret < 0) +			return ret; + +		/* only support firmware protocol gen3 */ +		if (cyapa->gen != CYAPA_GEN3) { +			dev_err(dev, "unsupported protocol version (%d)", +				cyapa->gen); +			return -EINVAL; +		} + +		/* only support product ID starting with CYTRA */ +		if (memcmp(cyapa->product_id, unique_str, +			   sizeof(unique_str) - 1) != 0) { +			dev_err(dev, "unsupported product ID (%s)\n", +				cyapa->product_id); +			return -EINVAL; +		} +		return 0; + +	default: +		return -EIO; +	} +	return 0; +} + +static irqreturn_t cyapa_irq(int irq, void *dev_id) +{ +	struct cyapa *cyapa = dev_id; +	struct device *dev = &cyapa->client->dev; +	struct input_dev *input = cyapa->input; +	struct cyapa_reg_data data; +	int i; +	int ret; +	int num_fingers; + +	if (device_may_wakeup(dev)) +		pm_wakeup_event(dev, 0); + +	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); +	if (ret != sizeof(data)) +		goto out; + +	if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || +	    (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || +	    (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { +		goto out; +	} + +	num_fingers = (data.finger_btn >> 4) & 0x0f; +	for (i = 0; i < num_fingers; i++) { +		const struct cyapa_touch *touch = &data.touches[i]; +		/* Note: touch->id range is 1 to 15; slots are 0 to 14. */ +		int slot = touch->id - 1; + +		input_mt_slot(input, slot); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, true); +		input_report_abs(input, ABS_MT_POSITION_X, +				 ((touch->xy_hi & 0xf0) << 4) | touch->x_lo); +		input_report_abs(input, ABS_MT_POSITION_Y, +				 ((touch->xy_hi & 0x0f) << 8) | touch->y_lo); +		input_report_abs(input, ABS_MT_PRESSURE, touch->pressure); +	} + +	input_mt_sync_frame(input); + +	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) +		input_report_key(input, BTN_LEFT, +				 data.finger_btn & OP_DATA_LEFT_BTN); + +	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) +		input_report_key(input, BTN_MIDDLE, +				 data.finger_btn & OP_DATA_MIDDLE_BTN); + +	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) +		input_report_key(input, BTN_RIGHT, +				 data.finger_btn & OP_DATA_RIGHT_BTN); + +	input_sync(input); + +out: +	return IRQ_HANDLED; +} + +static u8 cyapa_check_adapter_functionality(struct i2c_client *client) +{ +	u8 ret = CYAPA_ADAPTER_FUNC_NONE; + +	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) +		ret |= CYAPA_ADAPTER_FUNC_I2C; +	if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | +				     I2C_FUNC_SMBUS_BLOCK_DATA | +				     I2C_FUNC_SMBUS_I2C_BLOCK)) +		ret |= CYAPA_ADAPTER_FUNC_SMBUS; +	return ret; +} + +static int cyapa_create_input_dev(struct cyapa *cyapa) +{ +	struct device *dev = &cyapa->client->dev; +	int ret; +	struct input_dev *input; + +	if (!cyapa->physical_size_x || !cyapa->physical_size_y) +		return -EINVAL; + +	input = cyapa->input = input_allocate_device(); +	if (!input) { +		dev_err(dev, "allocate memory for input device failed\n"); +		return -ENOMEM; +	} + +	input->name = CYAPA_NAME; +	input->phys = cyapa->phys; +	input->id.bustype = BUS_I2C; +	input->id.version = 1; +	input->id.product = 0;  /* means any product in eventcomm. */ +	input->dev.parent = &cyapa->client->dev; + +	input_set_drvdata(input, cyapa); + +	__set_bit(EV_ABS, input->evbit); + +	/* finger position */ +	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, +			     0); +	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, +			     0); +	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + +	input_abs_set_res(input, ABS_MT_POSITION_X, +			  cyapa->max_abs_x / cyapa->physical_size_x); +	input_abs_set_res(input, ABS_MT_POSITION_Y, +			  cyapa->max_abs_y / cyapa->physical_size_y); + +	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) +		__set_bit(BTN_LEFT, input->keybit); +	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) +		__set_bit(BTN_MIDDLE, input->keybit); +	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) +		__set_bit(BTN_RIGHT, input->keybit); + +	if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) +		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + +	/* handle pointer emulation and unused slots in core */ +	ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, +				  INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); +	if (ret) { +		dev_err(dev, "allocate memory for MT slots failed, %d\n", ret); +		goto err_free_device; +	} + +	/* Register the device in input subsystem */ +	ret = input_register_device(input); +	if (ret) { +		dev_err(dev, "input device register failed, %d\n", ret); +		goto err_free_device; +	} +	return 0; + +err_free_device: +	input_free_device(input); +	cyapa->input = NULL; +	return ret; +} + +static int cyapa_probe(struct i2c_client *client, +		       const struct i2c_device_id *dev_id) +{ +	int ret; +	u8 adapter_func; +	struct cyapa *cyapa; +	struct device *dev = &client->dev; + +	adapter_func = cyapa_check_adapter_functionality(client); +	if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) { +		dev_err(dev, "not a supported I2C/SMBus adapter\n"); +		return -EIO; +	} + +	cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL); +	if (!cyapa) { +		dev_err(dev, "allocate memory for cyapa failed\n"); +		return -ENOMEM; +	} + +	cyapa->gen = CYAPA_GEN3; +	cyapa->client = client; +	i2c_set_clientdata(client, cyapa); +	sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, +		client->addr); + +	/* i2c isn't supported, use smbus */ +	if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) +		cyapa->smbus = true; +	cyapa->state = CYAPA_STATE_NO_DEVICE; +	ret = cyapa_check_is_operational(cyapa); +	if (ret) { +		dev_err(dev, "device not operational, %d\n", ret); +		goto err_mem_free; +	} + +	ret = cyapa_create_input_dev(cyapa); +	if (ret) { +		dev_err(dev, "create input_dev instance failed, %d\n", ret); +		goto err_mem_free; +	} + +	ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); +	if (ret) { +		dev_err(dev, "set active power failed, %d\n", ret); +		goto err_unregister_device; +	} + +	cyapa->irq = client->irq; +	ret = request_threaded_irq(cyapa->irq, +				   NULL, +				   cyapa_irq, +				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +				   "cyapa", +				   cyapa); +	if (ret) { +		dev_err(dev, "IRQ request failed: %d\n, ", ret); +		goto err_unregister_device; +	} + +	return 0; + +err_unregister_device: +	input_unregister_device(cyapa->input); +err_mem_free: +	kfree(cyapa); + +	return ret; +} + +static int cyapa_remove(struct i2c_client *client) +{ +	struct cyapa *cyapa = i2c_get_clientdata(client); + +	free_irq(cyapa->irq, cyapa); +	input_unregister_device(cyapa->input); +	cyapa_set_power_mode(cyapa, PWR_MODE_OFF); +	kfree(cyapa); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyapa_suspend(struct device *dev) +{ +	int ret; +	u8 power_mode; +	struct cyapa *cyapa = dev_get_drvdata(dev); + +	disable_irq(cyapa->irq); + +	/* +	 * Set trackpad device to idle mode if wakeup is allowed, +	 * otherwise turn off. +	 */ +	power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE +					    : PWR_MODE_OFF; +	ret = cyapa_set_power_mode(cyapa, power_mode); +	if (ret < 0) +		dev_err(dev, "set power mode failed, %d\n", ret); + +	if (device_may_wakeup(dev)) +		cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0); +	return 0; +} + +static int cyapa_resume(struct device *dev) +{ +	int ret; +	struct cyapa *cyapa = dev_get_drvdata(dev); + +	if (device_may_wakeup(dev) && cyapa->irq_wake) +		disable_irq_wake(cyapa->irq); + +	ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); +	if (ret) +		dev_warn(dev, "resume active power failed, %d\n", ret); + +	enable_irq(cyapa->irq); +	return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume); + +static const struct i2c_device_id cyapa_id_table[] = { +	{ "cyapa", 0 }, +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, cyapa_id_table); + +static struct i2c_driver cyapa_driver = { +	.driver = { +		.name = "cyapa", +		.owner = THIS_MODULE, +		.pm = &cyapa_pm_ops, +	}, + +	.probe = cyapa_probe, +	.remove = cyapa_remove, +	.id_table = cyapa_id_table, +}; + +module_i2c_driver(cyapa_driver); + +MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver"); +MODULE_AUTHOR("Dudley Du <dudl@cypress.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c new file mode 100644 index 00000000000..8af34ffe208 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.c @@ -0,0 +1,717 @@ +/* + * Cypress Trackpad PS/2 mouse driver + * + * Copyright (c) 2012 Cypress Semiconductor Corporation. + * + * Author: + *   Dudley Du <dudl@cypress.com> + * + * 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/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" + +#undef CYTP_DEBUG_VERBOSE  /* define this and DEBUG for more verbose dump */ + +static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n) +{ +	struct cytp_data *cytp = psmouse->private; +	cytp->pkt_size = n; +} + +static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; +static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03}; + +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; + +	if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) { +		psmouse_dbg(psmouse, +				"sending 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; +	} + +#ifdef CYTP_DEBUG_VERBOSE +	psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n", +			value & 0xff); +#endif + +	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 byte (0xE8 or 0xF3). +		 * If sending the command fails, send recovery command +		 * to make the device return to the ready state. +		 */ +		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 rc; +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	enum psmouse_state old_state; +	int pktsize; + +	ps2_begin_command(&psmouse->ps2dev); + +	old_state = psmouse->state; +	psmouse->state = PSMOUSE_CMD_MODE; +	psmouse->pktcnt = 0; + +	pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3; +	memset(param, 0, pktsize); + +	rc = cypress_ps2_sendbyte(psmouse, 0xe9); +	if (rc < 0) +		goto out; + +	wait_event_timeout(ps2dev->wait, +			(psmouse->pktcnt >= pktsize), +			msecs_to_jiffies(CYTP_CMD_TIMEOUT)); + +	memcpy(param, psmouse->packet, pktsize); + +	psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n", +			cmd, pktsize, param); + +out: +	psmouse->state = old_state; +	psmouse->pktcnt = 0; + +	ps2_end_command(&psmouse->ps2dev); + +	return rc; +} + +static bool cypress_verify_cmd_state(struct psmouse *psmouse, +				     unsigned char cmd, unsigned char *param) +{ +	bool rate_match = false; +	bool resolution_match = false; +	int i; + +	/* callers will do further checking. */ +	if (cmd == CYTP_CMD_READ_CYPRESS_ID || +	    cmd == CYTP_CMD_STANDARD_MODE || +	    cmd == CYTP_CMD_READ_TP_METRICS) +		return true; + +	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 = true; + +		for (i = 0; i < sizeof(cytp_rate); i++) +			if (cytp_rate[i] == param[2]) +				rate_match = true; + +		if (resolution_match && rate_match) +			return true; +	} + +	psmouse_dbg(psmouse, "verify cmd state failed.\n"); +	return false; +} + +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd, +				unsigned char *param) +{ +	int tries = CYTP_PS2_CMD_TRIES; +	int rc; + +	psmouse_dbg(psmouse, "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) +			continue; + +		if (cypress_verify_cmd_state(psmouse, cmd, param)) +			return 0; + +	} while (--tries > 0); + +	return -EIO; +} + +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 -ENODEV; + +	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ +	if (param[0] != 0x33 || param[1] != 0xCC) +		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 -ENODEV; + +	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ +	if (param[0] != 0x33 || param[1] != 0xCC) +		return -ENODEV; + +	cytp->fw_version = param[2] & FW_VERSION_MASX; +	cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; + +	/* +	 * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to +	 * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594. +	 */ +	if (cytp->fw_version >= 11) +		cytp->tp_metrics_supported = 0; + +	psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); +	psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", +		 cytp->tp_metrics_supported); + +	return 0; +} + +static int cypress_read_tp_metrics(struct psmouse *psmouse) +{ +	struct cytp_data *cytp = psmouse->private; +	unsigned char param[8]; + +	/* set default values for tp metrics. */ +	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; + +	if (!cytp->tp_metrics_supported) +		return 0; + +	memset(param, 0, sizeof(param)); +	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, 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 (!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 -EINVAL; + +	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; +	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + +#ifdef CYTP_DEBUG_VERBOSE +	psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); +	psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); +	psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); +	psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); +	psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); +	psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); +	psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); +	psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); +	psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); + +	psmouse_dbg(psmouse, "tp_type_APA = %d\n", +			(param[6] & TP_METRICS_BIT_APA) ? 1 : 0); +	psmouse_dbg(psmouse, "tp_type_MTG = %d\n", +			(param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); +	psmouse_dbg(psmouse, "tp_palm = %d\n", +			(param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); +	psmouse_dbg(psmouse, "tp_stubborn = %d\n", +			(param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); +	psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", +			(param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); +	psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", +			(param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); +	psmouse_dbg(psmouse, "tp_1f_spike = %d\n", +			param[7] & TP_METRICS_BIT_1F_SPIKE); +	psmouse_dbg(psmouse, "tp_2f_spike = %d\n", +			(param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); +	psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", +			(param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); +#endif + +	return 0; +} + +static int cypress_query_hardware(struct psmouse *psmouse) +{ +	int ret; + +	ret = cypress_read_fw_version(psmouse); +	if (ret) +		return ret; + +	ret = cypress_read_tp_metrics(psmouse); +	if (ret) +		return ret; + +	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->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) +			| CYTP_BIT_ABS_PRESSURE; +	cypress_set_packet_size(psmouse, 5); + +	return 0; +} + +/* + * Reset trackpad device. + * This is also the default mode when trackpad powered on. + */ +static void cypress_reset(struct psmouse *psmouse) +{ +	struct cytp_data *cytp = psmouse->private; + +	cytp->mode = 0; + +	psmouse_reset(psmouse); +} + +static int cypress_set_input_params(struct input_dev *input, +				    struct cytp_data *cytp) +{ +	int ret; + +	if (!cytp->tp_res_x || !cytp->tp_res_y) +		return -EINVAL; + +	__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_DROP_UNUSED|INPUT_MT_TRACK); +	if (ret < 0) +		return ret; + +	__set_bit(INPUT_PROP_SEMI_MT, input->propbit); + +	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); + +	__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) +		return 1; + +	if (header_byte & ABS_HSCROLL_BIT) { +		/* HSCROLL gets added on to 0 finger count. */ +		switch (finger_count) { +			case 0:	return 4; +			case 2: return 5; +			default: +				/* Invalid contact (e.g. palm). Ignore it. */ +				return 0; +		} +	} + +	return finger_count; +} + + +static int cypress_parse_packet(struct psmouse *psmouse, +				struct cytp_data *cytp, struct cytp_report_data *report_data) +{ +	unsigned char *packet = psmouse->packet; +	unsigned char header_byte = packet[0]; + +	memset(report_data, 0, sizeof(struct cytp_report_data)); + +	report_data->contact_cnt = cypress_get_finger_count(header_byte); +	report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; + +	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]; + +	} 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; + +	/* +	 * 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; + +#ifdef CYTP_DEBUG_VERBOSE +	{ +		int i; +		int n = report_data->contact_cnt; +		psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); +		psmouse_dbg(psmouse, "contact_cnt = %d\n", +			report_data->contact_cnt); +		if (n > CYTP_MAX_MT_SLOTS) +		    n = CYTP_MAX_MT_SLOTS; +		for (i = 0; i < n; i++) +			psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, +					report_data->contacts[i].x, +					report_data->contacts[i].y, +					report_data->contacts[i].z); +		psmouse_dbg(psmouse, "left = %d\n", report_data->left); +		psmouse_dbg(psmouse, "right = %d\n", report_data->right); +		psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); +	} +#endif + +	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; +	struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; +	int slots[CYTP_MAX_MT_SLOTS]; +	int n; + +	cypress_parse_packet(psmouse, cytp, &report_data); + +	n = report_data.contact_cnt; +	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; +	} + +	input_mt_assign_slots(input, slots, pos, n); + +	for (i = 0; i < n; i++) { +		contact = &report_data.contacts[i]; +		input_mt_slot(input, slots[i]); +		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); + +	input_mt_report_finger_count(input, report_data.contact_cnt); + +	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; +	} + +	/* +	 * Perform validation (and adjust packet size) based only on the +	 * first byte; allow all further bytes through. +	 */ +	if (index != 0) +		return PSMOUSE_GOOD_DATA; + +	/* +	 * If absolute/relative mode bit has not been set yet, just pass +	 * the byte through. +	 */ +	if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) +		return PSMOUSE_GOOD_DATA; + +	if ((packet[0] & 0x08) == 0x08) +		return PSMOUSE_BAD_DATA; + +	contact_cnt = cypress_get_finger_count(packet[0]); +	if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) +		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); +	else +		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 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->mode |= CYTP_BIT_HIGH_RATE; +	} else { +		psmouse->rate = 40; +		cytp->mode &= ~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; +} + +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_set_absolute_mode(psmouse)) { +		psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); +		return -1; +	} + +	return 0; +} + +int cypress_init(struct psmouse *psmouse) +{ +	struct cytp_data *cytp; + +	cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL); +	if (!cytp) +		return -ENOMEM; + +	psmouse->private = cytp; +	psmouse->pktsize = 8; + +	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; +	psmouse->reconnect = cypress_reconnect; +	psmouse->cleanup = cypress_reset; +	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 00000000000..4720f21d2d7 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.h @@ -0,0 +1,191 @@ +#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_TP_METRICS            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 TP_METRICS_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 TP_METRICS_BIT_PALM               0x80 +#define TP_METRICS_BIT_STUBBORN           0x40 +#define TP_METRICS_BIT_2F_JITTER          0x30 +#define TP_METRICS_BIT_1F_JITTER          0x0c +#define TP_METRICS_BIT_APA                0x02 +#define TP_METRICS_BIT_MTG                0x01 +#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0 +#define TP_METRICS_BIT_2F_SPIKE           0x0c +#define TP_METRICS_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_MT_SLOTS 2 + +struct cytp_contact { +	int x; +	int y; +	int z;  /* also named as touch pressure. */ +}; + +/* The structure of Cypress Trackpad event data. */ +struct cytp_report_data { +	int contact_cnt; +	struct cytp_contact contacts[CYTP_MAX_MT_SLOTS]; +	unsigned int left:1; +	unsigned int right:1; +	unsigned int middle:1; +	unsigned int tap:1;  /* multi-finger tap detected. */ +}; + +/* The structure of Cypress Trackpad device private data. */ +struct cytp_data { +	int fw_version; + +	int pkt_size; +	int mode; + +	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 absolute units that can be reported. */ +	int tp_max_abs_y;  /* Max Y absolute units that can be reported. */ + +	int tp_res_x;  /* X resolution in units/mm. */ +	int tp_res_y;  /* Y resolution in units/mm. */ + +	int tp_metrics_supported; +}; + + +#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 */ diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 04d9bf320a4..ee2a04d90d2 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -10,12 +10,12 @@   * Trademarks are the property of their respective owners.   */ -#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt -  #include <linux/delay.h> +#include <linux/dmi.h>  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/serio.h>  #include <linux/libps2.h>  #include "psmouse.h" @@ -24,13 +24,10 @@  #define elantech_debug(fmt, ...)					\  	do {								\  		if (etd->debug)						\ -			printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__);	\ +			psmouse_printk(KERN_DEBUG, psmouse,		\ +					fmt, ##__VA_ARGS__);		\  	} while (0) -static bool force_elantech; -module_param_named(force_elantech, force_elantech, bool, 0644); -MODULE_PARM_DESC(force_elantech, "Force the Elantech PS/2 protocol extension to be used, 1 = enabled, 0 = disabled (default)."); -  /*   * Send a Synaptics style sliced query command   */ @@ -39,7 +36,25 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,  {  	if (psmouse_sliced_command(psmouse, c) ||  	    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { -		pr_err("synaptics_send_cmd query 0x%02x failed.\n", c); +		psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); +		return -1; +	} + +	return 0; +} + +/* + * V3 and later support this fast command + */ +static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c, +				unsigned char *param) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; + +	if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || +	    ps2_command(ps2dev, NULL, c) || +	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { +		psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);  		return -1;  	} @@ -68,7 +83,7 @@ static int elantech_ps2_command(struct psmouse *psmouse,  	} while (tries > 0);  	if (rc) -		pr_err("ps2 command 0x%02x failed.\n", command); +		psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command);  	return rc;  } @@ -83,7 +98,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,  	unsigned char param[3];  	int rc = 0; -	if (reg < 0x10 || reg > 0x26) +	if (reg < 0x07 || reg > 0x26)  		return -1;  	if (reg > 0x11 && reg < 0x20) @@ -107,12 +122,24 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,  			rc = -1;  		}  		break; + +	case 3 ... 4: +		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || +		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, reg) || +		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { +			rc = -1; +		} +		break;  	}  	if (rc) -		pr_err("failed to read register 0x%02x.\n", reg); -	else +		psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); +	else if (etd->hw_version != 4)  		*val = param[0]; +	else +		*val = param[1];  	return rc;  } @@ -126,7 +153,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,  	struct elantech_data *etd = psmouse->private;  	int rc = 0; -	if (reg < 0x10 || reg > 0x26) +	if (reg < 0x07 || reg > 0x26)  		return -1;  	if (reg > 0x11 && reg < 0x20) @@ -153,11 +180,38 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,  			rc = -1;  		}  		break; + +	case 3: +		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || +		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, reg) || +		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, val) || +		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { +			rc = -1; +		} +		break; + +	case 4: +		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || +		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, reg) || +		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || +		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || +		    elantech_ps2_command(psmouse, NULL, val) || +		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { +			rc = -1; +		} +		break;  	}  	if (rc) -		pr_err("failed to write register 0x%02x with value 0x%02x.\n", -			reg, val); +		psmouse_err(psmouse, +			    "failed to write register 0x%02x with value 0x%02x.\n", +			    reg, val);  	return rc;  } @@ -165,13 +219,13 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,  /*   * Dump a complete mouse movement packet to the syslog   */ -static void elantech_packet_dump(unsigned char *packet, int size) +static void elantech_packet_dump(struct psmouse *psmouse)  {  	int	i; -	printk(KERN_DEBUG pr_fmt("PS/2 packet [")); -	for (i = 0; i < size; i++) -		printk("%s0x%02x ", (i) ? ", " : " ", packet[i]); +	psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet ["); +	for (i = 0; i < psmouse->pktsize; i++) +		printk("%s0x%02x ", i ? ", " : " ", psmouse->packet[i]);  	printk("]\n");  } @@ -222,7 +276,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); @@ -232,7 +286,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)  	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);  	if (etd->fw_version < 0x020000 && -	    (etd->capabilities & ETP_CAP_HAS_ROCKER)) { +	    (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {  		/* rocker up */  		input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);  		/* rocker down */ @@ -242,19 +296,41 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)  	input_sync(dev);  } +static void elantech_set_slot(struct input_dev *dev, int slot, bool active, +			      unsigned int x, unsigned int y) +{ +	input_mt_slot(dev, slot); +	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); +	if (active) { +		input_report_abs(dev, ABS_MT_POSITION_X, x); +		input_report_abs(dev, ABS_MT_POSITION_Y, y); +	} +} + +/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */ +static void elantech_report_semi_mt_data(struct input_dev *dev, +					 unsigned int num_fingers, +					 unsigned int x1, unsigned int y1, +					 unsigned int x2, unsigned int y2) +{ +	elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); +	elantech_set_slot(dev, 1, num_fingers == 2, x2, y2); +} +  /*   * Interpret complete data packets and report absolute mode input events for   * hardware version 2. (6 byte packets)   */  static void elantech_report_absolute_v2(struct psmouse *psmouse)  { +	struct elantech_data *etd = psmouse->private;  	struct input_dev *dev = psmouse->dev;  	unsigned char *packet = psmouse->packet; -	int fingers, x1, y1, x2, y2; +	unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0; +	unsigned int width = 0, pres = 0;  	/* byte 0: n1  n0   .   .   .   .   R   L */  	fingers = (packet[0] & 0xc0) >> 6; -	input_report_key(dev, BTN_TOUCH, fingers != 0);  	switch (fingers) {  	case 3: @@ -267,17 +343,18 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)  		/* pass through... */  	case 1:  		/* -		 * byte 1:  .   .   .   .   .  x10 x9  x8 +		 * byte 1:  .   .   .   .  x11 x10 x9  x8  		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0  		 */ -		input_report_abs(dev, ABS_X, -			((packet[1] & 0x07) << 8) | packet[2]); +		x1 = ((packet[1] & 0x0f) << 8) | packet[2];  		/* -		 * byte 4:  .   .   .   .   .   .  y9  y8 +		 * byte 4:  .   .   .   .  y11 y10 y9  y8  		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0  		 */ -		input_report_abs(dev, ABS_Y, -			ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5])); +		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + +		pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); +		width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);  		break;  	case 2: @@ -287,44 +364,261 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)  		 * byte 0:  .   .  ay8 ax8  .   .   .   .  		 * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0  		 */ -		x1 = ((packet[0] & 0x10) << 4) | packet[1]; +		x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;  		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ -		y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]); +		y1 = etd->y_max - +			((((packet[0] & 0x20) << 3) | packet[2]) << 2);  		/*  		 * byte 3:  .   .  by8 bx8  .   .   .   .  		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0  		 */ -		x2 = ((packet[3] & 0x10) << 4) | packet[4]; +		x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;  		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ -		y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]); +		y2 = etd->y_max - +			((((packet[3] & 0x20) << 3) | packet[5]) << 2); + +		/* Unknown so just report sensible values */ +		pres = 127; +		width = 7; +		break; +	} + +	input_report_key(dev, BTN_TOUCH, fingers != 0); +	if (fingers != 0) { +		input_report_abs(dev, ABS_X, x1); +		input_report_abs(dev, ABS_Y, y1); +	} +	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); +	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); +	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); +	input_report_key(dev, BTN_LEFT, packet[0] & 0x01); +	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); +	if (etd->reports_pressure) { +		input_report_abs(dev, ABS_PRESSURE, pres); +		input_report_abs(dev, ABS_TOOL_WIDTH, width); +	} + +	input_sync(dev); +} + +/* + * Interpret complete data packets and report absolute mode input events for + * hardware version 3. (12 byte packets for two fingers) + */ +static void elantech_report_absolute_v3(struct psmouse *psmouse, +					int packet_type) +{ +	struct input_dev *dev = psmouse->dev; +	struct elantech_data *etd = psmouse->private; +	unsigned char *packet = psmouse->packet; +	unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0; +	unsigned int width = 0, pres = 0; + +	/* byte 0: n1  n0   .   .   .   .   R   L */ +	fingers = (packet[0] & 0xc0) >> 6; + +	switch (fingers) { +	case 3: +	case 1:  		/* -		 * For compatibility with the X Synaptics driver scale up -		 * one coordinate and report as ordinary mouse movent +		 * byte 1:  .   .   .   .  x11 x10 x9  x8 +		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0  		 */ -		input_report_abs(dev, ABS_X, x1 << 2); -		input_report_abs(dev, ABS_Y, y1 << 2); +		x1 = ((packet[1] & 0x0f) << 8) | packet[2];  		/* -		 * For compatibility with the proprietary X Elantech driver -		 * report both coordinates as hat coordinates +		 * byte 4:  .   .   .   .  y11 y10 y9  y8 +		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0  		 */ -		input_report_abs(dev, ABS_HAT0X, x1); -		input_report_abs(dev, ABS_HAT0Y, y1); -		input_report_abs(dev, ABS_HAT1X, x2); -		input_report_abs(dev, ABS_HAT1Y, y2); +		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); +		break; + +	case 2: +		if (packet_type == PACKET_V3_HEAD) { +			/* +			 * byte 1:   .    .    .    .  ax11 ax10 ax9  ax8 +			 * byte 2: ax7  ax6  ax5  ax4  ax3  ax2  ax1  ax0 +			 */ +			etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2]; +			/* +			 * byte 4:   .    .    .    .  ay11 ay10 ay9  ay8 +			 * byte 5: ay7  ay6  ay5  ay4  ay3  ay2  ay1  ay0 +			 */ +			etd->mt[0].y = etd->y_max - +				(((packet[4] & 0x0f) << 8) | packet[5]); +			/* +			 * wait for next packet +			 */ +			return; +		} + +		/* packet_type == PACKET_V3_TAIL */ +		x1 = etd->mt[0].x; +		y1 = etd->mt[0].y; +		x2 = ((packet[1] & 0x0f) << 8) | packet[2]; +		y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);  		break;  	} +	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); +	width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); + +	input_report_key(dev, BTN_TOUCH, fingers != 0); +	if (fingers != 0) { +		input_report_abs(dev, ABS_X, x1); +		input_report_abs(dev, ABS_Y, y1); +	} +	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);  	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); -	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); -	input_report_key(dev, BTN_LEFT, packet[0] & 0x01); -	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + +	/* For clickpads map both buttons to BTN_LEFT */ +	if (etd->fw_version & 0x001000) { +		input_report_key(dev, BTN_LEFT, packet[0] & 0x03); +	} else { +		input_report_key(dev, BTN_LEFT, packet[0] & 0x01); +		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); +	} + +	input_report_abs(dev, ABS_PRESSURE, pres); +	input_report_abs(dev, ABS_TOOL_WIDTH, width);  	input_sync(dev);  } -static int elantech_check_parity_v1(struct psmouse *psmouse) +static void elantech_input_sync_v4(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	struct elantech_data *etd = psmouse->private; +	unsigned char *packet = psmouse->packet; + +	/* For clickpads map both buttons to BTN_LEFT */ +	if (etd->fw_version & 0x001000) { +		input_report_key(dev, BTN_LEFT, packet[0] & 0x03); +	} else { +		input_report_key(dev, BTN_LEFT, packet[0] & 0x01); +		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); +	} + +	input_mt_report_pointer_emulation(dev, true); +	input_sync(dev); +} + +static void process_packet_status_v4(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	unsigned char *packet = psmouse->packet; +	unsigned fingers; +	int i; + +	/* notify finger state change */ +	fingers = packet[1] & 0x1f; +	for (i = 0; i < ETP_MAX_FINGERS; i++) { +		if ((fingers & (1 << i)) == 0) { +			input_mt_slot(dev, i); +			input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); +		} +	} + +	elantech_input_sync_v4(psmouse); +} + +static void process_packet_head_v4(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	struct elantech_data *etd = psmouse->private; +	unsigned char *packet = psmouse->packet; +	int id = ((packet[3] & 0xe0) >> 5) - 1; +	int pres, traces; + +	if (id < 0) +		return; + +	etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2]; +	etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); +	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); +	traces = (packet[0] & 0xf0) >> 4; + +	input_mt_slot(dev, id); +	input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + +	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); +	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); +	input_report_abs(dev, ABS_MT_PRESSURE, pres); +	input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width); +	/* report this for backwards compatibility */ +	input_report_abs(dev, ABS_TOOL_WIDTH, traces); + +	elantech_input_sync_v4(psmouse); +} + +static void process_packet_motion_v4(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	struct elantech_data *etd = psmouse->private; +	unsigned char *packet = psmouse->packet; +	int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0; +	int id, sid; + +	id = ((packet[0] & 0xe0) >> 5) - 1; +	if (id < 0) +		return; + +	sid = ((packet[3] & 0xe0) >> 5) - 1; +	weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1; +	/* +	 * Motion packets give us the delta of x, y values of specific fingers, +	 * but in two's complement. Let the compiler do the conversion for us. +	 * Also _enlarge_ the numbers to int, in case of overflow. +	 */ +	delta_x1 = (signed char)packet[1]; +	delta_y1 = (signed char)packet[2]; +	delta_x2 = (signed char)packet[4]; +	delta_y2 = (signed char)packet[5]; + +	etd->mt[id].x += delta_x1 * weight; +	etd->mt[id].y -= delta_y1 * weight; +	input_mt_slot(dev, id); +	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); +	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); + +	if (sid >= 0) { +		etd->mt[sid].x += delta_x2 * weight; +		etd->mt[sid].y -= delta_y2 * weight; +		input_mt_slot(dev, sid); +		input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x); +		input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y); +	} + +	elantech_input_sync_v4(psmouse); +} + +static void elantech_report_absolute_v4(struct psmouse *psmouse, +					int packet_type) +{ +	switch (packet_type) { +	case PACKET_V4_STATUS: +		process_packet_status_v4(psmouse); +		break; + +	case PACKET_V4_HEAD: +		process_packet_head_v4(psmouse); +		break; + +	case PACKET_V4_MOTION: +		process_packet_motion_v4(psmouse); +		break; + +	case PACKET_UNKNOWN: +	default: +		/* impossible to get here */ +		break; +	} +} + +static int elantech_packet_check_v1(struct psmouse *psmouse)  {  	struct elantech_data *etd = psmouse->private;  	unsigned char *packet = psmouse->packet; @@ -348,31 +642,172 @@ static int elantech_check_parity_v1(struct psmouse *psmouse)  	       etd->parity[packet[3]] == p3;  } +static int elantech_debounce_check_v2(struct psmouse *psmouse) +{ +        /* +         * When we encounter packet that matches this exactly, it means the +         * hardware is in debounce status. Just ignore the whole packet. +         */ +        const u8 debounce_packet[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff }; +        unsigned char *packet = psmouse->packet; + +        return !memcmp(packet, debounce_packet, sizeof(debounce_packet)); +} + +static int elantech_packet_check_v2(struct psmouse *psmouse) +{ +	struct elantech_data *etd = psmouse->private; +	unsigned char *packet = psmouse->packet; + +	/* +	 * V2 hardware has two flavors. Older ones that do not report pressure, +	 * and newer ones that reports pressure and width. With newer ones, all +	 * packets (1, 2, 3 finger touch) have the same constant bits. With +	 * older ones, 1/3 finger touch packets and 2 finger touch packets +	 * have different constant bits. +	 * With all three cases, if the constant bits are not exactly what I +	 * expected, I consider them invalid. +	 */ +	if (etd->reports_pressure) +		return (packet[0] & 0x0c) == 0x04 && +		       (packet[3] & 0x0f) == 0x02; + +	if ((packet[0] & 0xc0) == 0x80) +		return (packet[0] & 0x0c) == 0x0c && +		       (packet[3] & 0x0e) == 0x08; + +	return (packet[0] & 0x3c) == 0x3c && +	       (packet[1] & 0xf0) == 0x00 && +	       (packet[3] & 0x3e) == 0x38 && +	       (packet[4] & 0xf0) == 0x00; +} + +/* + * We check the constant bits to determine what packet type we get, + * so packet checking is mandatory for v3 and later hardware. + */ +static int elantech_packet_check_v3(struct psmouse *psmouse) +{ +	struct elantech_data *etd = psmouse->private; +	const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff }; +	unsigned char *packet = psmouse->packet; + +	/* +	 * check debounce first, it has the same signature in byte 0 +	 * and byte 3 as PACKET_V3_HEAD. +	 */ +	if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) +		return PACKET_DEBOUNCE; + +	/* +	 * If the hardware flag 'crc_enabled' is set the packets have +	 * different signatures. +	 */ +	if (etd->crc_enabled) { +		if ((packet[3] & 0x09) == 0x08) +			return PACKET_V3_HEAD; + +		if ((packet[3] & 0x09) == 0x09) +			return PACKET_V3_TAIL; +	} else { +		if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) +			return PACKET_V3_HEAD; + +		if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) +			return PACKET_V3_TAIL; +	} + +	return PACKET_UNKNOWN; +} + +static int elantech_packet_check_v4(struct psmouse *psmouse) +{ +	struct elantech_data *etd = psmouse->private; +	unsigned char *packet = psmouse->packet; +	unsigned char packet_type = packet[3] & 0x03; +	bool sanity_check; + +	/* +	 * Sanity check based on the constant bits of a packet. +	 * The constant bits change depending on the value of +	 * the hardware flag 'crc_enabled' but are the same for +	 * every packet, regardless of the type. +	 */ +	if (etd->crc_enabled) +		sanity_check = ((packet[3] & 0x08) == 0x00); +	else +		sanity_check = ((packet[0] & 0x0c) == 0x04 && +				(packet[3] & 0x1c) == 0x10); + +	if (!sanity_check) +		return PACKET_UNKNOWN; + +	switch (packet_type) { +	case 0: +		return PACKET_V4_STATUS; + +	case 1: +		return PACKET_V4_HEAD; + +	case 2: +		return PACKET_V4_MOTION; +	} + +	return PACKET_UNKNOWN; +} +  /*   * Process byte stream from mouse and handle complete packets   */  static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)  {  	struct elantech_data *etd = psmouse->private; +	int packet_type;  	if (psmouse->pktcnt < psmouse->pktsize)  		return PSMOUSE_GOOD_DATA;  	if (etd->debug > 1) -		elantech_packet_dump(psmouse->packet, psmouse->pktsize); +		elantech_packet_dump(psmouse);  	switch (etd->hw_version) {  	case 1: -		if (etd->paritycheck && !elantech_check_parity_v1(psmouse)) +		if (etd->paritycheck && !elantech_packet_check_v1(psmouse))  			return PSMOUSE_BAD_DATA;  		elantech_report_absolute_v1(psmouse);  		break;  	case 2: -		/* We don't know how to check parity in protocol v2 */ +		/* ignore debounce */ +		if (elantech_debounce_check_v2(psmouse)) +			return PSMOUSE_FULL_PACKET; + +		if (etd->paritycheck && !elantech_packet_check_v2(psmouse)) +			return PSMOUSE_BAD_DATA; +  		elantech_report_absolute_v2(psmouse);  		break; + +	case 3: +		packet_type = elantech_packet_check_v3(psmouse); +		/* ignore debounce */ +		if (packet_type == PACKET_DEBOUNCE) +			return PSMOUSE_FULL_PACKET; + +		if (packet_type == PACKET_UNKNOWN) +			return PSMOUSE_BAD_DATA; + +		elantech_report_absolute_v3(psmouse, packet_type); +		break; + +	case 4: +		packet_type = elantech_packet_check_v4(psmouse); +		if (packet_type == PACKET_UNKNOWN) +			return PSMOUSE_BAD_DATA; + +		elantech_report_absolute_v4(psmouse, packet_type); +		break;  	}  	return PSMOUSE_FULL_PACKET; @@ -407,15 +842,33 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)  		    elantech_write_reg(psmouse, 0x11, etd->reg_11) ||  		    elantech_write_reg(psmouse, 0x21, etd->reg_21)) {  			rc = -1; -			break;  		} +		break; + +	case 3: +		if (etd->set_hw_resolution) +			etd->reg_10 = 0x0b; +		else +			etd->reg_10 = 0x01; + +		if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) +			rc = -1; + +		break; + +	case 4: +		etd->reg_07 = 0x01; +		if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) +			rc = -1; + +		goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */  	}  	if (rc == 0) {  		/*  		 * Read back reg 0x10. For hardware version 1 we must make  		 * sure the absolute mode bit is set. For hardware version 2 -		 * the touchpad is probably initalising and not ready until +		 * the touchpad is probably initializing and not ready until  		 * we read back the value we just wrote.  		 */  		do { @@ -428,28 +881,180 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)  		} while (tries > 0);  		if (rc) { -			pr_err("failed to read back register 0x10.\n"); +			psmouse_err(psmouse, +				    "failed to read back register 0x10.\n");  		} else if (etd->hw_version == 1 &&  			   !(val & ETP_R10_ABSOLUTE_MODE)) { -			pr_err("touchpad refuses to switch to absolute mode.\n"); +			psmouse_err(psmouse, +				    "touchpad refuses to switch to absolute mode.\n");  			rc = -1;  		}  	} + skip_readback_reg_10:  	if (rc) -		pr_err("failed to initialise registers.\n"); +		psmouse_err(psmouse, "failed to initialise registers.\n");  	return rc;  } +static int elantech_set_range(struct psmouse *psmouse, +			      unsigned int *x_min, unsigned int *y_min, +			      unsigned int *x_max, unsigned int *y_max, +			      unsigned int *width) +{ +	struct elantech_data *etd = psmouse->private; +	unsigned char param[3]; +	unsigned char traces; + +	switch (etd->hw_version) { +	case 1: +		*x_min = ETP_XMIN_V1; +		*y_min = ETP_YMIN_V1; +		*x_max = ETP_XMAX_V1; +		*y_max = ETP_YMAX_V1; +		break; + +	case 2: +		if (etd->fw_version == 0x020800 || +		    etd->fw_version == 0x020b00 || +		    etd->fw_version == 0x020030) { +			*x_min = ETP_XMIN_V2; +			*y_min = ETP_YMIN_V2; +			*x_max = ETP_XMAX_V2; +			*y_max = ETP_YMAX_V2; +		} else { +			int i; +			int fixed_dpi; + +			i = (etd->fw_version > 0x020800 && +			     etd->fw_version < 0x020900) ? 1 : 2; + +			if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) +				return -1; + +			fixed_dpi = param[1] & 0x10; + +			if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) { +				if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) +					return -1; + +				*x_max = (etd->capabilities[1] - i) * param[1] / 2; +				*y_max = (etd->capabilities[2] - i) * param[2] / 2; +			} else if (etd->fw_version == 0x040216) { +				*x_max = 819; +				*y_max = 405; +			} else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) { +				*x_max = 900; +				*y_max = 500; +			} else { +				*x_max = (etd->capabilities[1] - i) * 64; +				*y_max = (etd->capabilities[2] - i) * 64; +			} +		} +		break; + +	case 3: +		if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) +			return -1; + +		*x_max = (0x0f & param[0]) << 8 | param[1]; +		*y_max = (0xf0 & param[0]) << 4 | param[2]; +		break; + +	case 4: +		if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) +			return -1; + +		*x_max = (0x0f & param[0]) << 8 | param[1]; +		*y_max = (0xf0 & param[0]) << 4 | param[2]; +		traces = etd->capabilities[1]; +		if ((traces < 2) || (traces > *x_max)) +			return -1; + +		*width = *x_max / (traces - 1); +		break; +	} + +	return 0; +} + +/* + * (value from firmware) * 10 + 790 = dpi + * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) + */ +static unsigned int elantech_convert_res(unsigned int val) +{ +	return (val * 10 + 790) * 10 / 254; +} + +static int elantech_get_resolution_v4(struct psmouse *psmouse, +				      unsigned int *x_res, +				      unsigned int *y_res) +{ +	unsigned char param[3]; + +	if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param)) +		return -1; + +	*x_res = elantech_convert_res(param[1] & 0x0f); +	*y_res = elantech_convert_res((param[1] & 0xf0) >> 4); + +	return 0; +} + +/* + * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in + * fw_version for this is based on the following fw_version & caps table: + * + * Laptop-model:           fw_version:     caps:           buttons: + * Acer S3                 0x461f00        10, 13, 0e      clickpad + * Acer S7-392             0x581f01        50, 17, 0d      clickpad + * Acer V5-131             0x461f02        01, 16, 0c      clickpad + * Acer V5-551             0x461f00        ?               clickpad + * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons + * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons + * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons + * Asus UX31               0x361f00        20, 15, 0e      clickpad + * Asus UX32VD             0x361f02        00, 15, 0e      clickpad + * Avatar AVIU-145A2       0x361f00        ?               clickpad + * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons + * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*) + * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons + * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad + * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad + * Samsung NP900X3E-A02    0x575f03        ?               clickpad + * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad + * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons + * Samsung RF710           0x450f00        ?               2 hw buttons + * System76 Pangolin       0x250f01        ?               2 hw buttons + * (*) + 3 trackpoint buttons + */ +static void elantech_set_buttonpad_prop(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	struct elantech_data *etd = psmouse->private; + +	if (etd->fw_version & 0x001000) { +		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); +		__clear_bit(BTN_RIGHT, dev->keybit); +	} +} +  /*   * 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 int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; +	unsigned int x_res = 0, y_res = 0; + +	if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) +		return -1; +	__set_bit(INPUT_PROP_POINTER, dev->propbit);  	__set_bit(EV_KEY, dev->evbit);  	__set_bit(EV_ABS, dev->evbit);  	__clear_bit(EV_REL, dev->evbit); @@ -466,24 +1071,78 @@ static void elantech_set_input_params(struct psmouse *psmouse)  	case 1:  		/* Rocker button */  		if (etd->fw_version < 0x020000 && -		    (etd->capabilities & ETP_CAP_HAS_ROCKER)) { +		    (etd->capabilities[0] & 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, x_min, x_max, 0, 0); +		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);  		break;  	case 2:  		__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_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); -		input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); -		input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); -		input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); +		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit); +		/* fall through */ +	case 3: +		if (etd->hw_version == 3) +			elantech_set_buttonpad_prop(psmouse); +		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); +		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); +		if (etd->reports_pressure) { +			input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, +					     ETP_PMAX_V2, 0, 0); +			input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, +					     ETP_WMAX_V2, 0, 0); +		} +		input_mt_init_slots(dev, 2, 0); +		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); +		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); +		break; + +	case 4: +		if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) { +			/* +			 * if query failed, print a warning and leave the values +			 * zero to resemble synaptics.c behavior. +			 */ +			psmouse_warn(psmouse, "couldn't query resolution data.\n"); +		} +		elantech_set_buttonpad_prop(psmouse); +		__set_bit(BTN_TOOL_QUADTAP, dev->keybit); +		/* For X to recognize me as touchpad. */ +		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); +		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); +		input_abs_set_res(dev, ABS_X, x_res); +		input_abs_set_res(dev, ABS_Y, y_res); +		/* +		 * range of pressure and width is the same as v2, +		 * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility. +		 */ +		input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, +				     ETP_PMAX_V2, 0, 0); +		input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, +				     ETP_WMAX_V2, 0, 0); +		/* Multitouch capable pad, up to 5 fingers. */ +		input_mt_init_slots(dev, ETP_MAX_FINGERS, 0); +		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); +		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); +		input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); +		input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res); +		input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2, +				     ETP_PMAX_V2, 0, 0); +		/* +		 * The firmware reports how many trace lines the finger spans, +		 * convert to surface unit as Protocol-B requires. +		 */ +		input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, +				     ETP_WMAX_V2 * width, 0, 0);  		break;  	} + +	etd->y_max = y_max; +	etd->width = width; + +	return 0;  }  struct elantech_attr_data { @@ -517,16 +1176,13 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,  	struct elantech_data *etd = psmouse->private;  	struct elantech_attr_data *attr = data;  	unsigned char *reg = (unsigned char *) etd + attr->field_offset; -	unsigned long value; +	unsigned char value;  	int err; -	err = strict_strtoul(buf, 16, &value); +	err = kstrtou8(buf, 16, &value);  	if (err)  		return err; -	if (value > 0xff) -		return -EINVAL; -  	/* Do we need to preserve some bits for version 2 hardware too? */  	if (etd->hw_version == 1) {  		if (attr->reg == 0x10) @@ -553,6 +1209,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,  			    elantech_show_int_attr,			\  			    elantech_set_int_attr) +ELANTECH_INT_ATTR(reg_07, 0x07);  ELANTECH_INT_ATTR(reg_10, 0x10);  ELANTECH_INT_ATTR(reg_11, 0x11);  ELANTECH_INT_ATTR(reg_20, 0x20); @@ -566,6 +1223,7 @@ ELANTECH_INT_ATTR(debug, 0);  ELANTECH_INT_ATTR(paritycheck, 0);  static struct attribute *elantech_attrs[] = { +	&psmouse_attr_reg_07.dattr.attr,  	&psmouse_attr_reg_10.dattr.attr,  	&psmouse_attr_reg_11.dattr.attr,  	&psmouse_attr_reg_20.dattr.attr, @@ -617,7 +1275,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)  	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||  	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||  	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { -		pr_debug("sending Elantech magic knock failed.\n"); +		psmouse_dbg(psmouse, "sending Elantech magic knock failed.\n");  		return -1;  	} @@ -625,9 +1283,11 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)  	 * 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) { -		pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", -			 param[0], param[1], param[2]); +	if (param[0] != 0x3c || param[1] != 0x03 || +	    (param[2] != 0xc8 && param[2] != 0x00)) { +		psmouse_dbg(psmouse, +			    "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", +			    param[0], param[1], param[2]);  		return -1;  	} @@ -637,20 +1297,18 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)  	 * to Elantech magic knock and there might be more.  	 */  	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { -		pr_debug("failed to query firmware version.\n"); +		psmouse_dbg(psmouse, "failed to query firmware version.\n");  		return -1;  	} -	pr_debug("Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", -		 param[0], param[1], param[2]); +	psmouse_dbg(psmouse, +		    "Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", +		    param[0], param[1], param[2]);  	if (!elantech_is_signature_valid(param)) { -		if (!force_elantech) { -			pr_debug("Probably not a real Elantech touchpad. Aborting.\n"); -			return -1; -		} - -		pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); +		psmouse_dbg(psmouse, +			    "Probably not a real Elantech touchpad. Aborting.\n"); +		return -1;  	}  	if (set_properties) { @@ -677,11 +1335,14 @@ static void elantech_disconnect(struct psmouse *psmouse)   */  static int elantech_reconnect(struct psmouse *psmouse)  { +	psmouse_reset(psmouse); +  	if (elantech_detect(psmouse, 0))  		return -1;  	if (elantech_set_absolute_mode(psmouse)) { -		pr_err("failed to put touchpad back into absolute mode.\n"); +		psmouse_err(psmouse, +			    "failed to put touchpad back into absolute mode.\n");  		return -1;  	} @@ -689,6 +1350,89 @@ static int elantech_reconnect(struct psmouse *psmouse)  }  /* + * Some hw_version 3 models go into error state when we try to set + * bit 3 and/or bit 1 of r10. + */ +static const struct dmi_system_id no_hw_res_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) +	{ +		/* Gigabyte U2442 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), +			DMI_MATCH(DMI_PRODUCT_NAME, "U2442"), +		}, +	}, +#endif +	{ } +}; + +/* + * determine hardware version and set some properties according to it. + */ +static int elantech_set_properties(struct elantech_data *etd) +{ +	/* This represents the version of IC body. */ +	int ver = (etd->fw_version & 0x0f0000) >> 16; + +	/* Early version of Elan touchpads doesn't obey the rule. */ +	if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600) +		etd->hw_version = 1; +	else { +		switch (ver) { +		case 2: +		case 4: +			etd->hw_version = 2; +			break; +		case 5: +			etd->hw_version = 3; +			break; +		case 6: +		case 7: +		case 8: +		case 9: +			etd->hw_version = 4; +			break; +		default: +			return -1; +		} +	} + +	/* decide which send_cmd we're gonna use early */ +	etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd : +					       synaptics_send_cmd; + +	/* Turn on packet checking by default */ +	etd->paritycheck = 1; + +	/* +	 * This firmware suffers from misreporting coordinates when +	 * a touch action starts causing the mouse cursor or scrolled page +	 * to jump. Enable a workaround. +	 */ +	etd->jumpy_cursor = +		(etd->fw_version == 0x020022 || etd->fw_version == 0x020600); + +	if (etd->hw_version > 1) { +		/* For now show extra debug information */ +		etd->debug = 1; + +		if (etd->fw_version >= 0x020800) +			etd->reports_pressure = true; +	} + +	/* +	 * The signatures of v3 and v4 packets change depending on the +	 * value of this hardware flag. +	 */ +	etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000); + +	/* Enable real hardware resolution on hw_version 3 ? */ +	etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table); + +	return 0; +} + +/*   * Initialize the touchpad and create sysfs entries   */  int elantech_init(struct psmouse *psmouse) @@ -701,6 +1445,8 @@ int elantech_init(struct psmouse *psmouse)  	if (!etd)  		return -ENOMEM; +	psmouse_reset(psmouse); +  	etd->parity[0] = 1;  	for (i = 1; i < 256; i++)  		etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; @@ -709,66 +1455,53 @@ int elantech_init(struct psmouse *psmouse)  	 * Do the version query again so we can store the result  	 */  	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { -		pr_err("failed to query firmware version.\n"); +		psmouse_err(psmouse, "failed to query firmware version.\n");  		goto init_fail;  	} -  	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) { -		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; -	} else { -		etd->hw_version = 1; -		etd->paritycheck = 1; -	} - -	pr_info("assuming hardware version %d, firmware version %d.%d.%d\n", -		etd->hw_version, param[0], param[1], param[2]); - -	if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { -		pr_err("failed to query capabilities.\n"); +	if (elantech_set_properties(etd)) { +		psmouse_err(psmouse, "unknown hardware version, aborting...\n");  		goto init_fail;  	} -	pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", -		param[0], param[1], param[2]); -	etd->capabilities = param[0]; +	psmouse_info(psmouse, +		     "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n", +		     etd->hw_version, param[0], param[1], param[2]); -	/* -	 * This firmware suffers from misreporting coordinates when -	 * a touch action starts causing the mouse cursor or scrolled page -	 * to jump. Enable a workaround. -	 */ -	if (etd->fw_version == 0x020022 || etd->fw_version == 0x020600) { -		pr_info("firmware version 2.0.34/2.6.0 detected, enabling jumpy cursor workaround\n"); -		etd->jumpy_cursor = true; +	if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY, +	    etd->capabilities)) { +		psmouse_err(psmouse, "failed to query capabilities.\n"); +		goto init_fail;  	} +	psmouse_info(psmouse, +		     "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", +		     etd->capabilities[0], etd->capabilities[1], +		     etd->capabilities[2]);  	if (elantech_set_absolute_mode(psmouse)) { -		pr_err("failed to put touchpad into absolute mode.\n"); +		psmouse_err(psmouse, +			    "failed to put touchpad into absolute mode.\n");  		goto init_fail;  	} -	elantech_set_input_params(psmouse); +	if (elantech_set_input_params(psmouse)) { +		psmouse_err(psmouse, "failed to query touchpad range.\n"); +		goto init_fail; +	}  	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,  				   &elantech_attr_group);  	if (error) { -		pr_err("failed to create sysfs attributes, error: %d.\n", error); +		psmouse_err(psmouse, +			    "failed to create sysfs attributes, error: %d.\n", +			    error);  		goto init_fail;  	}  	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 ? 6 : 4;  	return 0; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index aa4aac5d219..9e0e2a1f340 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -16,14 +16,18 @@  /*   * Command values for Synaptics style queries   */ +#define ETP_FW_ID_QUERY			0x00  #define ETP_FW_VERSION_QUERY		0x01  #define ETP_CAPABILITIES_QUERY		0x02 +#define ETP_SAMPLE_QUERY		0x03 +#define ETP_RESOLUTION_QUERY		0x04  /*   * Command values for register reading or writing   */  #define ETP_REGISTER_READ		0x10  #define ETP_REGISTER_WRITE		0x11 +#define ETP_REGISTER_READWRITE		0x00  /*   * Hardware version 2 custom PS/2 command value @@ -66,29 +70,51 @@  #define ETP_YMAX_V1			(384 - ETP_EDGE_FUZZ_V1)  /* - * It seems the resolution for hardware version 2 doubled. - * Hence the X and Y ranges are doubled too. - * The bezel around the pad also appears to be smaller + * The resolution for older v2 hardware doubled. + * (newer v2's firmware provides command so we can query)   */ -#define ETP_EDGE_FUZZ_V2		8 +#define ETP_XMIN_V2			0 +#define ETP_XMAX_V2			1152 +#define ETP_YMIN_V2			0 +#define ETP_YMAX_V2			768 -#define ETP_XMIN_V2			(   0 + ETP_EDGE_FUZZ_V2) -#define ETP_XMAX_V2			(1152 - ETP_EDGE_FUZZ_V2) -#define ETP_YMIN_V2			(   0 + ETP_EDGE_FUZZ_V2) -#define ETP_YMAX_V2			( 768 - ETP_EDGE_FUZZ_V2) +#define ETP_PMIN_V2			0 +#define ETP_PMAX_V2			255 +#define ETP_WMIN_V2			0 +#define ETP_WMAX_V2			15  /* - * For two finger touches the coordinate of each finger gets reported - * separately but with reduced resolution. + * v3 hardware has 2 kinds of packet types, + * v4 hardware has 3.   */ -#define ETP_2FT_FUZZ			4 +#define PACKET_UNKNOWN			0x01 +#define PACKET_DEBOUNCE			0x02 +#define PACKET_V3_HEAD			0x03 +#define PACKET_V3_TAIL			0x04 +#define PACKET_V4_HEAD			0x05 +#define PACKET_V4_MOTION		0x06 +#define PACKET_V4_STATUS		0x07 -#define ETP_2FT_XMIN			(  0 + ETP_2FT_FUZZ) -#define ETP_2FT_XMAX			(288 - ETP_2FT_FUZZ) -#define ETP_2FT_YMIN			(  0 + ETP_2FT_FUZZ) -#define ETP_2FT_YMAX			(192 - ETP_2FT_FUZZ) +/* + * track up to 5 fingers for v4 hardware + */ +#define ETP_MAX_FINGERS			5 + +/* + * weight value for v4 hardware + */ +#define ETP_WEIGHT_VALUE		5 + +/* + * The base position for one finger, v4 hardware + */ +struct finger_pos { +	unsigned int x; +	unsigned int y; +};  struct elantech_data { +	unsigned char reg_07;  	unsigned char reg_10;  	unsigned char reg_11;  	unsigned char reg_20; @@ -99,13 +125,20 @@ struct elantech_data {  	unsigned char reg_25;  	unsigned char reg_26;  	unsigned char debug; -	unsigned char capabilities; +	unsigned char capabilities[3];  	bool paritycheck;  	bool jumpy_cursor; +	bool reports_pressure; +	bool crc_enabled; +	bool set_hw_resolution;  	unsigned char hw_version;  	unsigned int fw_version;  	unsigned int single_finger_reports; +	unsigned int y_max; +	unsigned int width; +	struct finger_pos mt[ETP_MAX_FINGERS];  	unsigned char parity[256]; +	int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);  };  #ifdef CONFIG_MOUSE_PS2_ELANTECH diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 7b6ce178f1b..8c7d94200bd 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -8,13 +8,12 @@   * published by the Free Software Foundation.   */ -#include <linux/init.h>  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/input-polldev.h> +#include <linux/gpio.h>  #include <linux/gpio_mouse.h> -#include <asm/gpio.h>  /*   * Timer function which is run every scan_ms ms when the device is opened. @@ -46,9 +45,9 @@ static void gpio_mouse_scan(struct input_polled_dev *dev)  	input_sync(input);  } -static int __devinit gpio_mouse_probe(struct platform_device *pdev) +static int gpio_mouse_probe(struct platform_device *pdev)  { -	struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data; +	struct gpio_mouse_platform_data *pdata = dev_get_platdata(&pdev->dev);  	struct input_polled_dev *input_poll;  	struct input_dev *input;  	int pin, i; @@ -138,7 +137,6 @@ static int __devinit gpio_mouse_probe(struct platform_device *pdev)   out_free_polldev:  	input_free_polled_device(input_poll); -	platform_set_drvdata(pdev, NULL);   out_free_gpios:  	while (--i >= 0) { @@ -150,7 +148,7 @@ static int __devinit gpio_mouse_probe(struct platform_device *pdev)  	return error;  } -static int __devexit gpio_mouse_remove(struct platform_device *pdev) +static int gpio_mouse_remove(struct platform_device *pdev)  {  	struct input_polled_dev *input = platform_get_drvdata(pdev);  	struct gpio_mouse_platform_data *pdata = input->private; @@ -165,33 +163,20 @@ static int __devexit gpio_mouse_remove(struct platform_device *pdev)  			gpio_free(pin);  	} -	platform_set_drvdata(pdev, NULL); -  	return 0;  }  static struct platform_driver gpio_mouse_device_driver = {  	.probe		= gpio_mouse_probe, -	.remove		= __devexit_p(gpio_mouse_remove), +	.remove		= gpio_mouse_remove,  	.driver		= {  		.name	= "gpio_mouse",  		.owner	= THIS_MODULE,  	}  }; +module_platform_driver(gpio_mouse_device_driver); -static int __init gpio_mouse_init(void) -{ -	return platform_driver_register(&gpio_mouse_device_driver); -} -module_init(gpio_mouse_init); - -static void __exit gpio_mouse_exit(void) -{ -	platform_driver_unregister(&gpio_mouse_device_driver); -} -module_exit(gpio_mouse_exit); - -MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");  MODULE_DESCRIPTION("GPIO mouse driver");  MODULE_LICENSE("GPL");  MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 1d2205b2480..62be888e83d 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -32,6 +32,7 @@  #define DEBUG  #include <linux/slab.h>  #include <linux/input.h> +#include <linux/module.h>  #include <linux/serio.h>  #include <linux/libps2.h>  #include <linux/delay.h> @@ -40,6 +41,8 @@  #include "psmouse.h"  #include "hgpk.h" +#define ILLEGAL_XY 999999 +  static bool tpdebug;  module_param(tpdebug, bool, 0644);  MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); @@ -47,48 +50,150 @@ MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");  static int recalib_delta = 100;  module_param(recalib_delta, int, 0644);  MODULE_PARM_DESC(recalib_delta, -	"packets containing a delta this large will cause a recalibration."); +	"packets containing a delta this large will be discarded, and a " +	"recalibration may be scheduled."); -static int jumpy_delay = 1000; +static int jumpy_delay = 20;  module_param(jumpy_delay, int, 0644);  MODULE_PARM_DESC(jumpy_delay,  	"delay (ms) before recal after jumpiness detected"); -static int spew_delay = 1000; +static int spew_delay = 1;  module_param(spew_delay, int, 0644);  MODULE_PARM_DESC(spew_delay,  	"delay (ms) before recal after packet spew detected"); -static int recal_guard_time = 2000; +static int recal_guard_time;  module_param(recal_guard_time, int, 0644);  MODULE_PARM_DESC(recal_guard_time,  	"interval (ms) during which recal will be restarted if packet received"); -static int post_interrupt_delay = 1000; +static int post_interrupt_delay = 40;  module_param(post_interrupt_delay, int, 0644);  MODULE_PARM_DESC(post_interrupt_delay,  	"delay (ms) before recal after recal interrupt detected"); +static bool autorecal = true; +module_param(autorecal, bool, 0644); +MODULE_PARM_DESC(autorecal, "enable recalibration in the driver"); + +static char hgpk_mode_name[16]; +module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); +MODULE_PARM_DESC(hgpk_mode, +	"default hgpk mode: mouse, glidesensor or pentablet"); + +static int hgpk_default_mode = HGPK_MODE_MOUSE; + +static const char * const hgpk_mode_names[] = { +	[HGPK_MODE_MOUSE] = "Mouse", +	[HGPK_MODE_GLIDESENSOR] = "GlideSensor", +	[HGPK_MODE_PENTABLET] = "PenTablet", +}; + +static int hgpk_mode_from_name(const char *buf, int len) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) { +		const char *name = hgpk_mode_names[i]; +		if (strlen(name) == len && !strncasecmp(name, buf, len)) +			return i; +	} + +	return HGPK_MODE_INVALID; +} +  /* - * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" - * above the pad and still have it send packets.  This causes a jump cursor - * when one places their finger on the pad.  We can probably detect the - * jump as we see a large deltas (>= 100px).  In mouse mode, I've been - * unable to even come close to 100px deltas during normal usage, so I think - * this threshold is safe.  If a large delta occurs, trigger a recalibration. + * see if new value is within 20% of half of old value   */ -static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) +static int approx_half(int curr, int prev) +{ +	int belowhalf, abovehalf; + +	if (curr < 5 || prev < 5) +		return 0; + +	belowhalf = (prev * 8) / 20; +	abovehalf = (prev * 12) / 20; + +	return belowhalf < curr && curr <= abovehalf; +} + +/* + * Throw out oddly large delta packets, and any that immediately follow whose + * values are each approximately half of the previous.  It seems that the ALPS + * firmware emits errant packets, and they get averaged out slowly. + */ +static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)  {  	struct hgpk_data *priv = psmouse->private; +	int avx, avy; +	bool do_recal = false; + +	avx = abs(x); +	avy = abs(y); + +	/* discard if too big, or half that but > 4 times the prev delta */ +	if (avx > recalib_delta || +		(avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) { +		psmouse_warn(psmouse, "detected %dpx jump in x\n", x); +		priv->xbigj = avx; +	} else if (approx_half(avx, priv->xbigj)) { +		psmouse_warn(psmouse, "detected secondary %dpx jump in x\n", x); +		priv->xbigj = avx; +		priv->xsaw_secondary++; +	} else { +		if (priv->xbigj && priv->xsaw_secondary > 1) +			do_recal = true; +		priv->xbigj = 0; +		priv->xsaw_secondary = 0; +	} -	if (abs(x) > recalib_delta || abs(y) > recalib_delta) { -		hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", -				recalib_delta, x, y); -		/* My car gets forty rods to the hogshead and that's the -		 * way I likes it! */ +	if (avy > recalib_delta || +		(avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) { +		psmouse_warn(psmouse, "detected %dpx jump in y\n", y); +		priv->ybigj = avy; +	} else if (approx_half(avy, priv->ybigj)) { +		psmouse_warn(psmouse, "detected secondary %dpx jump in y\n", y); +		priv->ybigj = avy; +		priv->ysaw_secondary++; +	} else { +		if (priv->ybigj && priv->ysaw_secondary > 1) +			do_recal = true; +		priv->ybigj = 0; +		priv->ysaw_secondary = 0; +	} + +	priv->xlast = avx; +	priv->ylast = avy; + +	if (do_recal && jumpy_delay) { +		psmouse_warn(psmouse, "scheduling recalibration\n");  		psmouse_queue_work(psmouse, &priv->recalib_wq,  				msecs_to_jiffies(jumpy_delay));  	} + +	return priv->xbigj || priv->ybigj; +} + +static void hgpk_reset_spew_detection(struct hgpk_data *priv) +{ +	priv->spew_count = 0; +	priv->dupe_count = 0; +	priv->x_tally = 0; +	priv->y_tally = 0; +	priv->spew_flag = NO_SPEW; +} + +static void hgpk_reset_hack_state(struct psmouse *psmouse) +{ +	struct hgpk_data *priv = psmouse->private; + +	priv->abs_x = priv->abs_y = -1; +	priv->xlast = priv->ylast = ILLEGAL_XY; +	priv->xbigj = priv->ybigj = 0; +	priv->xsaw_secondary = priv->ysaw_secondary = 0; +	hgpk_reset_spew_detection(priv);  }  /* @@ -116,20 +221,57 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,  	if (l || r)  		return; +	/* don't track spew if the workaround feature has been turned off */ +	if (!spew_delay) +		return; + +	if (abs(x) > 3 || abs(y) > 3) { +		/* no spew, or spew ended */ +		hgpk_reset_spew_detection(priv); +		return; +	} + +	/* Keep a tally of the overall delta to the cursor position caused by +	 * the spew */  	priv->x_tally += x;  	priv->y_tally += y; -	if (++priv->count > 100) { +	switch (priv->spew_flag) { +	case NO_SPEW: +		/* we're not spewing, but this packet might be the start */ +		priv->spew_flag = MAYBE_SPEWING; + +		/* fall-through */ + +	case MAYBE_SPEWING: +		priv->spew_count++; + +		if (priv->spew_count < SPEW_WATCH_COUNT) +			break; + +		/* excessive spew detected, request recalibration */ +		priv->spew_flag = SPEW_DETECTED; + +		/* fall-through */ + +	case SPEW_DETECTED: +		/* only recalibrate when the overall delta to the cursor +		 * is really small. if the spew is causing significant cursor +		 * movement, it is probably a case of the user moving the +		 * cursor very slowly across the screen. */  		if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { -			hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n", -				 priv->x_tally, priv->y_tally); +			psmouse_warn(psmouse, "packet spew detected (%d,%d)\n", +				     priv->x_tally, priv->y_tally); +			priv->spew_flag = RECALIBRATING;  			psmouse_queue_work(psmouse, &priv->recalib_wq,  					   msecs_to_jiffies(spew_delay));  		} -		/* reset every 100 packets */ -		priv->count = 0; -		priv->x_tally = 0; -		priv->y_tally = 0; + +		break; +	case RECALIBRATING: +		/* we already detected a spew and requested a recalibration, +		 * just wait for the queue to kick into action. */ +		break;  	}  } @@ -143,29 +285,171 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,   * swr/swl are the left/right buttons.   * x-neg/y-neg are the x and y delta negative bits   * x-over/y-over are the x and y overflow bits + * + * --- + * + * HGPK Advanced Mode - single-mode format + * + * byte 0(PT):  1    1    0    0    1    1     1     1 + * byte 0(GS):  1    1    1    1    1    1     1     1 + * byte 1:      0   x6   x5   x4   x3   x2    x1    x0 + * byte 2(PT):  0    0   x9   x8   x7    ? pt-dsw    0 + * byte 2(GS):  0  x10   x9   x8   x7    ? gs-dsw pt-dsw + * byte 3:      0   y9   y8   y7    1    0   swr   swl + * byte 4:      0   y6   y5   y4   y3   y2    y1    y0 + * byte 5:      0   z6   z5   z4   z3   z2    z1    z0 + * + * ?'s are not defined in the protocol spec, may vary between models. + * + * swr/swl are the left/right buttons. + * + * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a + * pen/finger   */ -static int hgpk_validate_byte(unsigned char *packet) +static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet)  { -	return (packet[0] & 0x0C) != 0x08; +	struct hgpk_data *priv = psmouse->private; +	int pktcnt = psmouse->pktcnt; +	bool valid; + +	switch (priv->mode) { +	case HGPK_MODE_MOUSE: +		valid = (packet[0] & 0x0C) == 0x08; +		break; + +	case HGPK_MODE_GLIDESENSOR: +		valid = pktcnt == 1 ? +			packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80); +		break; + +	case HGPK_MODE_PENTABLET: +		valid = pktcnt == 1 ? +			packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80); +		break; + +	default: +		valid = false; +		break; +	} + +	if (!valid) +		psmouse_dbg(psmouse, +			    "bad data, mode %d (%d) %*ph\n", +			    priv->mode, pktcnt, 6, psmouse->packet); + +	return valid;  } -static void hgpk_process_packet(struct psmouse *psmouse) +static void hgpk_process_advanced_packet(struct psmouse *psmouse)  { -	struct input_dev *dev = psmouse->dev; +	struct hgpk_data *priv = psmouse->private; +	struct input_dev *idev = psmouse->dev;  	unsigned char *packet = psmouse->packet; -	int x, y, left, right; +	int down = !!(packet[2] & 2); +	int left = !!(packet[3] & 1); +	int right = !!(packet[3] & 2); +	int x = packet[1] | ((packet[2] & 0x78) << 4); +	int y = packet[4] | ((packet[3] & 0x70) << 3); + +	if (priv->mode == HGPK_MODE_GLIDESENSOR) { +		int pt_down = !!(packet[2] & 1); +		int finger_down = !!(packet[2] & 2); +		int z = packet[5]; + +		input_report_abs(idev, ABS_PRESSURE, z); +		if (tpdebug) +			psmouse_dbg(psmouse, "pd=%d fd=%d z=%d", +				    pt_down, finger_down, z); +	} else { +		/* +		 * PenTablet mode does not report pressure, so we don't +		 * report it here +		 */ +		if (tpdebug) +			psmouse_dbg(psmouse, "pd=%d ", down); +	} + +	if (tpdebug) +		psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", +			    left, right, x, y); + +	input_report_key(idev, BTN_TOUCH, down); +	input_report_key(idev, BTN_LEFT, left); +	input_report_key(idev, BTN_RIGHT, right); + +	/* +	 * If this packet says that the finger was removed, reset our position +	 * tracking so that we don't erroneously detect a jump on next press. +	 */ +	if (!down) { +		hgpk_reset_hack_state(psmouse); +		goto done; +	} -	left = packet[0] & 1; -	right = (packet[0] >> 1) & 1; +	/* +	 * Weed out duplicate packets (we get quite a few, and they mess up +	 * our jump detection) +	 */ +	if (x == priv->abs_x && y == priv->abs_y) { +		if (++priv->dupe_count > SPEW_WATCH_COUNT) { +			if (tpdebug) +				psmouse_dbg(psmouse, "hard spew detected\n"); +			priv->spew_flag = RECALIBRATING; +			psmouse_queue_work(psmouse, &priv->recalib_wq, +					   msecs_to_jiffies(spew_delay)); +		} +		goto done; +	} + +	/* not a duplicate, continue with position reporting */ +	priv->dupe_count = 0; + +	/* Don't apply hacks in PT mode, it seems reliable */ +	if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { +		int x_diff = priv->abs_x - x; +		int y_diff = priv->abs_y - y; +		if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) { +			if (tpdebug) +				psmouse_dbg(psmouse, "discarding\n"); +			goto done; +		} +		hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff); +	} -	x = packet[1] - ((packet[0] << 4) & 0x100); -	y = ((packet[0] << 3) & 0x100) - packet[2]; +	input_report_abs(idev, ABS_X, x); +	input_report_abs(idev, ABS_Y, y); +	priv->abs_x = x; +	priv->abs_y = y; + +done: +	input_sync(idev); +} + +static void hgpk_process_simple_packet(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	unsigned char *packet = psmouse->packet; +	int left = packet[0] & 1; +	int right = (packet[0] >> 1) & 1; +	int x = packet[1] - ((packet[0] << 4) & 0x100); +	int y = ((packet[0] << 3) & 0x100) - packet[2]; + +	if (packet[0] & 0xc0) +		psmouse_dbg(psmouse, +			    "overflow -- 0x%02x 0x%02x 0x%02x\n", +			    packet[0], packet[1], packet[2]); + +	if (hgpk_discard_decay_hack(psmouse, x, y)) { +		if (tpdebug) +			psmouse_dbg(psmouse, "discarding\n"); +		return; +	} -	hgpk_jumpy_hack(psmouse, x, y);  	hgpk_spewing_hack(psmouse, left, right, x, y);  	if (tpdebug) -		hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); +		psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", +			    left, right, x, y);  	input_report_key(dev, BTN_LEFT, left);  	input_report_key(dev, BTN_RIGHT, right); @@ -180,15 +464,14 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)  {  	struct hgpk_data *priv = psmouse->private; -	if (hgpk_validate_byte(psmouse->packet)) { -		hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n", -				__func__, psmouse->pktcnt, psmouse->packet[0], -				psmouse->packet[1], psmouse->packet[2]); +	if (!hgpk_is_byte_valid(psmouse, psmouse->packet))  		return PSMOUSE_BAD_DATA; -	}  	if (psmouse->pktcnt >= psmouse->pktsize) { -		hgpk_process_packet(psmouse); +		if (priv->mode == HGPK_MODE_MOUSE) +			hgpk_process_simple_packet(psmouse); +		else +			hgpk_process_advanced_packet(psmouse);  		return PSMOUSE_FULL_PACKET;  	} @@ -198,9 +481,8 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)  			 * ugh, got a packet inside our recalibration  			 * window, schedule another recalibration.  			 */ -			hgpk_dbg(psmouse, -				 "packet inside calibration window, " -				 "queueing another recalibration\n"); +			psmouse_dbg(psmouse, +				    "packet inside calibration window, queueing another recalibration\n");  			psmouse_queue_work(psmouse, &priv->recalib_wq,  					msecs_to_jiffies(post_interrupt_delay));  		} @@ -210,62 +492,211 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)  	return PSMOUSE_GOOD_DATA;  } -static int hgpk_force_recalibrate(struct psmouse *psmouse) +static int hgpk_select_mode(struct psmouse *psmouse)  {  	struct ps2dev *ps2dev = &psmouse->ps2dev;  	struct hgpk_data *priv = psmouse->private; +	int i; +	int cmd; + +	/* +	 * 4 disables to enable advanced mode +	 * then 3 0xf2 bytes as the preamble for GS/PT selection +	 */ +	const int advanced_init[] = { +		PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, +		PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, +		0xf2, 0xf2, 0xf2, +	}; + +	switch (priv->mode) { +	case HGPK_MODE_MOUSE: +		psmouse->pktsize = 3; +		break; + +	case HGPK_MODE_GLIDESENSOR: +	case HGPK_MODE_PENTABLET: +		psmouse->pktsize = 6; + +		/* Switch to 'Advanced mode.', four disables in a row. */ +		for (i = 0; i < ARRAY_SIZE(advanced_init); i++) +			if (ps2_command(ps2dev, NULL, advanced_init[i])) +				return -EIO; + +		/* select between GlideSensor (mouse) or PenTablet */ +		cmd = priv->mode == HGPK_MODE_GLIDESENSOR ? +			PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21; + +		if (ps2_command(ps2dev, NULL, cmd)) +			return -EIO; +		break; + +	default: +		return -EINVAL; +	} + +	return 0; +} + +static void hgpk_setup_input_device(struct input_dev *input, +				    struct input_dev *old_input, +				    enum hgpk_mode mode) +{ +	if (old_input) { +		input->name = old_input->name; +		input->phys = old_input->phys; +		input->id = old_input->id; +		input->dev.parent = old_input->dev.parent; +	} + +	memset(input->evbit, 0, sizeof(input->evbit)); +	memset(input->relbit, 0, sizeof(input->relbit)); +	memset(input->keybit, 0, sizeof(input->keybit)); + +	/* All modes report left and right buttons */ +	__set_bit(EV_KEY, input->evbit); +	__set_bit(BTN_LEFT, input->keybit); +	__set_bit(BTN_RIGHT, input->keybit); + +	switch (mode) { +	case HGPK_MODE_MOUSE: +		__set_bit(EV_REL, input->evbit); +		__set_bit(REL_X, input->relbit); +		__set_bit(REL_Y, input->relbit); +		break; + +	case HGPK_MODE_GLIDESENSOR: +		__set_bit(BTN_TOUCH, input->keybit); +		__set_bit(BTN_TOOL_FINGER, input->keybit); + +		__set_bit(EV_ABS, input->evbit); + +		/* GlideSensor has pressure sensor, PenTablet does not */ +		input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0); + +		/* From device specs */ +		input_set_abs_params(input, ABS_X, 0, 399, 0, 0); +		input_set_abs_params(input, ABS_Y, 0, 290, 0, 0); + +		/* Calculated by hand based on usable size (52mm x 38mm) */ +		input_abs_set_res(input, ABS_X, 8); +		input_abs_set_res(input, ABS_Y, 8); +		break; + +	case HGPK_MODE_PENTABLET: +		__set_bit(BTN_TOUCH, input->keybit); +		__set_bit(BTN_TOOL_FINGER, input->keybit); + +		__set_bit(EV_ABS, input->evbit); + +		/* From device specs */ +		input_set_abs_params(input, ABS_X, 0, 999, 0, 0); +		input_set_abs_params(input, ABS_Y, 5, 239, 0, 0); + +		/* Calculated by hand based on usable size (156mm x 38mm) */ +		input_abs_set_res(input, ABS_X, 6); +		input_abs_set_res(input, ABS_Y, 8); +		break; + +	default: +		BUG(); +	} +} + +static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) +{ +	int err; + +	psmouse_reset(psmouse); + +	if (recalibrate) { +		struct ps2dev *ps2dev = &psmouse->ps2dev; + +		/* send the recalibrate request */ +		if (ps2_command(ps2dev, NULL, 0xf5) || +		    ps2_command(ps2dev, NULL, 0xf5) || +		    ps2_command(ps2dev, NULL, 0xe6) || +		    ps2_command(ps2dev, NULL, 0xf5)) { +			return -1; +		} + +		/* according to ALPS, 150mS is required for recalibration */ +		msleep(150); +	} + +	err = hgpk_select_mode(psmouse); +	if (err) { +		psmouse_err(psmouse, "failed to select mode\n"); +		return err; +	} + +	hgpk_reset_hack_state(psmouse); + +	return 0; +} + +static int hgpk_force_recalibrate(struct psmouse *psmouse) +{ +	struct hgpk_data *priv = psmouse->private; +	int err;  	/* C-series touchpads added the recalibrate command */  	if (psmouse->model < HGPK_MODEL_C)  		return 0; +	if (!autorecal) { +		psmouse_dbg(psmouse, "recalibration disabled, ignoring\n"); +		return 0; +	} + +	psmouse_dbg(psmouse, "recalibrating touchpad..\n"); +  	/* we don't want to race with the irq handler, nor with resyncs */  	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);  	/* start by resetting the device */ -	psmouse_reset(psmouse); - -	/* send the recalibrate request */ -	if (ps2_command(ps2dev, NULL, 0xf5) || -	    ps2_command(ps2dev, NULL, 0xf5) || -	    ps2_command(ps2dev, NULL, 0xe6) || -	    ps2_command(ps2dev, NULL, 0xf5)) { -		return -1; -	} - -	/* according to ALPS, 150mS is required for recalibration */ -	msleep(150); +	err = hgpk_reset_device(psmouse, true); +	if (err) +		return err; -	/* XXX: If a finger is down during this delay, recalibration will +	/* +	 * XXX: If a finger is down during this delay, recalibration will  	 * detect capacitance incorrectly.  This is a hardware bug, and  	 * we don't have a good way to deal with it.  The 2s window stuff  	 * (below) is our best option for now.  	 */ - -	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) +	if (psmouse_activate(psmouse))  		return -1; -	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); +	if (tpdebug) +		psmouse_dbg(psmouse, "touchpad reactivated\n"); -	/* After we recalibrate, we shouldn't get any packets for 2s.  If -	 * we do, it's likely that someone's finger was on the touchpad. -	 * If someone's finger *was* on the touchpad, it's probably -	 * miscalibrated.  So, we should schedule another recalibration +	/* +	 * If we get packets right away after recalibrating, it's likely +	 * that a finger was on the touchpad.  If so, it's probably +	 * miscalibrated, so we optionally schedule another.  	 */ -	priv->recalib_window = jiffies +  msecs_to_jiffies(recal_guard_time); +	if (recal_guard_time) +		priv->recalib_window = jiffies + +			msecs_to_jiffies(recal_guard_time);  	return 0;  }  /* - * This kills power to the touchpad; according to ALPS, current consumption - * goes down to 50uA after running this.  To turn power back on, we drive - * MS-DAT low. + * This puts the touchpad in a power saving mode; according to ALPS, current + * consumption goes down to 50uA after running this.  To turn power back on, + * we drive MS-DAT low.  Measuring with a 1mA resolution ammeter says that + * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this. + * + * We have no formal spec that details this operation -- the low-power + * sequence came from a long-lost email trail.   */ -static int hgpk_toggle_power(struct psmouse *psmouse, int enable) +static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable)  {  	struct ps2dev *ps2dev = &psmouse->ps2dev;  	int timeo; +	int err;  	/* Added on D-series touchpads */  	if (psmouse->model < HGPK_MODEL_D) @@ -279,24 +710,26 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable)  		 * the controller.  Once we get an ACK back from it, it  		 * means we can continue with the touchpad re-init.  ALPS  		 * tells us that 1s should be long enough, so set that as -		 * the upper bound. +		 * the upper bound. (in practice, it takes about 3 loops.)  		 */  		for (timeo = 20; timeo > 0; timeo--) {  			if (!ps2_sendbyte(&psmouse->ps2dev,  					PSMOUSE_CMD_DISABLE, 20))  				break; -			msleep(50); +			msleep(25);  		} -		psmouse_reset(psmouse); +		err = hgpk_reset_device(psmouse, false); +		if (err) { +			psmouse_err(psmouse, "Failed to reset device!\n"); +			return err; +		}  		/* should be all set, enable the touchpad */ -		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); -		psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - +		psmouse_activate(psmouse); +		psmouse_dbg(psmouse, "Touchpad powered up.\n");  	} else { -		hgpk_dbg(psmouse, "Powering off touchpad.\n"); -		psmouse_set_state(psmouse, PSMOUSE_IGNORE); +		psmouse_dbg(psmouse, "Powering off touchpad.\n");  		if (ps2_command(ps2dev, NULL, 0xec) ||  		    ps2_command(ps2dev, NULL, 0xec) || @@ -304,6 +737,8 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable)  			return -1;  		} +		psmouse_set_state(psmouse, PSMOUSE_IGNORE); +  		/* probably won't see an ACK, the touchpad will be off */  		ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);  	} @@ -319,17 +754,20 @@ static int hgpk_poll(struct psmouse *psmouse)  static int hgpk_reconnect(struct psmouse *psmouse)  { -	/* During suspend/resume the ps2 rails remain powered.  We don't want +	struct hgpk_data *priv = psmouse->private; + +	/* +	 * During suspend/resume the ps2 rails remain powered.  We don't want  	 * to do a reset because it's flush data out of buffers; however, -	 * earlier prototypes (B1) had some brokenness that required a reset. */ +	 * earlier prototypes (B1) had some brokenness that required a reset. +	 */  	if (olpc_board_at_least(olpc_board(0xb2)))  		if (psmouse->ps2dev.serio->dev.power.power_state.event !=  				PM_EVENT_ON)  			return 0; -	psmouse_reset(psmouse); - -	return 0; +	priv->powered = 1; +	return hgpk_reset_device(psmouse, false);  }  static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) @@ -343,11 +781,14 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,  				const char *buf, size_t count)  {  	struct hgpk_data *priv = psmouse->private; -	unsigned long value; +	unsigned int value;  	int err; -	err = strict_strtoul(buf, 10, &value); -	if (err || value > 1) +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err; + +	if (value > 1)  		return -EINVAL;  	if (value != priv->powered) { @@ -355,7 +796,7 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,  		 * hgpk_toggle_power will deal w/ state so  		 * we're not racing w/ irq  		 */ -		err = hgpk_toggle_power(psmouse, value); +		err = hgpk_toggle_powersave(psmouse, value);  		if (!err)  			priv->powered = value;  	} @@ -366,6 +807,65 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,  __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,  		      hgpk_show_powered, hgpk_set_powered, false); +static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf) +{ +	struct hgpk_data *priv = psmouse->private; + +	return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]); +} + +static ssize_t attr_set_mode(struct psmouse *psmouse, void *data, +			     const char *buf, size_t len) +{ +	struct hgpk_data *priv = psmouse->private; +	enum hgpk_mode old_mode = priv->mode; +	enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len); +	struct input_dev *old_dev = psmouse->dev; +	struct input_dev *new_dev; +	int err; + +	if (new_mode == HGPK_MODE_INVALID) +		return -EINVAL; + +	if (old_mode == new_mode) +		return len; + +	new_dev = input_allocate_device(); +	if (!new_dev) +		return -ENOMEM; + +	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + +	/* Switch device into the new mode */ +	priv->mode = new_mode; +	err = hgpk_reset_device(psmouse, false); +	if (err) +		goto err_try_restore; + +	hgpk_setup_input_device(new_dev, old_dev, new_mode); + +	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + +	err = input_register_device(new_dev); +	if (err) +		goto err_try_restore; + +	psmouse->dev = new_dev; +	input_unregister_device(old_dev); + +	return len; + +err_try_restore: +	input_free_device(new_dev); +	priv->mode = old_mode; +	hgpk_reset_device(psmouse, false); + +	return err; +} + +PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL, +		    attr_show_mode, attr_set_mode); +  static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,  		void *data, char *buf)  { @@ -376,11 +876,14 @@ static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,  				const char *buf, size_t count)  {  	struct hgpk_data *priv = psmouse->private; -	unsigned long value; +	unsigned int value;  	int err; -	err = strict_strtoul(buf, 10, &value); -	if (err || value != 1) +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err; + +	if (value != 1)  		return -EINVAL;  	/* @@ -401,6 +904,8 @@ static void hgpk_disconnect(struct psmouse *psmouse)  	device_remove_file(&psmouse->ps2dev.serio->dev,  			   &psmouse_attr_powered.dattr); +	device_remove_file(&psmouse->ps2dev.serio->dev, +			   &psmouse_attr_hgpk_mode.dattr);  	if (psmouse->model >= HGPK_MODEL_C)  		device_remove_file(&psmouse->ps2dev.serio->dev, @@ -416,14 +921,13 @@ static void hgpk_recalib_work(struct work_struct *work)  	struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);  	struct psmouse *psmouse = priv->psmouse; -	hgpk_dbg(psmouse, "recalibrating touchpad..\n"); -  	if (hgpk_force_recalibrate(psmouse)) -		hgpk_err(psmouse, "recalibration failed!\n"); +		psmouse_err(psmouse, "recalibration failed!\n");  }  static int hgpk_register(struct psmouse *psmouse)  { +	struct hgpk_data *priv = psmouse->private;  	int err;  	/* register handlers */ @@ -431,51 +935,70 @@ static int hgpk_register(struct psmouse *psmouse)  	psmouse->poll = hgpk_poll;  	psmouse->disconnect = hgpk_disconnect;  	psmouse->reconnect = hgpk_reconnect; -	psmouse->pktsize = 3;  	/* Disable the idle resync. */  	psmouse->resync_time = 0;  	/* Reset after a lot of bad bytes. */  	psmouse->resetafter = 1024; +	hgpk_setup_input_device(psmouse->dev, NULL, priv->mode); +  	err = device_create_file(&psmouse->ps2dev.serio->dev,  				 &psmouse_attr_powered.dattr);  	if (err) { -		hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n"); +		psmouse_err(psmouse, "Failed creating 'powered' sysfs node\n");  		return err;  	} +	err = device_create_file(&psmouse->ps2dev.serio->dev, +				 &psmouse_attr_hgpk_mode.dattr); +	if (err) { +		psmouse_err(psmouse, +			    "Failed creating 'hgpk_mode' sysfs node\n"); +		goto err_remove_powered; +	} +  	/* C-series touchpads added the recalibrate command */  	if (psmouse->model >= HGPK_MODEL_C) {  		err = device_create_file(&psmouse->ps2dev.serio->dev,  					 &psmouse_attr_recalibrate.dattr);  		if (err) { -			hgpk_err(psmouse, -				"Failed creating 'recalibrate' sysfs node\n"); -			device_remove_file(&psmouse->ps2dev.serio->dev, -					&psmouse_attr_powered.dattr); -			return err; +			psmouse_err(psmouse, +				    "Failed creating 'recalibrate' sysfs node\n"); +			goto err_remove_mode;  		}  	}  	return 0; + +err_remove_mode: +	device_remove_file(&psmouse->ps2dev.serio->dev, +			   &psmouse_attr_hgpk_mode.dattr); +err_remove_powered: +	device_remove_file(&psmouse->ps2dev.serio->dev, +			   &psmouse_attr_powered.dattr); +	return err;  }  int hgpk_init(struct psmouse *psmouse)  {  	struct hgpk_data *priv; -	int err = -ENOMEM; +	int err;  	priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); -	if (!priv) +	if (!priv) { +		err = -ENOMEM;  		goto alloc_fail; +	}  	psmouse->private = priv; +  	priv->psmouse = psmouse;  	priv->powered = true; +	priv->mode = hgpk_default_mode;  	INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); -	err = psmouse_reset(psmouse); +	err = hgpk_reset_device(psmouse, false);  	if (err)  		goto init_fail; @@ -504,13 +1027,13 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)  		return -EIO;  	} -	hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]); +	psmouse_dbg(psmouse, "ID: %*ph\n", 3, param);  	/* HGPK signature: 0x67, 0x00, 0x<model> */  	if (param[0] != 0x67 || param[1] != 0x00)  		return -ENODEV; -	hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); +	psmouse_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);  	return param[2];  } @@ -531,3 +1054,14 @@ int hgpk_detect(struct psmouse *psmouse, bool set_properties)  	return 0;  } + +void hgpk_module_init(void) +{ +	hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name, +						strlen(hgpk_mode_name)); +	if (hgpk_default_mode == HGPK_MODE_INVALID) { +		hgpk_default_mode = HGPK_MODE_MOUSE; +		strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], +			sizeof(hgpk_mode_name)); +	} +} diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h index d61cfd3ee9c..dd686771cfe 100644 --- a/drivers/input/mouse/hgpk.h +++ b/drivers/input/mouse/hgpk.h @@ -5,6 +5,9 @@  #ifndef _HGPK_H  #define _HGPK_H +#define HGPK_GS		0xff       /* The GlideSensor */ +#define HGPK_PT		0xcf       /* The PenTablet */ +  enum hgpk_model_t {  	HGPK_MODEL_PREA = 0x0a,	/* pre-B1s */  	HGPK_MODEL_A = 0x14,	/* found on B1s, PT disabled in hardware */ @@ -13,29 +16,44 @@ enum hgpk_model_t {  	HGPK_MODEL_D = 0x50,	/* C1, mass production */  }; +enum hgpk_spew_flag { +	NO_SPEW, +	MAYBE_SPEWING, +	SPEW_DETECTED, +	RECALIBRATING, +}; + +#define SPEW_WATCH_COUNT 42  /* at 12ms/packet, this is 1/2 second */ + +enum hgpk_mode { +	HGPK_MODE_MOUSE, +	HGPK_MODE_GLIDESENSOR, +	HGPK_MODE_PENTABLET, +	HGPK_MODE_INVALID +}; +  struct hgpk_data {  	struct psmouse *psmouse; +	enum hgpk_mode mode;  	bool powered; -	int count, x_tally, y_tally;	/* hardware workaround stuff */ +	enum hgpk_spew_flag spew_flag; +	int spew_count, x_tally, y_tally;	/* spew detection */  	unsigned long recalib_window;  	struct delayed_work recalib_wq; +	int abs_x, abs_y; +	int dupe_count; +	int xbigj, ybigj, xlast, ylast; /* jumpiness detection */ +	int xsaw_secondary, ysaw_secondary; /* jumpiness detection */  }; -#define hgpk_dbg(psmouse, format, arg...)		\ -	dev_dbg(&(psmouse)->ps2dev.serio->dev, format, ## arg) -#define hgpk_err(psmouse, format, arg...)		\ -	dev_err(&(psmouse)->ps2dev.serio->dev, format, ## arg) -#define hgpk_info(psmouse, format, arg...)		\ -	dev_info(&(psmouse)->ps2dev.serio->dev, format, ## arg) -#define hgpk_warn(psmouse, format, arg...)		\ -	dev_warn(&(psmouse)->ps2dev.serio->dev, format, ## arg) -#define hgpk_notice(psmouse, format, arg...)		\ -	dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg) -  #ifdef CONFIG_MOUSE_PS2_OLPC +void hgpk_module_init(void);  int hgpk_detect(struct psmouse *psmouse, bool set_properties);  int hgpk_init(struct psmouse *psmouse);  #else +static inline void hgpk_module_init(void) +{ +}  static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties)  {  	return -ENODEV; diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index c31ad11df6b..23222dd5a66 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -33,7 +33,7 @@ static const char *desired_serio_phys;  static int lifebook_limit_serio3(const struct dmi_system_id *d)  {  	desired_serio_phys = "isa0060/serio3"; -	return 0; +	return 1;  }  static bool lifebook_use_6byte_proto; @@ -41,10 +41,10 @@ static bool lifebook_use_6byte_proto;  static int lifebook_set_6byte_proto(const struct dmi_system_id *d)  {  	lifebook_use_6byte_proto = true; -	return 0; +	return 1;  } -static const struct dmi_system_id __initconst lifebook_dmi_table[] = { +static const struct dmi_system_id lifebook_dmi_table[] __initconst = {  	{  		/* FLORA-ie 55mi */  		.matches = { @@ -169,8 +169,8 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)  	if (relative_packet) {  		if (!dev2) -			printk(KERN_WARNING "lifebook.c: got relative packet " -				"but no relative device set up\n"); +			psmouse_warn(psmouse, +				     "got relative packet but no relative device set up\n");  	} else {  		if (lifebook_use_6byte_proto) {  			input_report_abs(dev1, ABS_X, @@ -212,7 +212,7 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)  	/*  	 * Enable absolute output -- ps2_command fails always but if -	 * you leave this call out the touchsreen will never send +	 * you leave this call out the touchscreen will never send  	 * absolute coordinates  	 */  	param = lifebook_use_6byte_proto ? 0x08 : 0x07; diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index c9983aee908..136e222e2a1 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -82,11 +82,11 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)  			packet[0] = packet[2] | 0x08;  			break; -#ifdef DEBUG  		default: -			printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", -				(packet[1] >> 4) | (packet[0] & 0x30)); -#endif +			psmouse_dbg(psmouse, +				    "Received PS2++ packet #%x, but don't know how to handle.\n", +				    (packet[1] >> 4) | (packet[0] & 0x30)); +			break;  		}  	} else {  		/* Standard PS/2 motion data */ @@ -155,9 +155,14 @@ static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse,  static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,  					  const char *buf, size_t count)  { -	unsigned long value; +	unsigned int value; +	int err; + +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err; -	if (strict_strtoul(buf, 10, &value) || value > 1) +	if (value > 1)  		return -EINVAL;  	ps2pp_set_smartscroll(psmouse, value); @@ -215,7 +220,7 @@ static const struct ps2pp_info *get_model_info(unsigned char model)  		{ 61,	PS2PP_KIND_MX,					/* MX700 */  				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |  				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, -		{ 66,	PS2PP_KIND_MX,					/* MX3100 reciver */ +		{ 66,	PS2PP_KIND_MX,					/* MX3100 receiver */  				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |  				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },  		{ 72,	PS2PP_KIND_TRACKMAN,	0 },			/* T-CH11: TrackMan Marble */ @@ -382,7 +387,7 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)  		}  	} else { -		printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model); +		psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model);  	}  	if (set_properties) { @@ -400,9 +405,9 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)  				error = device_create_file(&psmouse->ps2dev.serio->dev,  							   &psmouse_attr_smartscroll.dattr);  				if (error) { -					printk(KERN_ERR -						"logips2pp.c: failed to create smartscroll " -						"sysfs attribute, error: %d\n", error); +					psmouse_err(psmouse, +						    "failed to create smartscroll sysfs attribute, error: %d\n", +						    error);  					return -1;  				}  			} diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index 5f278176eb9..0a60717b91c 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -64,7 +64,7 @@ static void dc_mouse_close(struct input_dev *dev)  }  /* allow the mouse to be used */ -static int __devinit probe_maple_mouse(struct device *dev) +static int probe_maple_mouse(struct device *dev)  {  	struct maple_device *mdev = to_maple_dev(dev);  	struct maple_driver *mdrv = to_maple_driver(dev->driver); @@ -114,7 +114,7 @@ fail:  	return error;  } -static int __devexit remove_maple_mouse(struct device *dev) +static int remove_maple_mouse(struct device *dev)  {  	struct maple_device *mdev = to_maple_dev(dev);  	struct dc_mouse *mse = maple_get_drvdata(mdev); @@ -132,7 +132,7 @@ static struct maple_driver dc_mouse_driver = {  	.drv = {  		.name = "Dreamcast_mouse",  		.probe = probe_maple_mouse, -		.remove = __devexit_p(remove_maple_mouse), +		.remove = remove_maple_mouse,  	},  }; diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c new file mode 100644 index 00000000000..1ccc88af1f0 --- /dev/null +++ b/drivers/input/mouse/navpoint.c @@ -0,0 +1,368 @@ +/* + * Synaptics NavPoint (PXA27x SSP/SPI) driver. + * + * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.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/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/input/navpoint.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/pxa2xx_ssp.h> +#include <linux/slab.h> + +/* + * Synaptics Modular Embedded Protocol: Module Packet Format. + * Module header byte 2:0 = Length (# bytes that follow) + * Module header byte 4:3 = Control + * Module header byte 7:5 = Module Address + */ +#define HEADER_LENGTH(byte)	((byte) & 0x07) +#define HEADER_CONTROL(byte)	(((byte) >> 3) & 0x03) +#define HEADER_ADDRESS(byte)	((byte) >> 5) + +struct navpoint { +	struct ssp_device	*ssp; +	struct input_dev	*input; +	struct device		*dev; +	int			gpio; +	int			index; +	u8			data[1 + HEADER_LENGTH(0xff)]; +}; + +/* + * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. + */ +static const u32 sscr0 = 0 +	| SSCR0_TUM		/* TIM = 1; No TUR interrupts */ +	| SSCR0_RIM		/* RIM = 1; No ROR interrupts */ +	| SSCR0_SSE		/* SSE = 1; SSP enabled */ +	| SSCR0_Motorola	/* FRF = 0; Motorola SPI */ +	| SSCR0_DataSize(16)	/* DSS = 15; Data size = 16-bit */ +	; +static const u32 sscr1 = 0 +	| SSCR1_SCFR		/* SCFR = 1; SSPSCLK only during transfers */ +	| SSCR1_SCLKDIR		/* SCLKDIR = 1; Slave mode */ +	| SSCR1_SFRMDIR		/* SFRMDIR = 1; Slave mode */ +	| SSCR1_RWOT		/* RWOT = 1; Receive without transmit mode */ +	| SSCR1_RxTresh(1)	/* RFT = 0; Receive FIFO threshold = 1 */ +	| SSCR1_SPH		/* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ +	| SSCR1_RIE		/* RIE = 1; Receive FIFO interrupt enabled */ +	; +static const u32 sssr = 0 +	| SSSR_BCE		/* BCE = 1; Clear BCE */ +	| SSSR_TUR		/* TUR = 1; Clear TUR */ +	| SSSR_EOC		/* EOC = 1; Clear EOC */ +	| SSSR_TINT		/* TINT = 1; Clear TINT */ +	| SSSR_PINT		/* PINT = 1; Clear PINT */ +	| SSSR_ROR		/* ROR = 1; Clear ROR */ +	; + +/* + * MEP Query $22: Touchpad Coordinate Range Query is not supported by + * the NavPoint module, so sampled values provide the default limits. + */ +#define NAVPOINT_X_MIN		1278 +#define NAVPOINT_X_MAX		5340 +#define NAVPOINT_Y_MIN		1572 +#define NAVPOINT_Y_MAX		4396 +#define NAVPOINT_PRESSURE_MIN	0 +#define NAVPOINT_PRESSURE_MAX	255 + +static void navpoint_packet(struct navpoint *navpoint) +{ +	int finger; +	int gesture; +	int x, y, z; + +	switch (navpoint->data[0]) { +	case 0xff:	/* Garbage (packet?) between reset and Hello packet */ +	case 0x00:	/* Module 0, NULL packet */ +		break; + +	case 0x0e:	/* Module 0, Absolute packet */ +		finger = (navpoint->data[1] & 0x01); +		gesture = (navpoint->data[1] & 0x02); +		x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; +		y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; +		z = navpoint->data[6]; +		input_report_key(navpoint->input, BTN_TOUCH, finger); +		input_report_abs(navpoint->input, ABS_X, x); +		input_report_abs(navpoint->input, ABS_Y, y); +		input_report_abs(navpoint->input, ABS_PRESSURE, z); +		input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); +		input_report_key(navpoint->input, BTN_LEFT, gesture); +		input_sync(navpoint->input); +		break; + +	case 0x19:	/* Module 0, Hello packet */ +		if ((navpoint->data[1] & 0xf0) == 0x10) +			break; +		/* FALLTHROUGH */ +	default: +		dev_warn(navpoint->dev, +			 "spurious packet: data=0x%02x,0x%02x,...\n", +			 navpoint->data[0], navpoint->data[1]); +		break; +	} +} + +static irqreturn_t navpoint_irq(int irq, void *dev_id) +{ +	struct navpoint *navpoint = dev_id; +	struct ssp_device *ssp = navpoint->ssp; +	irqreturn_t ret = IRQ_NONE; +	u32 status; + +	status = pxa_ssp_read_reg(ssp, SSSR); +	if (status & sssr) { +		dev_warn(navpoint->dev, +			 "unexpected interrupt: status=0x%08x\n", status); +		pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); +		ret = IRQ_HANDLED; +	} + +	while (status & SSSR_RNE) { +		u32 data; + +		data = pxa_ssp_read_reg(ssp, SSDR); +		navpoint->data[navpoint->index + 0] = (data >> 8); +		navpoint->data[navpoint->index + 1] = data; +		navpoint->index += 2; +		if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { +			navpoint_packet(navpoint); +			navpoint->index = 0; +		} +		status = pxa_ssp_read_reg(ssp, SSSR); +		ret = IRQ_HANDLED; +	} + +	return ret; +} + +static void navpoint_up(struct navpoint *navpoint) +{ +	struct ssp_device *ssp = navpoint->ssp; +	int timeout; + +	clk_prepare_enable(ssp->clk); + +	pxa_ssp_write_reg(ssp, SSCR1, sscr1); +	pxa_ssp_write_reg(ssp, SSSR, sssr); +	pxa_ssp_write_reg(ssp, SSTO, 0); +	pxa_ssp_write_reg(ssp, SSCR0, sscr0);	/* SSCR0_SSE written last */ + +	/* Wait until SSP port is ready for slave clock operations */ +	for (timeout = 100; timeout != 0; --timeout) { +		if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) +			break; +		msleep(1); +	} + +	if (timeout == 0) +		dev_err(navpoint->dev, +			"timeout waiting for SSSR[CSS] to clear\n"); + +	if (gpio_is_valid(navpoint->gpio)) +		gpio_set_value(navpoint->gpio, 1); +} + +static void navpoint_down(struct navpoint *navpoint) +{ +	struct ssp_device *ssp = navpoint->ssp; + +	if (gpio_is_valid(navpoint->gpio)) +		gpio_set_value(navpoint->gpio, 0); + +	pxa_ssp_write_reg(ssp, SSCR0, 0); + +	clk_disable_unprepare(ssp->clk); +} + +static int navpoint_open(struct input_dev *input) +{ +	struct navpoint *navpoint = input_get_drvdata(input); + +	navpoint_up(navpoint); + +	return 0; +} + +static void navpoint_close(struct input_dev *input) +{ +	struct navpoint *navpoint = input_get_drvdata(input); + +	navpoint_down(navpoint); +} + +static int navpoint_probe(struct platform_device *pdev) +{ +	const struct navpoint_platform_data *pdata = +					dev_get_platdata(&pdev->dev); +	struct ssp_device *ssp; +	struct input_dev *input; +	struct navpoint *navpoint; +	int error; + +	if (!pdata) { +		dev_err(&pdev->dev, "no platform data\n"); +		return -EINVAL; +	} + +	if (gpio_is_valid(pdata->gpio)) { +		error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, +					 "SYNAPTICS_ON"); +		if (error) +			return error; +	} + +	ssp = pxa_ssp_request(pdata->port, pdev->name); +	if (!ssp) { +		error = -ENODEV; +		goto err_free_gpio; +	} + +	/* HaRET does not disable devices before jumping into Linux */ +	if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { +		pxa_ssp_write_reg(ssp, SSCR0, 0); +		dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); +	} + +	navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); +	input = input_allocate_device(); +	if (!navpoint || !input) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	navpoint->ssp = ssp; +	navpoint->input = input; +	navpoint->dev = &pdev->dev; +	navpoint->gpio = pdata->gpio; + +	input->name = pdev->name; +	input->dev.parent = &pdev->dev; + +	__set_bit(EV_KEY, input->evbit); +	__set_bit(EV_ABS, input->evbit); +	__set_bit(BTN_LEFT, input->keybit); +	__set_bit(BTN_TOUCH, input->keybit); +	__set_bit(BTN_TOOL_FINGER, input->keybit); + +	input_set_abs_params(input, ABS_X, +			     NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); +	input_set_abs_params(input, ABS_Y, +			     NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); +	input_set_abs_params(input, ABS_PRESSURE, +			     NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, +			     0, 0); + +	input->open = navpoint_open; +	input->close = navpoint_close; + +	input_set_drvdata(input, navpoint); + +	error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); +	if (error) +		goto err_free_mem; + +	error = input_register_device(input); +	if (error) +		goto err_free_irq; + +	platform_set_drvdata(pdev, navpoint); +	dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); + +	return 0; + +err_free_irq: +	free_irq(ssp->irq, navpoint); +err_free_mem: +	input_free_device(input); +	kfree(navpoint); +	pxa_ssp_free(ssp); +err_free_gpio: +	if (gpio_is_valid(pdata->gpio)) +		gpio_free(pdata->gpio); + +	return error; +} + +static int navpoint_remove(struct platform_device *pdev) +{ +	const struct navpoint_platform_data *pdata = +					dev_get_platdata(&pdev->dev); +	struct navpoint *navpoint = platform_get_drvdata(pdev); +	struct ssp_device *ssp = navpoint->ssp; + +	free_irq(ssp->irq, navpoint); + +	input_unregister_device(navpoint->input); +	kfree(navpoint); + +	pxa_ssp_free(ssp); + +	if (gpio_is_valid(pdata->gpio)) +		gpio_free(pdata->gpio); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int navpoint_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct navpoint *navpoint = platform_get_drvdata(pdev); +	struct input_dev *input = navpoint->input; + +	mutex_lock(&input->mutex); +	if (input->users) +		navpoint_down(navpoint); +	mutex_unlock(&input->mutex); + +	return 0; +} + +static int navpoint_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct navpoint *navpoint = platform_get_drvdata(pdev); +	struct input_dev *input = navpoint->input; + +	mutex_lock(&input->mutex); +	if (input->users) +		navpoint_up(navpoint); +	mutex_unlock(&input->mutex); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume); + +static struct platform_driver navpoint_driver = { +	.probe		= navpoint_probe, +	.remove		= navpoint_remove, +	.driver = { +		.name	= "navpoint", +		.owner	= THIS_MODULE, +		.pm	= &navpoint_pm_ops, +	}, +}; + +module_platform_driver(navpoint_driver); + +MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); +MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:navpoint"); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index cd9d0c97e42..cff065f6261 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -11,6 +11,9 @@   * the Free Software Foundation.   */ +#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt +#define psmouse_fmt(fmt)	fmt +  #include <linux/delay.h>  #include <linux/module.h>  #include <linux/slab.h> @@ -31,6 +34,7 @@  #include "touchkit_ps2.h"  #include "elantech.h"  #include "sentelic.h" +#include "cypress_ps2.h"  #define DRIVER_DESC	"PS/2 mouse driver" @@ -57,7 +61,7 @@ static unsigned int psmouse_rate = 100;  module_param_named(rate, psmouse_rate, uint, 0644);  MODULE_PARM_DESC(rate, "Report rate, in reports per second."); -static unsigned int psmouse_smartscroll = 1; +static bool psmouse_smartscroll = 1;  module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);  MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); @@ -124,7 +128,7 @@ struct psmouse_protocol {   * relevant events to the input module once full packet has arrived.   */ -static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) +psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)  {  	struct input_dev *dev = psmouse->dev;  	unsigned char *packet = psmouse->packet; @@ -251,11 +255,14 @@ static int psmouse_handle_byte(struct psmouse *psmouse)  	switch (rc) {  	case PSMOUSE_BAD_DATA:  		if (psmouse->state == PSMOUSE_ACTIVATED) { -			printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", -				psmouse->name, psmouse->phys, psmouse->pktcnt); +			psmouse_warn(psmouse, +				     "%s at %s lost sync at byte %d\n", +				     psmouse->name, psmouse->phys, +				     psmouse->pktcnt);  			if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {  				__psmouse_set_state(psmouse, PSMOUSE_IGNORE); -				printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); +				psmouse_notice(psmouse, +						"issuing reconnect request\n");  				serio_reconnect(psmouse->ps2dev.serio);  				return -1;  			} @@ -267,8 +274,9 @@ static int psmouse_handle_byte(struct psmouse *psmouse)  		psmouse->pktcnt = 0;  		if (psmouse->out_of_sync_cnt) {  			psmouse->out_of_sync_cnt = 0; -			printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", -				psmouse->name, psmouse->phys); +			psmouse_notice(psmouse, +					"%s at %s - driver resynced.\n", +					psmouse->name, psmouse->phys);  		}  		break; @@ -295,9 +303,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,  		     ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {  		if (psmouse->state == PSMOUSE_ACTIVATED) -			printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n", -				flags & SERIO_TIMEOUT ? " timeout" : "", -				flags & SERIO_PARITY ? " bad parity" : ""); +			psmouse_warn(psmouse, +				     "bad data from KBC -%s%s\n", +				     flags & SERIO_TIMEOUT ? " timeout" : "", +				     flags & SERIO_PARITY ? " bad parity" : "");  		ps2_cmd_aborted(&psmouse->ps2dev);  		goto out;  	} @@ -315,8 +324,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,  	if (psmouse->state == PSMOUSE_ACTIVATED &&  	    psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { -		printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", -		       psmouse->name, psmouse->phys, psmouse->pktcnt); +		psmouse_info(psmouse, "%s at %s lost synchronization, throwing %d bytes away.\n", +			     psmouse->name, psmouse->phys, psmouse->pktcnt);  		psmouse->badbyte = psmouse->packet[0];  		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);  		psmouse_queue_work(psmouse, &psmouse->resync_work, 0); @@ -410,6 +419,49 @@ int psmouse_reset(struct psmouse *psmouse)  	return 0;  } +/* + * Here we set the mouse resolution. + */ + +void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ +	static const unsigned char params[] = { 0, 1, 2, 2, 3 }; +	unsigned char p; + +	if (resolution == 0 || resolution > 200) +		resolution = 200; + +	p = params[resolution / 50]; +	ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); +	psmouse->resolution = 25 << p; +} + +/* + * Here we set the mouse report rate. + */ + +static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) +{ +	static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; +	unsigned char r; +	int i = 0; + +	while (rates[i] > rate) i++; +	r = rates[i]; +	ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); +	psmouse->rate = r; +} + +/* + * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. + */ + +static int psmouse_poll(struct psmouse *psmouse) +{ +	return ps2_command(&psmouse->ps2dev, psmouse->packet, +			   PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); +} +  /*   * Genius NetMouse magic init. @@ -595,6 +647,56 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)  }  /* + * Apply default settings to the psmouse structure. Most of them will + * be overridden by individual protocol initialization routines. + */ + +static void psmouse_apply_defaults(struct psmouse *psmouse) +{ +	struct input_dev *input_dev = psmouse->dev; + +	memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); +	memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); +	memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); +	memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); +	memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); + +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(EV_REL, input_dev->evbit); + +	__set_bit(BTN_LEFT, input_dev->keybit); +	__set_bit(BTN_RIGHT, input_dev->keybit); + +	__set_bit(REL_X, input_dev->relbit); +	__set_bit(REL_Y, input_dev->relbit); + +	psmouse->set_rate = psmouse_set_rate; +	psmouse->set_resolution = psmouse_set_resolution; +	psmouse->poll = psmouse_poll; +	psmouse->protocol_handler = psmouse_process_byte; +	psmouse->pktsize = 3; +	psmouse->reconnect = NULL; +	psmouse->disconnect = NULL; +	psmouse->cleanup = NULL; +	psmouse->pt_activate = NULL; +	psmouse->pt_deactivate = NULL; +} + +/* + * Apply default settings to the psmouse structure and call specified + * protocol detection or initialization routine. + */ +static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, +					   bool set_properties), +			     struct psmouse *psmouse, bool set_properties) +{ +	if (set_properties) +		psmouse_apply_defaults(psmouse); + +	return detect(psmouse, set_properties); +} + +/*   * psmouse_extensions() probes for any extensions to the basic PS/2 protocol   * the mouse may have.   */ @@ -608,7 +710,7 @@ static int psmouse_extensions(struct psmouse *psmouse,   * We always check for lifebook because it does not disturb mouse   * (it only checks DMI information).   */ -	if (lifebook_detect(psmouse, set_properties) == 0) { +	if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) {  		if (max_proto > PSMOUSE_IMEX) {  			if (!set_properties || lifebook_init(psmouse) == 0)  				return PSMOUSE_LIFEBOOK; @@ -620,15 +722,18 @@ static int psmouse_extensions(struct psmouse *psmouse,   * upsets the thinkingmouse).   */ -	if (max_proto > PSMOUSE_IMEX && thinking_detect(psmouse, set_properties) == 0) +	if (max_proto > PSMOUSE_IMEX && +	    psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) {  		return PSMOUSE_THINKPS; +	}  /*   * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol   * support is disabled in config - we need to know if it is synaptics so we   * can reset it properly after probing for intellimouse.   */ -	if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) { +	if (max_proto > PSMOUSE_PS2 && +	    psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) {  		synaptics_hardware = true;  		if (max_proto > PSMOUSE_IMEX) { @@ -655,11 +760,34 @@ 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) {  		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); -		if (alps_detect(psmouse, set_properties) == 0) { +		if (psmouse_do_detect(alps_detect, +				      psmouse, set_properties) == 0) {  			if (!set_properties || alps_init(psmouse) == 0)  				return PSMOUSE_ALPS;  /* @@ -673,7 +801,7 @@ static int psmouse_extensions(struct psmouse *psmouse,   * Try OLPC HGPK touchpad.   */  	if (max_proto > PSMOUSE_IMEX && -			hgpk_detect(psmouse, set_properties) == 0) { +	    psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) {  		if (!set_properties || hgpk_init(psmouse) == 0)  			return PSMOUSE_HGPK;  /* @@ -686,7 +814,7 @@ static int psmouse_extensions(struct psmouse *psmouse,   * Try Elantech touchpad.   */  	if (max_proto > PSMOUSE_IMEX && -			elantech_detect(psmouse, set_properties) == 0) { +	    psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) {  		if (!set_properties || elantech_init(psmouse) == 0)  			return PSMOUSE_ELANTECH;  /* @@ -695,18 +823,21 @@ static int psmouse_extensions(struct psmouse *psmouse,  		max_proto = PSMOUSE_IMEX;  	} -  	if (max_proto > PSMOUSE_IMEX) { -		if (genius_detect(psmouse, set_properties) == 0) +		if (psmouse_do_detect(genius_detect, +				      psmouse, set_properties) == 0)  			return PSMOUSE_GENPS; -		if (ps2pp_init(psmouse, set_properties) == 0) +		if (psmouse_do_detect(ps2pp_init, +				      psmouse, set_properties) == 0)  			return PSMOUSE_PS2PP; -		if (trackpoint_detect(psmouse, set_properties) == 0) +		if (psmouse_do_detect(trackpoint_detect, +				      psmouse, set_properties) == 0)  			return PSMOUSE_TRACKPOINT; -		if (touchkit_ps2_detect(psmouse, set_properties) == 0) +		if (psmouse_do_detect(touchkit_ps2_detect, +				      psmouse, set_properties) == 0)  			return PSMOUSE_TOUCHKIT_PS2;  	} @@ -715,7 +846,8 @@ static int psmouse_extensions(struct psmouse *psmouse,   * Trackpoint devices (causing TP_READ_ID command to time out).   */  	if (max_proto > PSMOUSE_IMEX) { -		if (fsp_detect(psmouse, set_properties) == 0) { +		if (psmouse_do_detect(fsp_detect, +				      psmouse, set_properties) == 0) {  			if (!set_properties || fsp_init(psmouse) == 0)  				return PSMOUSE_FSP;  /* @@ -733,17 +865,23 @@ static int psmouse_extensions(struct psmouse *psmouse,  	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);  	psmouse_reset(psmouse); -	if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0) +	if (max_proto >= PSMOUSE_IMEX && +	    psmouse_do_detect(im_explorer_detect, +			      psmouse, set_properties) == 0) {  		return PSMOUSE_IMEX; +	} -	if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0) +	if (max_proto >= PSMOUSE_IMPS && +	    psmouse_do_detect(intellimouse_detect, +			      psmouse, set_properties) == 0) {  		return PSMOUSE_IMPS; +	}  /*   * Okay, all failed, we have a standard mouse here. The number of the buttons   * is still a question, though. We assume 3.   */ -	ps2bare_detect(psmouse, set_properties); +	psmouse_do_detect(ps2bare_detect, psmouse, set_properties);  	if (synaptics_hardware) {  /* @@ -781,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", @@ -811,6 +958,13 @@ static const struct psmouse_protocol psmouse_protocols[] = {  		.detect		= synaptics_detect,  		.init		= synaptics_init,  	}, +	{ +		.type		= PSMOUSE_SYNAPTICS_RELATIVE, +		.name		= "SynRelPS/2", +		.alias		= "synaptics-relative", +		.detect		= synaptics_detect, +		.init		= synaptics_init_relative, +	},  #endif  #ifdef CONFIG_MOUSE_PS2_ALPS  	{ @@ -943,45 +1097,13 @@ static int psmouse_probe(struct psmouse *psmouse)   */  	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) -		printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys); +		psmouse_warn(psmouse, "Failed to reset mouse on %s\n", +			     ps2dev->serio->phys);  	return 0;  }  /* - * Here we set the mouse resolution. - */ - -void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) -{ -	static const unsigned char params[] = { 0, 1, 2, 2, 3 }; -	unsigned char p; - -	if (resolution == 0 || resolution > 200) -		resolution = 200; - -	p = params[resolution / 50]; -	ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); -	psmouse->resolution = 25 << p; -} - -/* - * Here we set the mouse report rate. - */ - -static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) -{ -	static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; -	unsigned char r; -	int i = 0; - -	while (rates[i] > rate) i++; -	r = rates[i]; -	ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); -	psmouse->rate = r; -} - -/*   * psmouse_initialize() initializes the mouse to a sane state.   */ @@ -1002,38 +1124,33 @@ static void psmouse_initialize(struct psmouse *psmouse)   * psmouse_activate() enables the mouse so that we get motion reports from it.   */ -static void psmouse_activate(struct psmouse *psmouse) +int psmouse_activate(struct psmouse *psmouse)  { -	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) -		printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", -			psmouse->ps2dev.serio->phys); +	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { +		psmouse_warn(psmouse, "Failed to enable mouse on %s\n", +			     psmouse->ps2dev.serio->phys); +		return -1; +	}  	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); +	return 0;  } -  /*   * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion   * reports from it unless we explicitly request it.   */ -static void psmouse_deactivate(struct psmouse *psmouse) +int psmouse_deactivate(struct psmouse *psmouse)  { -	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) -		printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", -			psmouse->ps2dev.serio->phys); +	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { +		psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n", +			     psmouse->ps2dev.serio->phys); +		return -1; +	}  	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); -} - -/* - * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it. - */ - -static int psmouse_poll(struct psmouse *psmouse) -{ -	return ps2_command(&psmouse->ps2dev, psmouse->packet, -			   PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); +	return 0;  } @@ -1115,14 +1232,15 @@ static void psmouse_resync(struct work_struct *work)  	}  	if (!enabled) { -		printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n", -			psmouse->ps2dev.serio->phys); +		psmouse_warn(psmouse, "failed to re-enable mouse on %s\n", +			     psmouse->ps2dev.serio->phys);  		failed = true;  	}  	if (failed) {  		psmouse_set_state(psmouse, PSMOUSE_IGNORE); -		printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n"); +		psmouse_info(psmouse, +			     "resync failed, issuing reconnect request\n");  		serio_reconnect(serio);  	} else  		psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); @@ -1155,8 +1273,8 @@ static void psmouse_cleanup(struct serio *serio)  	 * Disable stream mode so cleanup routine can proceed undisturbed.  	 */  	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) -		printk(KERN_WARNING "psmouse.c: Failed to disable mouse on %s\n", -			psmouse->ps2dev.serio->phys); +		psmouse_warn(psmouse, "Failed to disable mouse on %s\n", +			     psmouse->ps2dev.serio->phys);  	if (psmouse->cleanup)  		psmouse->cleanup(psmouse); @@ -1235,18 +1353,9 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,  	input_dev->dev.parent = &psmouse->ps2dev.serio->dev; -	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); -	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = -				BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); -	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - -	psmouse->set_rate = psmouse_set_rate; -	psmouse->set_resolution = psmouse_set_resolution; -	psmouse->poll = psmouse_poll; -	psmouse->protocol_handler = psmouse_process_byte; -	psmouse->pktsize = 3; -  	if (proto && (proto->detect || proto->init)) { +		psmouse_apply_defaults(psmouse); +  		if (proto->detect && proto->detect(psmouse, true) < 0)  			return -1; @@ -1400,7 +1509,8 @@ static int psmouse_reconnect(struct serio *serio)  	int rc = -1;  	if (!drv || !psmouse) { -		printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n"); +		psmouse_dbg(psmouse, +			    "reconnect request, but serio is disconnected, ignoring...\n");  		return -1;  	} @@ -1427,8 +1537,9 @@ static int psmouse_reconnect(struct serio *serio)  			goto out;  	} -	/* ok, the device type (and capabilities) match the old one, -	 * we can continue using it, complete intialization +	/* +	 * OK, the device type (and capabilities) match the old one, +	 * we can continue using it, complete initialization  	 */  	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); @@ -1546,13 +1657,12 @@ static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char  static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count)  {  	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); -	unsigned long value; - -	if (strict_strtoul(buf, 10, &value)) -		return -EINVAL; +	unsigned int value; +	int err; -	if ((unsigned int)value != value) -		return -EINVAL; +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err;  	*field = value; @@ -1586,9 +1696,8 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co  	while (!list_empty(&serio->children)) {  		if (++retry > 3) { -			printk(KERN_WARNING -				"psmouse: failed to destroy children ports, " -				"protocol change aborted.\n"); +			psmouse_warn(psmouse, +				     "failed to destroy children ports, protocol change aborted.\n");  			input_free_device(new_dev);  			return -EIO;  		} @@ -1660,10 +1769,12 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co  static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count)  { -	unsigned long value; +	unsigned int value; +	int err; -	if (strict_strtoul(buf, 10, &value)) -		return -EINVAL; +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err;  	psmouse->set_rate(psmouse, value);  	return count; @@ -1671,10 +1782,12 @@ static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const  static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count)  { -	unsigned long value; +	unsigned int value; +	int err; -	if (strict_strtoul(buf, 10, &value)) -		return -EINVAL; +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err;  	psmouse->set_resolution(psmouse, value);  	return count; @@ -1711,10 +1824,11 @@ static int __init psmouse_init(void)  	lifebook_module_init();  	synaptics_module_init(); +	hgpk_module_init();  	kpsmoused_wq = create_singlethread_workqueue("kpsmoused");  	if (!kpsmoused_wq) { -		printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n"); +		pr_err("failed to create kpsmoused workqueue\n");  		return -ENOMEM;  	} diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 593e910bfc7..2f0b39d59a9 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -8,6 +8,7 @@  #define PSMOUSE_CMD_SETSTREAM	0x00ea  #define PSMOUSE_CMD_SETPOLL	0x00f0  #define PSMOUSE_CMD_POLL	0x00eb	/* caller sets number of bytes to receive */ +#define PSMOUSE_CMD_RESET_WRAP	0x00ec  #define PSMOUSE_CMD_GETID	0x02f2  #define PSMOUSE_CMD_SETRATE	0x10f3  #define PSMOUSE_CMD_ENABLE	0x00f4 @@ -93,6 +94,8 @@ enum psmouse_type {  	PSMOUSE_HGPK,  	PSMOUSE_ELANTECH,  	PSMOUSE_FSP, +	PSMOUSE_SYNAPTICS_RELATIVE, +	PSMOUSE_CYPRESS,  	PSMOUSE_AUTO		/* This one should always be last */  }; @@ -102,6 +105,9 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);  int psmouse_reset(struct psmouse *psmouse);  void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);  void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); +psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); +int psmouse_activate(struct psmouse *psmouse); +int psmouse_deactivate(struct psmouse *psmouse);  struct psmouse_attribute {  	struct device_attribute dattr; @@ -150,4 +156,29 @@ static struct psmouse_attribute psmouse_attr_##_name = {			\  	static ssize_t _set(struct psmouse *, void *, const char *, size_t);	\  	__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true) +#ifndef psmouse_fmt +#define psmouse_fmt(fmt)	KBUILD_BASENAME ": " fmt +#endif + +#define psmouse_dbg(psmouse, format, ...)		\ +	dev_dbg(&(psmouse)->ps2dev.serio->dev,		\ +		psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_info(psmouse, format, ...)		\ +	dev_info(&(psmouse)->ps2dev.serio->dev,		\ +		 psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_warn(psmouse, format, ...)		\ +	dev_warn(&(psmouse)->ps2dev.serio->dev,		\ +		 psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_err(psmouse, format, ...)		\ +	dev_err(&(psmouse)->ps2dev.serio->dev,		\ +		psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_notice(psmouse, format, ...)		\ +	dev_notice(&(psmouse)->ps2dev.serio->dev,	\ +		   psmouse_fmt(format), ##__VA_ARGS__) +#define psmouse_printk(level, psmouse, format, ...)	\ +	dev_printk(level,				\ +		   &(psmouse)->ps2dev.serio->dev,	\ +		   psmouse_fmt(format), ##__VA_ARGS__) + +  #endif /* _PSMOUSE_H */ diff --git a/drivers/input/mouse/pxa930_trkball.c b/drivers/input/mouse/pxa930_trkball.c index 943cfec1566..9b4d9a59e22 100644 --- a/drivers/input/mouse/pxa930_trkball.c +++ b/drivers/input/mouse/pxa930_trkball.c @@ -10,9 +10,7 @@   *  published by the Free Software Foundation.   */ -#include <linux/init.h>  #include <linux/input.h> -#include <linux/version.h>  #include <linux/interrupt.h>  #include <linux/module.h>  #include <linux/platform_device.h> @@ -21,7 +19,7 @@  #include <linux/slab.h>  #include <mach/hardware.h> -#include <mach/pxa930_trkball.h> +#include <linux/platform_data/mouse-pxa930_trkball.h>  /* Trackball Controller Register Definitions */  #define TBCR		(0x000C) @@ -144,7 +142,7 @@ static void pxa930_trkball_close(struct input_dev *dev)  	pxa930_trkball_disable(trkball);  } -static int __devinit pxa930_trkball_probe(struct platform_device *pdev) +static int pxa930_trkball_probe(struct platform_device *pdev)  {  	struct pxa930_trkball *trkball;  	struct input_dev *input; @@ -167,7 +165,7 @@ static int __devinit pxa930_trkball_probe(struct platform_device *pdev)  	if (!trkball)  		return -ENOMEM; -	trkball->pdata = pdev->dev.platform_data; +	trkball->pdata = dev_get_platdata(&pdev->dev);  	if (!trkball->pdata) {  		dev_err(&pdev->dev, "no platform data defined\n");  		error = -EINVAL; @@ -184,7 +182,7 @@ static int __devinit pxa930_trkball_probe(struct platform_device *pdev)  	/* held the module in reset, will be enabled in open() */  	pxa930_trkball_disable(trkball); -	error = request_irq(irq, pxa930_trkball_interrupt, IRQF_DISABLED, +	error = request_irq(irq, pxa930_trkball_interrupt, 0,  			    pdev->name, trkball);  	if (error) {  		dev_err(&pdev->dev, "failed to request irq: %d\n", error); @@ -231,7 +229,7 @@ failed:  	return error;  } -static int __devexit pxa930_trkball_remove(struct platform_device *pdev) +static int pxa930_trkball_remove(struct platform_device *pdev)  {  	struct pxa930_trkball *trkball = platform_get_drvdata(pdev);  	int irq = platform_get_irq(pdev, 0); @@ -249,21 +247,9 @@ static struct platform_driver pxa930_trkball_driver = {  		.name	= "pxa930-trkball",  	},  	.probe		= pxa930_trkball_probe, -	.remove		= __devexit_p(pxa930_trkball_remove), +	.remove		= pxa930_trkball_remove,  }; - -static int __init pxa930_trkball_init(void) -{ -	return platform_driver_register(&pxa930_trkball_driver); -} - -static void __exit pxa930_trkball_exit(void) -{ -	platform_driver_unregister(&pxa930_trkball_driver); -} - -module_init(pxa930_trkball_init); -module_exit(pxa930_trkball_exit); +module_platform_driver(pxa930_trkball_driver);  MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>");  MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver"); diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c index 272deddc8db..21c60fea5d3 100644 --- a/drivers/input/mouse/rpcmouse.c +++ b/drivers/input/mouse/rpcmouse.c @@ -42,7 +42,7 @@ static irqreturn_t rpcmouse_irq(int irq, void *dev_id)  	x = (short) iomd_readl(IOMD_MOUSEX);  	y = (short) iomd_readl(IOMD_MOUSEY); -	b = (short) (__raw_readl(0xe0310000) ^ 0x70); +	b = (short) (__raw_readl(IOMEM(0xe0310000)) ^ 0x70);  	dx = x - rpcmouse_lastx;  	dy = y - rpcmouse_lasty; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 1242775fee1..cc7e0d4a8f9 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -2,7 +2,7 @@   * Finger Sensing Pad PS/2 mouse driver.   *   * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. - * Copyright (C) 2005-2010 Tai-hwa Liang, Sentelic Corporation. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.   *   *   This program is free software; you can redistribute it and/or   *   modify it under the terms of the GNU General Public License @@ -20,8 +20,8 @@   */  #include <linux/module.h> -#include <linux/version.h>  #include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/ctype.h>  #include <linux/libps2.h>  #include <linux/serio.h> @@ -37,8 +37,11 @@  #define	FSP_CMD_TIMEOUT		200  #define	FSP_CMD_TIMEOUT2	30 +#define	GET_ABS_X(packet)	((packet[1] << 2) | ((packet[3] >> 2) & 0x03)) +#define	GET_ABS_Y(packet)	((packet[2] << 2) | (packet[3] & 0x03)) +  /** Driver version. */ -static const char fsp_drv_ver[] = "1.0.0-K"; +static const char fsp_drv_ver[] = "1.1.0-K";  /*   * Make sure that the value being sent to FSP will not conflict with @@ -91,8 +94,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)  	 * to do that for writes because sysfs set helper does this for  	 * us.  	 */ -	ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); -	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); +	psmouse_deactivate(psmouse);  	ps2_begin_command(ps2dev); @@ -129,10 +131,10 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)   out:  	ps2_end_command(ps2dev); -	ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); -	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); -	dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", -		reg_addr, *reg_val, rc); +	psmouse_activate(psmouse); +	psmouse_dbg(psmouse, +		    "READ REG: 0x%02x is 0x%02x (rc = %d)\n", +		    reg_addr, *reg_val, rc);  	return rc;  } @@ -163,7 +165,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)  	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);  	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) -		return -1; +		goto out;  	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {  		/* inversion is required */ @@ -182,8 +184,9 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)   out:  	ps2_end_command(ps2dev); -	dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", -		reg_addr, reg_val, rc); +	psmouse_dbg(psmouse, +		    "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", +		    reg_addr, reg_val, rc);  	return rc;  } @@ -214,8 +217,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)  	unsigned char param[3];  	int rc = -1; -	ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); -	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); +	psmouse_deactivate(psmouse);  	ps2_begin_command(ps2dev); @@ -240,10 +242,10 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)   out:  	ps2_end_command(ps2dev); -	ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); -	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); -	dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", -		*reg_val, rc); +	psmouse_activate(psmouse); +	psmouse_dbg(psmouse, +		    "READ PAGE REG: 0x%02x (rc = %d)\n", +		    *reg_val, rc);  	return rc;  } @@ -262,7 +264,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)  	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);  	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) -		return -1; +		goto out;  	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {  		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); @@ -279,8 +281,9 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)   out:  	ps2_end_command(ps2dev); -	dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", -		reg_val, rc); +	psmouse_dbg(psmouse, +		    "WRITE PAGE REG: to 0x%02x (rc = %d)\n", +		    reg_val, rc);  	return rc;  } @@ -300,6 +303,27 @@ static int fsp_get_revision(struct psmouse *psmouse, int *rev)  	return 0;  } +static int fsp_get_sn(struct psmouse *psmouse, int *sn) +{ +	int v0, v1, v2; +	int rc = -EIO; + +	/* production number since Cx is available at: 0x0b40 ~ 0x0b42 */ +	if (fsp_page_reg_write(psmouse, FSP_PAGE_0B)) +		goto out; +	if (fsp_reg_read(psmouse, FSP_REG_SN0, &v0)) +		goto out; +	if (fsp_reg_read(psmouse, FSP_REG_SN1, &v1)) +		goto out; +	if (fsp_reg_read(psmouse, FSP_REG_SN2, &v2)) +		goto out; +	*sn = (v0 << 16) | (v1 << 8) | v2; +	rc = 0; +out: +	fsp_page_reg_write(psmouse, FSP_PAGE_DEFAULT); +	return rc; +} +  static int fsp_get_buttons(struct psmouse *psmouse, int *btn)  {  	static const int buttons[] = { @@ -310,7 +334,7 @@ static int fsp_get_buttons(struct psmouse *psmouse, int *btn)  	};  	int val; -	if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1) +	if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, &val) == -1)  		return -EIO;  	*btn = buttons[(val & 0x30) >> 4]; @@ -324,7 +348,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)  	int res = 0;  	if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { -		dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n"); +		psmouse_err(psmouse, "Unable get OPC state.\n");  		return -EIO;  	} @@ -341,8 +365,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)  	}  	if (res != 0) { -		dev_err(&psmouse->ps2dev.serio->dev, -			"Unable to enable OPC tag.\n"); +		psmouse_err(psmouse, "Unable to enable OPC tag.\n");  		res = -EIO;  	} @@ -409,7 +432,7 @@ static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)  static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,  				   const char *buf, size_t count)  { -	unsigned long reg, val; +	int reg, val;  	char *rest;  	ssize_t retval; @@ -417,7 +440,11 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,  	if (rest == buf || *rest != ' ' || reg > 0xff)  		return -EINVAL; -	if (strict_strtoul(rest + 1, 16, &val) || val > 0xff) +	retval = kstrtoint(rest + 1, 16, &val); +	if (retval) +		return retval; + +	if (val > 0xff)  		return -EINVAL;  	if (fsp_reg_write_enable(psmouse, true)) @@ -449,10 +476,13 @@ static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,  					const char *buf, size_t count)  {  	struct fsp_data *pad = psmouse->private; -	unsigned long reg; -	int val; +	int reg, val, err; -	if (strict_strtoul(buf, 16, ®) || reg > 0xff) +	err = kstrtoint(buf, 16, ®); +	if (err) +		return err; + +	if (reg > 0xff)  		return -EINVAL;  	if (fsp_reg_read(psmouse, reg, &val)) @@ -481,9 +511,13 @@ static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,  static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,  					const char *buf, size_t count)  { -	unsigned long val; +	int val, err; + +	err = kstrtoint(buf, 16, &val); +	if (err) +		return err; -	if (strict_strtoul(buf, 16, &val) || val > 0xff) +	if (val > 0xff)  		return -EINVAL;  	if (fsp_page_reg_write(psmouse, val)) @@ -506,9 +540,14 @@ static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse,  static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,  					const char *buf, size_t count)  { -	unsigned long val; +	unsigned int val; +	int err; -	if (strict_strtoul(buf, 10, &val) || val > 1) +	err = kstrtouint(buf, 10, &val); +	if (err) +		return err; + +	if (val > 1)  		return -EINVAL;  	fsp_onpad_vscr(psmouse, val); @@ -530,9 +569,14 @@ static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse,  static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,  					const char *buf, size_t count)  { -	unsigned long val; +	unsigned int val; +	int err; + +	err = kstrtouint(buf, 10, &val); +	if (err) +		return err; -	if (strict_strtoul(buf, 10, &val) || val > 1) +	if (val > 1)  		return -EINVAL;  	fsp_onpad_hscr(psmouse, val); @@ -599,36 +643,71 @@ static struct attribute_group fsp_attribute_group = {  	.attrs = fsp_attributes,  }; -#ifdef FSP_DEBUG -static void fsp_packet_debug(unsigned char packet[]) +#ifdef	FSP_DEBUG +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])  {  	static unsigned int ps2_packet_cnt;  	static unsigned int ps2_last_second;  	unsigned int jiffies_msec; +	const char *packet_type = "UNKNOWN"; +	unsigned short abs_x = 0, abs_y = 0; + +	/* Interpret & dump the packet data. */ +	switch (packet[0] >> FSP_PKT_TYPE_SHIFT) { +	case FSP_PKT_TYPE_ABS: +		packet_type = "Absolute"; +		abs_x = GET_ABS_X(packet); +		abs_y = GET_ABS_Y(packet); +		break; +	case FSP_PKT_TYPE_NORMAL: +		packet_type = "Normal"; +		break; +	case FSP_PKT_TYPE_NOTIFY: +		packet_type = "Notify"; +		break; +	case FSP_PKT_TYPE_NORMAL_OPC: +		packet_type = "Normal-OPC"; +		break; +	}  	ps2_packet_cnt++;  	jiffies_msec = jiffies_to_msecs(jiffies); -	printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n", -		jiffies_msec, packet[0], packet[1], packet[2], packet[3]); +	psmouse_dbg(psmouse, +		    "%08dms %s packets: %02x, %02x, %02x, %02x; " +		    "abs_x: %d, abs_y: %d\n", +		    jiffies_msec, packet_type, +		    packet[0], packet[1], packet[2], packet[3], abs_x, abs_y);  	if (jiffies_msec - ps2_last_second > 1000) { -		printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt); +		psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);  		ps2_packet_cnt = 0;  		ps2_last_second = jiffies_msec;  	}  }  #else -static void fsp_packet_debug(unsigned char packet[]) +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])  {  }  #endif +static void fsp_set_slot(struct input_dev *dev, int slot, bool active, +			 unsigned int x, unsigned int y) +{ +	input_mt_slot(dev, slot); +	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); +	if (active) { +		input_report_abs(dev, ABS_MT_POSITION_X, x); +		input_report_abs(dev, ABS_MT_POSITION_Y, y); +	} +} +  static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)  {  	struct input_dev *dev = psmouse->dev;  	struct fsp_data *ad = psmouse->private;  	unsigned char *packet = psmouse->packet;  	unsigned char button_status = 0, lscroll = 0, rscroll = 0; +	unsigned short abs_x, abs_y, fgrs = 0;  	int rel_x, rel_y;  	if (psmouse->pktcnt < 4) @@ -638,16 +717,95 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)  	 * Full packet accumulated, process it  	 */ +	fsp_packet_debug(psmouse, packet); +  	switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {  	case FSP_PKT_TYPE_ABS: -		dev_warn(&psmouse->ps2dev.serio->dev, -			 "Unexpected absolute mode packet, ignored.\n"); + +		if ((packet[0] == 0x48 || packet[0] == 0x49) && +		    packet[1] == 0 && packet[2] == 0) { +			/* +			 * Ignore coordinate noise when finger leaving the +			 * surface, otherwise cursor may jump to upper-left +			 * corner. +			 */ +			packet[3] &= 0xf0; +		} + +		abs_x = GET_ABS_X(packet); +		abs_y = GET_ABS_Y(packet); + +		if (packet[0] & FSP_PB0_MFMC) { +			/* +			 * MFMC packet: assume that there are two fingers on +			 * pad +			 */ +			fgrs = 2; + +			/* MFMC packet */ +			if (packet[0] & FSP_PB0_MFMC_FGR2) { +				/* 2nd finger */ +				if (ad->last_mt_fgr == 2) { +					/* +					 * workaround for buggy firmware +					 * which doesn't clear MFMC bit if +					 * the 1st finger is up +					 */ +					fgrs = 1; +					fsp_set_slot(dev, 0, false, 0, 0); +				} +				ad->last_mt_fgr = 2; + +				fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y); +			} else { +				/* 1st finger */ +				if (ad->last_mt_fgr == 1) { +					/* +					 * workaround for buggy firmware +					 * which doesn't clear MFMC bit if +					 * the 2nd finger is up +					 */ +					fgrs = 1; +					fsp_set_slot(dev, 1, false, 0, 0); +				} +				ad->last_mt_fgr = 1; +				fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y); +			} +		} else { +			/* SFAC packet */ +			if ((packet[0] & (FSP_PB0_LBTN|FSP_PB0_PHY_BTN)) == +				FSP_PB0_LBTN) { +				/* On-pad click in SFAC mode should be handled +				 * by userspace.  On-pad clicks in MFMC mode +				 * are real clickpad clicks, and not ignored. +				 */ +				packet[0] &= ~FSP_PB0_LBTN; +			} + +			/* no multi-finger information */ +			ad->last_mt_fgr = 0; + +			if (abs_x != 0 && abs_y != 0) +				fgrs = 1; + +			fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y); +			fsp_set_slot(dev, 1, false, 0, 0); +		} +		if (fgrs == 1 || (fgrs == 2 && !(packet[0] & FSP_PB0_MFMC_FGR2))) { +			input_report_abs(dev, ABS_X, abs_x); +			input_report_abs(dev, ABS_Y, abs_y); +		} +		input_report_key(dev, BTN_LEFT, packet[0] & 0x01); +		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); +		input_report_key(dev, BTN_TOUCH, fgrs); +		input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1); +		input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2);  		break;  	case FSP_PKT_TYPE_NORMAL_OPC:  		/* on-pad click, filter it if necessary */  		if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) -			packet[0] &= ~BIT(0); +			packet[0] &= ~FSP_PB0_LBTN;  		/* fall through */  	case FSP_PKT_TYPE_NORMAL: @@ -694,8 +852,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)  	input_sync(dev); -	fsp_packet_debug(packet); -  	return PSMOUSE_FULL_PACKET;  } @@ -719,42 +875,106 @@ static int fsp_activate_protocol(struct psmouse *psmouse)  	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);  	if (param[0] != 0x04) { -		dev_err(&psmouse->ps2dev.serio->dev, -			"Unable to enable 4 bytes packet format.\n"); +		psmouse_err(psmouse, +			    "Unable to enable 4 bytes packet format.\n");  		return -EIO;  	} -	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { -		dev_err(&psmouse->ps2dev.serio->dev, -			"Unable to read SYSCTL5 register.\n"); -		return -EIO; -	} +	if (pad->ver < FSP_VER_STL3888_C0) { +		/* Preparing relative coordinates output for older hardware */ +		if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { +			psmouse_err(psmouse, +				    "Unable to read SYSCTL5 register.\n"); +			return -EIO; +		} -	val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); -	/* Ensure we are not in absolute mode */ -	val &= ~FSP_BIT_EN_PKT_G0; -	if (pad->buttons == 0x06) { -		/* Left/Middle/Right & Scroll Up/Down/Right/Left */ -		val |= FSP_BIT_EN_MSID6; -	} +		if (fsp_get_buttons(psmouse, &pad->buttons)) { +			psmouse_err(psmouse, +				    "Unable to retrieve number of buttons.\n"); +			return -EIO; +		} -	if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { -		dev_err(&psmouse->ps2dev.serio->dev, -			"Unable to set up required mode bits.\n"); -		return -EIO; +		val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); +		/* Ensure we are not in absolute mode */ +		val &= ~FSP_BIT_EN_PKT_G0; +		if (pad->buttons == 0x06) { +			/* Left/Middle/Right & Scroll Up/Down/Right/Left */ +			val |= FSP_BIT_EN_MSID6; +		} + +		if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { +			psmouse_err(psmouse, +				    "Unable to set up required mode bits.\n"); +			return -EIO; +		} + +		/* +		 * Enable OPC tags such that driver can tell the difference +		 * between on-pad and real button click +		 */ +		if (fsp_opc_tag_enable(psmouse, true)) +			psmouse_warn(psmouse, +				     "Failed to enable OPC tag mode.\n"); +		/* enable on-pad click by default */ +		pad->flags |= FSPDRV_FLAG_EN_OPC; + +		/* Enable on-pad vertical and horizontal scrolling */ +		fsp_onpad_vscr(psmouse, true); +		fsp_onpad_hscr(psmouse, true); +	} else { +		/* Enable absolute coordinates output for Cx/Dx hardware */ +		if (fsp_reg_write(psmouse, FSP_REG_SWC1, +				  FSP_BIT_SWC1_EN_ABS_1F | +				  FSP_BIT_SWC1_EN_ABS_2F | +				  FSP_BIT_SWC1_EN_FUP_OUT | +				  FSP_BIT_SWC1_EN_ABS_CON)) { +			psmouse_err(psmouse, +				    "Unable to enable absolute coordinates output.\n"); +			return -EIO; +		}  	} -	/* -	 * Enable OPC tags such that driver can tell the difference between -	 * on-pad and real button click -	 */ -	if (fsp_opc_tag_enable(psmouse, true)) -		dev_warn(&psmouse->ps2dev.serio->dev, -			 "Failed to enable OPC tag mode.\n"); +	return 0; +} -	/* Enable on-pad vertical and horizontal scrolling */ -	fsp_onpad_vscr(psmouse, true); -	fsp_onpad_hscr(psmouse, true); +static int fsp_set_input_params(struct psmouse *psmouse) +{ +	struct input_dev *dev = psmouse->dev; +	struct fsp_data *pad = psmouse->private; + +	if (pad->ver < FSP_VER_STL3888_C0) { +		__set_bit(BTN_MIDDLE, dev->keybit); +		__set_bit(BTN_BACK, dev->keybit); +		__set_bit(BTN_FORWARD, dev->keybit); +		__set_bit(REL_WHEEL, dev->relbit); +		__set_bit(REL_HWHEEL, dev->relbit); +	} else { +		/* +		 * Hardware prior to Cx performs much better in relative mode; +		 * hence, only enable absolute coordinates output as well as +		 * multi-touch output for the newer hardware. +		 * +		 * Maximum coordinates can be computed as: +		 * +		 *	number of scanlines * 64 - 57 +		 * +		 * where number of X/Y scanline lines are 16/12. +		 */ +		int abs_x = 967, abs_y = 711; + +		__set_bit(EV_ABS, dev->evbit); +		__clear_bit(EV_REL, dev->evbit); +		__set_bit(BTN_TOUCH, dev->keybit); +		__set_bit(BTN_TOOL_FINGER, dev->keybit); +		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); +		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + +		input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); +		input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); +		input_mt_init_slots(dev, 2, 0); +		input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); +		input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); +	}  	return 0;  } @@ -812,18 +1032,21 @@ static int fsp_reconnect(struct psmouse *psmouse)  int fsp_init(struct psmouse *psmouse)  {  	struct fsp_data *priv; -	int ver, rev, buttons; +	int ver, rev, sn = 0;  	int error;  	if (fsp_get_version(psmouse, &ver) || -	    fsp_get_revision(psmouse, &rev) || -	    fsp_get_buttons(psmouse, &buttons)) { +	    fsp_get_revision(psmouse, &rev)) {  		return -ENODEV;  	} +	if (ver >= FSP_VER_STL3888_C0) { +		/* firmware information is only available since C0 */ +		fsp_get_sn(psmouse, &sn); +	} -	printk(KERN_INFO -		"Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n", -		ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7); +	psmouse_info(psmouse, +		     "Finger Sensing Pad, hw: %d.%d.%d, sn: %x, sw: %s\n", +		     ver >> 4, ver & 0x0F, rev, sn, fsp_drv_ver);  	psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);  	if (!priv) @@ -831,17 +1054,6 @@ int fsp_init(struct psmouse *psmouse)  	priv->ver = ver;  	priv->rev = rev; -	priv->buttons = buttons; - -	/* enable on-pad click by default */ -	priv->flags |= FSPDRV_FLAG_EN_OPC; - -	/* Set up various supported input event bits */ -	__set_bit(BTN_MIDDLE, psmouse->dev->keybit); -	__set_bit(BTN_BACK, psmouse->dev->keybit); -	__set_bit(BTN_FORWARD, psmouse->dev->keybit); -	__set_bit(REL_WHEEL, psmouse->dev->relbit); -	__set_bit(REL_HWHEEL, psmouse->dev->relbit);  	psmouse->protocol_handler = fsp_process_byte;  	psmouse->disconnect = fsp_disconnect; @@ -849,16 +1061,20 @@ int fsp_init(struct psmouse *psmouse)  	psmouse->cleanup = fsp_reset;  	psmouse->pktsize = 4; -	/* set default packet output based on number of buttons we found */  	error = fsp_activate_protocol(psmouse);  	if (error)  		goto err_out; +	/* Set up various supported input event bits */ +	error = fsp_set_input_params(psmouse); +	if (error) +		goto err_out; +  	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,  				   &fsp_attribute_group);  	if (error) { -		dev_err(&psmouse->ps2dev.serio->dev, -			"Failed to create sysfs attributes (%d)", error); +		psmouse_err(psmouse, +			    "Failed to create sysfs attributes (%d)", error);  		goto err_out;  	} diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h index ed1395ac7b8..aa697ece405 100644 --- a/drivers/input/mouse/sentelic.h +++ b/drivers/input/mouse/sentelic.h @@ -2,7 +2,7 @@   * Finger Sensing Pad PS/2 mouse driver.   *   * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. - * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.   *   *   This program is free software; you can redistribute it and/or   *   modify it under the terms of the GNU General Public License @@ -33,6 +33,7 @@  /* Finger-sensing Pad control registers */  #define	FSP_REG_SYSCTL1		0x10  #define	FSP_BIT_EN_REG_CLK	BIT(5) +#define	FSP_REG_TMOD_STATUS	0x20  #define	FSP_REG_OPC_QDOWN	0x31  #define	FSP_BIT_EN_OPC_TAG	BIT(7)  #define	FSP_REG_OPTZ_XLO	0x34 @@ -54,6 +55,24 @@  #define	FSP_BIT_FIX_HSCR	BIT(5)  #define	FSP_BIT_DRAG_LOCK	BIT(6) +#define	FSP_REG_SWC1		(0x90) +#define	FSP_BIT_SWC1_EN_ABS_1F	BIT(0) +#define	FSP_BIT_SWC1_EN_GID	BIT(1) +#define	FSP_BIT_SWC1_EN_ABS_2F	BIT(2) +#define	FSP_BIT_SWC1_EN_FUP_OUT	BIT(3) +#define	FSP_BIT_SWC1_EN_ABS_CON	BIT(4) +#define	FSP_BIT_SWC1_GST_GRP0	BIT(5) +#define	FSP_BIT_SWC1_GST_GRP1	BIT(6) +#define	FSP_BIT_SWC1_BX_COMPAT	BIT(7) + +#define	FSP_PAGE_0B		(0x0b) +#define	FSP_PAGE_82		(0x82) +#define	FSP_PAGE_DEFAULT	FSP_PAGE_82 + +#define	FSP_REG_SN0		(0x40) +#define	FSP_REG_SN1		(0x41) +#define	FSP_REG_SN2		(0x42) +  /* Finger-sensing Pad packet formating related definitions */  /* absolute packet type */ @@ -63,12 +82,32 @@  #define	FSP_PKT_TYPE_NORMAL_OPC	(0x03)  #define	FSP_PKT_TYPE_SHIFT	(6) +/* bit definitions for the first byte of report packet */ +#define	FSP_PB0_LBTN		BIT(0) +#define	FSP_PB0_RBTN		BIT(1) +#define	FSP_PB0_MBTN		BIT(2) +#define	FSP_PB0_MFMC_FGR2	FSP_PB0_MBTN +#define	FSP_PB0_MUST_SET	BIT(3) +#define	FSP_PB0_PHY_BTN		BIT(4) +#define	FSP_PB0_MFMC		BIT(5) + +/* hardware revisions */ +#define	FSP_VER_STL3888_A4	(0xC1) +#define	FSP_VER_STL3888_B0	(0xD0) +#define	FSP_VER_STL3888_B1	(0xD1) +#define	FSP_VER_STL3888_B2	(0xD2) +#define	FSP_VER_STL3888_C0	(0xE0) +#define	FSP_VER_STL3888_C1	(0xE1) +#define	FSP_VER_STL3888_D0	(0xE2) +#define	FSP_VER_STL3888_D1	(0xE3) +#define	FSP_VER_STL3888_E0	(0xE4) +  #ifdef __KERNEL__  struct fsp_data {  	unsigned char	ver;		/* hardware version */  	unsigned char	rev;		/* hardware revison */ -	unsigned char	buttons;	/* Number of buttons */ +	unsigned int	buttons;	/* Number of buttons */  	unsigned int	flags;  #define	FSPDRV_FLAG_EN_OPC	(0x001)	/* enable on-pad clicking */ @@ -77,6 +116,7 @@ struct fsp_data {  	unsigned char	last_reg;	/* Last register we requested read from */  	unsigned char	last_val; +	unsigned int	last_mt_fgr;	/* Last seen finger(multitouch) */  };  #ifdef CONFIG_MOUSE_PS2_SENTELIC diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index 17ff137b9bd..8df526620eb 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -32,7 +32,6 @@  #include <linux/interrupt.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Serial mouse driver" @@ -355,15 +354,4 @@ static struct serio_driver sermouse_drv = {  	.disconnect	= sermouse_disconnect,  }; -static int __init sermouse_init(void) -{ -	return serio_register_driver(&sermouse_drv); -} - -static void __exit sermouse_exit(void) -{ -	serio_unregister_driver(&sermouse_drv); -} - -module_init(sermouse_init); -module_exit(sermouse_exit); +module_serio_driver(sermouse_drv); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2e300a46055..ef9e0b8a9aa 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -24,8 +24,9 @@   */  #include <linux/module.h> +#include <linux/delay.h>  #include <linux/dmi.h> -#include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/serio.h>  #include <linux/libps2.h>  #include <linux/slab.h> @@ -39,11 +40,32 @@   * Note that newer firmware allows querying device for maximum useable   * coordinates.   */ +#define XMIN 0 +#define XMAX 6143 +#define YMIN 0 +#define YMAX 6143  #define XMIN_NOMINAL 1472  #define XMAX_NOMINAL 5472  #define YMIN_NOMINAL 1408  #define YMAX_NOMINAL 4448 +/* Size in bits of absolute position values reported by the hardware */ +#define ABS_POS_BITS 13 + +/* + * These values should represent the absolute maximum value that will + * be reported for a positive position value. Some Synaptics firmware + * uses this value to indicate a finger near the edge of the touchpad + * whose precise position cannot be determined. + * + * At least one touchpad is known to report positions in excess of this + * value which are actually negative values truncated to the 13-bit + * reporting range. These values have never been observed to be lower + * than 8184 (i.e. -8), so we treat all values greater than 8176 as + * negative and any other value as positive. + */ +#define X_MAX_POSITIVE 8176 +#define Y_MAX_POSITIVE 8176  /*****************************************************************************   *	Stuff we need even when we do not want native Synaptics support @@ -95,12 +117,98 @@ void synaptics_reset(struct psmouse *psmouse)  }  #ifdef CONFIG_MOUSE_PS2_SYNAPTICS +struct min_max_quirk { +	const char * const *pnp_ids; +	int x_min, x_max, y_min, y_max; +}; + +static const struct min_max_quirk min_max_pnpid_table[] = { +	{ +		(const char * const []){"LEN0033", NULL}, +		1024, 5052, 2258, 4832 +	}, +	{ +		(const char * const []){"LEN0035", "LEN0042", NULL}, +		1232, 5710, 1156, 4696 +	}, +	{ +		(const char * const []){"LEN0034", "LEN0036", "LEN2002", +					"LEN2004", NULL}, +		1024, 5112, 2024, 4832 +	}, +	{ +		(const char * const []){"LEN2001", NULL}, +		1024, 5022, 2508, 4832 +	}, +	{ } +}; + +/* This list has been kindly provided by Synaptics. */ +static const char * const topbuttonpad_pnp_ids[] = { +	"LEN0017", +	"LEN0018", +	"LEN0019", +	"LEN0023", +	"LEN002A", +	"LEN002B", +	"LEN002C", +	"LEN002D", +	"LEN002E", +	"LEN0033", /* Helix */ +	"LEN0034", /* T431s, L440, L540, T540, W540, X1 Carbon 2nd */ +	"LEN0035", /* X240 */ +	"LEN0036", /* T440 */ +	"LEN0037", +	"LEN0038", +	"LEN0041", +	"LEN0042", /* Yoga */ +	"LEN0045", +	"LEN0046", +	"LEN0047", +	"LEN0048", +	"LEN0049", +	"LEN2000", +	"LEN2001", /* Edge E431 */ +	"LEN2002", /* Edge E531 */ +	"LEN2003", +	"LEN2004", /* L440 */ +	"LEN2005", +	"LEN2006", +	"LEN2007", +	"LEN2008", +	"LEN2009", +	"LEN200A", +	"LEN200B", +	NULL +}; + +static bool matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) +{ +	int i; + +	if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) +		for (i = 0; ids[i]; i++) +			if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) +				return true; + +	return false; +}  /*****************************************************************************   *	Synaptics communications functions   ****************************************************************************/  /* + * Synaptics touchpads report the y coordinate from bottom to top, which is + * opposite from what userspace expects. + * This function is used to invert y before reporting. + */ +static int synaptics_invert_y(int y) +{ +	return YMAX_NOMINAL + YMIN_NOMINAL - y; +} + +/*   * Send a command to the synpatics touchpad by special commands   */  static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) @@ -128,6 +236,35 @@ static int synaptics_model_id(struct psmouse *psmouse)  }  /* + * Read the board id from the touchpad + * The board id is encoded in the "QUERY MODES" response + */ +static int synaptics_board_id(struct psmouse *psmouse) +{ +	struct synaptics_data *priv = psmouse->private; +	unsigned char bid[3]; + +	if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid)) +		return -1; +	priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; +	return 0; +} + +/* + * Read the firmware id from the touchpad + */ +static int synaptics_firmware_id(struct psmouse *psmouse) +{ +	struct synaptics_data *priv = psmouse->private; +	unsigned char fwid[3]; + +	if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid)) +		return -1; +	priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2]; +	return 0; +} + +/*   * Read the capability-bits from the touchpad   * see also the SYN_CAP_* macros   */ @@ -157,8 +294,8 @@ static int synaptics_capability(struct psmouse *psmouse)  	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {  		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { -			printk(KERN_ERR "Synaptics claims to have extended capabilities," -			       " but I'm not able to read them.\n"); +			psmouse_warn(psmouse, +				     "device claims to have extended capabilities, but I'm not able to read them.\n");  		} else {  			priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; @@ -173,8 +310,8 @@ static int synaptics_capability(struct psmouse *psmouse)  	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {  		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) { -			printk(KERN_ERR "Synaptics claims to have extended capability 0x0c," -			       " but I'm not able to read it.\n"); +			psmouse_warn(psmouse, +				     "device claims to have extended capability 0x0c, but I'm not able to read it.\n");  		} else {  			priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];  		} @@ -204,30 +341,52 @@ static int synaptics_identify(struct psmouse *psmouse)   * Read touchpad resolution and maximum reported coordinates   * Resolution is left zero if touchpad does not support the query   */ +  static int synaptics_resolution(struct psmouse *psmouse)  {  	struct synaptics_data *priv = psmouse->private; -	unsigned char res[3]; -	unsigned char max[3]; +	unsigned char resp[3]; +	int i;  	if (SYN_ID_MAJOR(priv->identity) < 4)  		return 0; -	if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) { -		if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) { -			priv->x_res = res[0]; /* x resolution in units/mm */ -			priv->y_res = res[2]; /* y resolution in units/mm */ +	if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) { +		if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) { +			priv->x_res = resp[0]; /* x resolution in units/mm */ +			priv->y_res = resp[2]; /* y resolution in units/mm */ +		} +	} + +	for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { +		if (matches_pnp_id(psmouse, min_max_pnpid_table[i].pnp_ids)) { +			priv->x_min = min_max_pnpid_table[i].x_min; +			priv->x_max = min_max_pnpid_table[i].x_max; +			priv->y_min = min_max_pnpid_table[i].y_min; +			priv->y_max = min_max_pnpid_table[i].y_max; +			return 0;  		}  	}  	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&  	    SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { -		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) { -			printk(KERN_ERR "Synaptics claims to have dimensions query," -			       " but I'm not able to read it.\n"); +		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) { +			psmouse_warn(psmouse, +				     "device claims to have max coordinates query, but I'm not able to read it.\n"); +		} else { +			priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); +			priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); +		} +	} + +	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 && +	    SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) { +		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) { +			psmouse_warn(psmouse, +				     "device claims to have min coordinates query, but I'm not able to read it.\n");  		} else { -			priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1); -			priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3); +			priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); +			priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);  		}  	} @@ -240,6 +399,10 @@ static int synaptics_query_hardware(struct psmouse *psmouse)  		return -1;  	if (synaptics_model_id(psmouse))  		return -1; +	if (synaptics_firmware_id(psmouse)) +		return -1; +	if (synaptics_board_id(psmouse)) +		return -1;  	if (synaptics_capability(psmouse))  		return -1;  	if (synaptics_resolution(psmouse)) @@ -248,19 +411,50 @@ static int synaptics_query_hardware(struct psmouse *psmouse)  	return 0;  } -static int synaptics_set_absolute_mode(struct psmouse *psmouse) +static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) +{ +	static unsigned char param = 0xc8; +	struct synaptics_data *priv = psmouse->private; + +	if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || +	      SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))) +		return 0; + +	if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) +		return -1; + +	if (ps2_command(&psmouse->ps2dev, ¶m, PSMOUSE_CMD_SETRATE)) +		return -1; + +	/* Advanced gesture mode also sends multi finger data */ +	priv->capabilities |= BIT(1); + +	return 0; +} + +static int synaptics_set_mode(struct psmouse *psmouse)  {  	struct synaptics_data *priv = psmouse->private; -	priv->mode = SYN_BIT_ABSOLUTE_MODE; -	if (SYN_ID_MAJOR(priv->identity) >= 4) +	priv->mode = 0; +	if (priv->absolute_mode) +		priv->mode |= SYN_BIT_ABSOLUTE_MODE; +	if (priv->disable_gesture)  		priv->mode |= SYN_BIT_DISABLE_GESTURE; +	if (psmouse->rate >= 80) +		priv->mode |= SYN_BIT_HIGH_RATE;  	if (SYN_CAP_EXTENDED(priv->capabilities))  		priv->mode |= SYN_BIT_W_MODE;  	if (synaptics_mode_cmd(psmouse, priv->mode))  		return -1; +	if (priv->absolute_mode && +	    synaptics_set_advanced_gesture_mode(psmouse)) { +		psmouse_err(psmouse, "Advanced gesture mode init failed.\n"); +		return -1; +	} +  	return 0;  } @@ -348,7 +542,8 @@ static void synaptics_pt_activate(struct psmouse *psmouse)  			priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;  		if (synaptics_mode_cmd(psmouse, priv->mode)) -			printk(KERN_INFO "synaptics: failed to switch guest protocol\n"); +			psmouse_warn(psmouse, +				     "failed to switch guest protocol\n");  	}  } @@ -358,7 +553,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)  	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);  	if (!serio) { -		printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); +		psmouse_err(psmouse, +			    "not enough memory for pass-through port\n");  		return;  	} @@ -372,7 +568,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)  	psmouse->pt_activate = synaptics_pt_activate; -	printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys); +	psmouse_info(psmouse, "serio: %s port at %s\n", +		     serio->name, psmouse->phys);  	serio_register_port(serio);  } @@ -380,19 +577,51 @@ static void synaptics_pt_create(struct psmouse *psmouse)   *	Functions to interpret the absolute mode packets   ****************************************************************************/ -static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) +static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count, +				   int sgm, int agm) +{ +	state->count = count; +	state->sgm = sgm; +	state->agm = agm; +} + +static void synaptics_parse_agm(const unsigned char buf[], +				struct synaptics_data *priv, +				struct synaptics_hw_state *hw) +{ +	struct synaptics_hw_state *agm = &priv->agm; +	int agm_packet_type; + +	agm_packet_type = (buf[5] & 0x30) >> 4; +	switch (agm_packet_type) { +	case 1: +		/* Gesture packet: (x, y, z) half resolution */ +		agm->w = hw->w; +		agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1; +		agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1; +		agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1; +		break; + +	case 2: +		/* AGM-CONTACT packet: (count, sgm, agm) */ +		synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]); +		break; + +	default: +		break; +	} + +	/* Record that at least one AGM has been received since last SGM */ +	priv->agm_pending = true; +} + +static int synaptics_parse_hw_state(const unsigned char buf[], +				    struct synaptics_data *priv, +				    struct synaptics_hw_state *hw)  {  	memset(hw, 0, sizeof(struct synaptics_hw_state));  	if (SYN_MODEL_NEWABS(priv->model_id)) { -		hw->x = (((buf[3] & 0x10) << 8) | -			 ((buf[1] & 0x0f) << 8) | -			 buf[4]); -		hw->y = (((buf[3] & 0x20) << 7) | -			 ((buf[1] & 0xf0) << 4) | -			 buf[5]); - -		hw->z = buf[2];  		hw->w = (((buf[0] & 0x30) >> 2) |  			 ((buf[0] & 0x04) >> 1) |  			 ((buf[3] & 0x04) >> 2)); @@ -419,6 +648,21 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data  			hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;  		} +		if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || +			SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) && +		    hw->w == 2) { +			synaptics_parse_agm(buf, priv, hw); +			return 1; +		} + +		hw->x = (((buf[3] & 0x10) << 8) | +			 ((buf[1] & 0x0f) << 8) | +			 buf[4]); +		hw->y = (((buf[3] & 0x20) << 7) | +			 ((buf[1] & 0xf0) << 4) | +			 buf[5]); +		hw->z = buf[2]; +  		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&  		    ((buf[0] ^ buf[3]) & 0x02)) {  			switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { @@ -452,6 +696,460 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data  		hw->left  = (buf[0] & 0x01) ? 1 : 0;  		hw->right = (buf[0] & 0x02) ? 1 : 0;  	} + +	/* +	 * Convert wrap-around values to negative. (X|Y)_MAX_POSITIVE +	 * is used by some firmware to indicate a finger at the edge of +	 * the touchpad whose precise position cannot be determined, so +	 * convert these values to the maximum axis value. +	 */ +	if (hw->x > X_MAX_POSITIVE) +		hw->x -= 1 << ABS_POS_BITS; +	else if (hw->x == X_MAX_POSITIVE) +		hw->x = XMAX; + +	if (hw->y > Y_MAX_POSITIVE) +		hw->y -= 1 << ABS_POS_BITS; +	else if (hw->y == Y_MAX_POSITIVE) +		hw->y = YMAX; + +	return 0; +} + +static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot, +					  bool active, int x, int y) +{ +	input_mt_slot(dev, slot); +	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); +	if (active) { +		input_report_abs(dev, ABS_MT_POSITION_X, x); +		input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y)); +	} +} + +static void synaptics_report_semi_mt_data(struct input_dev *dev, +					  const struct synaptics_hw_state *a, +					  const struct synaptics_hw_state *b, +					  int num_fingers) +{ +	if (num_fingers >= 2) { +		synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x), +					      min(a->y, b->y)); +		synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x), +					      max(a->y, b->y)); +	} else if (num_fingers == 1) { +		synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y); +		synaptics_report_semi_mt_slot(dev, 1, false, 0, 0); +	} else { +		synaptics_report_semi_mt_slot(dev, 0, false, 0, 0); +		synaptics_report_semi_mt_slot(dev, 1, false, 0, 0); +	} +} + +static void synaptics_report_buttons(struct psmouse *psmouse, +				     const struct synaptics_hw_state *hw) +{ +	struct input_dev *dev = psmouse->dev; +	struct synaptics_data *priv = psmouse->private; +	int i; + +	input_report_key(dev, BTN_LEFT, hw->left); +	input_report_key(dev, BTN_RIGHT, hw->right); + +	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) +		input_report_key(dev, BTN_MIDDLE, hw->middle); + +	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { +		input_report_key(dev, BTN_FORWARD, hw->up); +		input_report_key(dev, BTN_BACK, hw->down); +	} + +	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) +		input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); +} + +static void synaptics_report_slot(struct input_dev *dev, int slot, +				  const struct synaptics_hw_state *hw) +{ +	input_mt_slot(dev, slot); +	input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL)); +	if (!hw) +		return; + +	input_report_abs(dev, ABS_MT_POSITION_X, hw->x); +	input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y)); +	input_report_abs(dev, ABS_MT_PRESSURE, hw->z); +} + +static void synaptics_report_mt_data(struct psmouse *psmouse, +				     struct synaptics_mt_state *mt_state, +				     const struct synaptics_hw_state *sgm) +{ +	struct input_dev *dev = psmouse->dev; +	struct synaptics_data *priv = psmouse->private; +	struct synaptics_hw_state *agm = &priv->agm; +	struct synaptics_mt_state *old = &priv->mt_state; + +	switch (mt_state->count) { +	case 0: +		synaptics_report_slot(dev, 0, NULL); +		synaptics_report_slot(dev, 1, NULL); +		break; +	case 1: +		if (mt_state->sgm == -1) { +			synaptics_report_slot(dev, 0, NULL); +			synaptics_report_slot(dev, 1, NULL); +		} else if (mt_state->sgm == 0) { +			synaptics_report_slot(dev, 0, sgm); +			synaptics_report_slot(dev, 1, NULL); +		} else { +			synaptics_report_slot(dev, 0, NULL); +			synaptics_report_slot(dev, 1, sgm); +		} +		break; +	default: +		/* +		 * If the finger slot contained in SGM is valid, and either +		 * hasn't changed, or is new, or the old SGM has now moved to +		 * AGM, then report SGM in MTB slot 0. +		 * Otherwise, empty MTB slot 0. +		 */ +		if (mt_state->sgm != -1 && +		    (mt_state->sgm == old->sgm || +		     old->sgm == -1 || mt_state->agm == old->sgm)) +			synaptics_report_slot(dev, 0, sgm); +		else +			synaptics_report_slot(dev, 0, NULL); + +		/* +		 * If the finger slot contained in AGM is valid, and either +		 * hasn't changed, or is new, then report AGM in MTB slot 1. +		 * Otherwise, empty MTB slot 1. +		 * +		 * However, in the case where the AGM is new, make sure that +		 * that it is either the same as the old SGM, or there was no +		 * SGM. +		 * +		 * Otherwise, if the SGM was just 1, and the new AGM is 2, then +		 * the new AGM will keep the old SGM's tracking ID, which can +		 * cause apparent drumroll.  This happens if in the following +		 * valid finger sequence: +		 * +		 *  Action                 SGM  AGM (MTB slot:Contact) +		 *  1. Touch contact 0    (0:0) +		 *  2. Touch contact 1    (0:0, 1:1) +		 *  3. Lift  contact 0    (1:1) +		 *  4. Touch contacts 2,3 (0:2, 1:3) +		 * +		 * In step 4, contact 3, in AGM must not be given the same +		 * tracking ID as contact 1 had in step 3.  To avoid this, +		 * the first agm with contact 3 is dropped and slot 1 is +		 * invalidated (tracking ID = -1). +		 */ +		if (mt_state->agm != -1 && +		    (mt_state->agm == old->agm || +		     (old->agm == -1 && +		      (old->sgm == -1 || mt_state->agm == old->sgm)))) +			synaptics_report_slot(dev, 1, agm); +		else +			synaptics_report_slot(dev, 1, NULL); +		break; +	} + +	/* Don't use active slot count to generate BTN_TOOL events. */ +	input_mt_report_pointer_emulation(dev, false); + +	/* Send the number of fingers reported by touchpad itself. */ +	input_mt_report_finger_count(dev, mt_state->count); + +	synaptics_report_buttons(psmouse, sgm); + +	input_sync(dev); +} + +/* Handle case where mt_state->count = 0 */ +static void synaptics_image_sensor_0f(struct synaptics_data *priv, +				      struct synaptics_mt_state *mt_state) +{ +	synaptics_mt_state_set(mt_state, 0, -1, -1); +	priv->mt_state_lost = false; +} + +/* Handle case where mt_state->count = 1 */ +static void synaptics_image_sensor_1f(struct synaptics_data *priv, +				      struct synaptics_mt_state *mt_state) +{ +	struct synaptics_hw_state *agm = &priv->agm; +	struct synaptics_mt_state *old = &priv->mt_state; + +	/* +	 * If the last AGM was (0,0,0), and there is only one finger left, +	 * then we absolutely know that SGM contains slot 0, and all other +	 * fingers have been removed. +	 */ +	if (priv->agm_pending && agm->z == 0) { +		synaptics_mt_state_set(mt_state, 1, 0, -1); +		priv->mt_state_lost = false; +		return; +	} + +	switch (old->count) { +	case 0: +		synaptics_mt_state_set(mt_state, 1, 0, -1); +		break; +	case 1: +		/* +		 * If mt_state_lost, then the previous transition was 3->1, +		 * and SGM now contains either slot 0 or 1, but we don't know +		 * which.  So, we just assume that the SGM now contains slot 1. +		 * +		 * If pending AGM and either: +		 *   (a) the previous SGM slot contains slot 0, or +		 *   (b) there was no SGM slot +		 * then, the SGM now contains slot 1 +		 * +		 * Case (a) happens with very rapid "drum roll" gestures, where +		 * slot 0 finger is lifted and a new slot 1 finger touches +		 * within one reporting interval. +		 * +		 * Case (b) happens if initially two or more fingers tap +		 * briefly, and all but one lift before the end of the first +		 * reporting interval. +		 * +		 * (In both these cases, slot 0 will becomes empty, so SGM +		 * contains slot 1 with the new finger) +		 * +		 * Else, if there was no previous SGM, it now contains slot 0. +		 * +		 * Otherwise, SGM still contains the same slot. +		 */ +		if (priv->mt_state_lost || +		    (priv->agm_pending && old->sgm <= 0)) +			synaptics_mt_state_set(mt_state, 1, 1, -1); +		else if (old->sgm == -1) +			synaptics_mt_state_set(mt_state, 1, 0, -1); +		break; +	case 2: +		/* +		 * If mt_state_lost, we don't know which finger SGM contains. +		 * +		 * So, report 1 finger, but with both slots empty. +		 * We will use slot 1 on subsequent 1->1 +		 */ +		if (priv->mt_state_lost) { +			synaptics_mt_state_set(mt_state, 1, -1, -1); +			break; +		} +		/* +		 * Since the last AGM was NOT (0,0,0), it was the finger in +		 * slot 0 that has been removed. +		 * So, SGM now contains previous AGM's slot, and AGM is now +		 * empty. +		 */ +		synaptics_mt_state_set(mt_state, 1, old->agm, -1); +		break; +	case 3: +		/* +		 * Since last AGM was not (0,0,0), we don't know which finger +		 * is left. +		 * +		 * So, report 1 finger, but with both slots empty. +		 * We will use slot 1 on subsequent 1->1 +		 */ +		synaptics_mt_state_set(mt_state, 1, -1, -1); +		priv->mt_state_lost = true; +		break; +	case 4: +	case 5: +		/* mt_state was updated by AGM-CONTACT packet */ +		break; +	} +} + +/* Handle case where mt_state->count = 2 */ +static void synaptics_image_sensor_2f(struct synaptics_data *priv, +				      struct synaptics_mt_state *mt_state) +{ +	struct synaptics_mt_state *old = &priv->mt_state; + +	switch (old->count) { +	case 0: +		synaptics_mt_state_set(mt_state, 2, 0, 1); +		break; +	case 1: +		/* +		 * If previous SGM contained slot 1 or higher, SGM now contains +		 * slot 0 (the newly touching finger) and AGM contains SGM's +		 * previous slot. +		 * +		 * Otherwise, SGM still contains slot 0 and AGM now contains +		 * slot 1. +		 */ +		if (old->sgm >= 1) +			synaptics_mt_state_set(mt_state, 2, 0, old->sgm); +		else +			synaptics_mt_state_set(mt_state, 2, 0, 1); +		break; +	case 2: +		/* +		 * If mt_state_lost, SGM now contains either finger 1 or 2, but +		 * we don't know which. +		 * So, we just assume that the SGM contains slot 0 and AGM 1. +		 */ +		if (priv->mt_state_lost) +			synaptics_mt_state_set(mt_state, 2, 0, 1); +		/* +		 * Otherwise, use the same mt_state, since it either hasn't +		 * changed, or was updated by a recently received AGM-CONTACT +		 * packet. +		 */ +		break; +	case 3: +		/* +		 * 3->2 transitions have two unsolvable problems: +		 *  1) no indication is given which finger was removed +		 *  2) no way to tell if agm packet was for finger 3 +		 *     before 3->2, or finger 2 after 3->2. +		 * +		 * So, report 2 fingers, but empty all slots. +		 * We will guess slots [0,1] on subsequent 2->2. +		 */ +		synaptics_mt_state_set(mt_state, 2, -1, -1); +		priv->mt_state_lost = true; +		break; +	case 4: +	case 5: +		/* mt_state was updated by AGM-CONTACT packet */ +		break; +	} +} + +/* Handle case where mt_state->count = 3 */ +static void synaptics_image_sensor_3f(struct synaptics_data *priv, +				      struct synaptics_mt_state *mt_state) +{ +	struct synaptics_mt_state *old = &priv->mt_state; + +	switch (old->count) { +	case 0: +		synaptics_mt_state_set(mt_state, 3, 0, 2); +		break; +	case 1: +		/* +		 * If previous SGM contained slot 2 or higher, SGM now contains +		 * slot 0 (one of the newly touching fingers) and AGM contains +		 * SGM's previous slot. +		 * +		 * Otherwise, SGM now contains slot 0 and AGM contains slot 2. +		 */ +		if (old->sgm >= 2) +			synaptics_mt_state_set(mt_state, 3, 0, old->sgm); +		else +			synaptics_mt_state_set(mt_state, 3, 0, 2); +		break; +	case 2: +		/* +		 * If the AGM previously contained slot 3 or higher, then the +		 * newly touching finger is in the lowest available slot. +		 * +		 * If SGM was previously 1 or higher, then the new SGM is +		 * now slot 0 (with a new finger), otherwise, the new finger +		 * is now in a hidden slot between 0 and AGM's slot. +		 * +		 * In all such cases, the SGM now contains slot 0, and the AGM +		 * continues to contain the same slot as before. +		 */ +		if (old->agm >= 3) { +			synaptics_mt_state_set(mt_state, 3, 0, old->agm); +			break; +		} + +		/* +		 * After some 3->1 and all 3->2 transitions, we lose track +		 * of which slot is reported by SGM and AGM. +		 * +		 * For 2->3 in this state, report 3 fingers, but empty all +		 * slots, and we will guess (0,2) on a subsequent 0->3. +		 * +		 * To userspace, the resulting transition will look like: +		 *    2:[0,1] -> 3:[-1,-1] -> 3:[0,2] +		 */ +		if (priv->mt_state_lost) { +			synaptics_mt_state_set(mt_state, 3, -1, -1); +			break; +		} + +		/* +		 * If the (SGM,AGM) really previously contained slots (0, 1), +		 * then we cannot know what slot was just reported by the AGM, +		 * because the 2->3 transition can occur either before or after +		 * the AGM packet. Thus, this most recent AGM could contain +		 * either the same old slot 1 or the new slot 2. +		 * Subsequent AGMs will be reporting slot 2. +		 * +		 * To userspace, the resulting transition will look like: +		 *    2:[0,1] -> 3:[0,-1] -> 3:[0,2] +		 */ +		synaptics_mt_state_set(mt_state, 3, 0, -1); +		break; +	case 3: +		/* +		 * If, for whatever reason, the previous agm was invalid, +		 * Assume SGM now contains slot 0, AGM now contains slot 2. +		 */ +		if (old->agm <= 2) +			synaptics_mt_state_set(mt_state, 3, 0, 2); +		/* +		 * mt_state either hasn't changed, or was updated by a recently +		 * received AGM-CONTACT packet. +		 */ +		break; + +	case 4: +	case 5: +		/* mt_state was updated by AGM-CONTACT packet */ +		break; +	} +} + +/* Handle case where mt_state->count = 4, or = 5 */ +static void synaptics_image_sensor_45f(struct synaptics_data *priv, +				       struct synaptics_mt_state *mt_state) +{ +	/* mt_state was updated correctly by AGM-CONTACT packet */ +	priv->mt_state_lost = false; +} + +static void synaptics_image_sensor_process(struct psmouse *psmouse, +					   struct synaptics_hw_state *sgm) +{ +	struct synaptics_data *priv = psmouse->private; +	struct synaptics_hw_state *agm = &priv->agm; +	struct synaptics_mt_state mt_state; + +	/* Initialize using current mt_state (as updated by last agm) */ +	mt_state = agm->mt_state; + +	/* +	 * Update mt_state using the new finger count and current mt_state. +	 */ +	if (sgm->z == 0) +		synaptics_image_sensor_0f(priv, &mt_state); +	else if (sgm->w >= 4) +		synaptics_image_sensor_1f(priv, &mt_state); +	else if (sgm->w == 0) +		synaptics_image_sensor_2f(priv, &mt_state); +	else if (sgm->w == 1 && mt_state.count <= 3) +		synaptics_image_sensor_3f(priv, &mt_state); +	else +		synaptics_image_sensor_45f(priv, &mt_state); + +	/* Send resulting input events to user space */ +	synaptics_report_mt_data(psmouse, &mt_state, sgm); + +	/* Store updated mt_state */ +	priv->mt_state = agm->mt_state = mt_state; +	priv->agm_pending = false;  }  /* @@ -464,9 +1162,14 @@ static void synaptics_process_packet(struct psmouse *psmouse)  	struct synaptics_hw_state hw;  	int num_fingers;  	int finger_width; -	int i; -	synaptics_parse_hw_state(psmouse->packet, priv, &hw); +	if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) +		return; + +	if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { +		synaptics_image_sensor_process(psmouse, &hw); +		return; +	}  	if (hw.scroll) {  		priv->scroll += hw.scroll; @@ -488,7 +1191,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)  		return;  	} -	if (hw.z > 0) { +	if (hw.z > 0 && hw.x > 1) {  		num_fingers = 1;  		finger_width = 5;  		if (SYN_CAP_EXTENDED(priv->capabilities)) { @@ -512,6 +1215,10 @@ static void synaptics_process_packet(struct psmouse *psmouse)  		finger_width = 0;  	} +	if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) +		synaptics_report_semi_mt_data(dev, &hw, &priv->agm, +					      num_fingers); +  	/* Post events  	 * BTN_TOUCH has to be first as mousedev relies on it when doing  	 * absolute -> relative conversion @@ -519,9 +1226,9 @@ static void synaptics_process_packet(struct psmouse *psmouse)  	if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);  	if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); -	if (hw.z > 0) { +	if (num_fingers > 0) {  		input_report_abs(dev, ABS_X, hw.x); -		input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y); +		input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y));  	}  	input_report_abs(dev, ABS_PRESSURE, hw.z); @@ -529,35 +1236,25 @@ static void synaptics_process_packet(struct psmouse *psmouse)  		input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);  	input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); -	input_report_key(dev, BTN_LEFT, hw.left); -	input_report_key(dev, BTN_RIGHT, hw.right); -  	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {  		input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);  		input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);  	} -	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) -		input_report_key(dev, BTN_MIDDLE, hw.middle); - -	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { -		input_report_key(dev, BTN_FORWARD, hw.up); -		input_report_key(dev, BTN_BACK, hw.down); -	} - -	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) -		input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i)); +	synaptics_report_buttons(psmouse, &hw);  	input_sync(dev);  } -static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type) +static int synaptics_validate_byte(struct psmouse *psmouse, +				   int idx, unsigned char pkt_type)  {  	static const unsigned char newabs_mask[]	= { 0xC8, 0x00, 0x00, 0xC8, 0x00 };  	static const unsigned char newabs_rel_mask[]	= { 0xC0, 0x00, 0x00, 0xC0, 0x00 };  	static const unsigned char newabs_rslt[]	= { 0x80, 0x00, 0x00, 0xC0, 0x00 };  	static const unsigned char oldabs_mask[]	= { 0xC0, 0x60, 0x00, 0xC0, 0x60 };  	static const unsigned char oldabs_rslt[]	= { 0xC0, 0x00, 0x00, 0x80, 0x00 }; +	const char *packet = psmouse->packet;  	if (idx < 0 || idx > 4)  		return 0; @@ -575,7 +1272,7 @@ static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned cha  		return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];  	default: -		printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); +		psmouse_err(psmouse, "unknown packet type %d\n", pkt_type);  		return 0;  	}  } @@ -585,8 +1282,8 @@ static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)  	int i;  	for (i = 0; i < 5; i++) -		if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) { -			printk(KERN_INFO "synaptics: using relaxed packet validation\n"); +		if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) { +			psmouse_info(psmouse, "using relaxed packet validation\n");  			return SYN_NEWABS_RELAXED;  		} @@ -611,41 +1308,87 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)  		return PSMOUSE_FULL_PACKET;  	} -	return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ? +	return synaptics_validate_byte(psmouse, psmouse->pktcnt - 1, priv->pkt_type) ?  		PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;  }  /*****************************************************************************   *	Driver initialization/cleanup functions   ****************************************************************************/ -static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) +static void set_abs_position_params(struct input_dev *dev, +				    struct synaptics_data *priv, int x_code, +				    int y_code)  { +	int x_min = priv->x_min ?: XMIN_NOMINAL; +	int x_max = priv->x_max ?: XMAX_NOMINAL; +	int y_min = priv->y_min ?: YMIN_NOMINAL; +	int y_max = priv->y_max ?: YMAX_NOMINAL; +	int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ? +			SYN_REDUCED_FILTER_FUZZ : 0; + +	input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0); +	input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0); +	input_abs_set_res(dev, x_code, priv->x_res); +	input_abs_set_res(dev, y_code, priv->y_res); +} + +static void set_input_params(struct psmouse *psmouse, +			     struct synaptics_data *priv) +{ +	struct input_dev *dev = psmouse->dev;  	int i; +	/* Things that apply to both modes */ +	__set_bit(INPUT_PROP_POINTER, dev->propbit); +	__set_bit(EV_KEY, dev->evbit); +	__set_bit(BTN_LEFT, dev->keybit); +	__set_bit(BTN_RIGHT, dev->keybit); + +	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) +		__set_bit(BTN_MIDDLE, dev->keybit); + +	if (!priv->absolute_mode) { +		/* Relative mode */ +		__set_bit(EV_REL, dev->evbit); +		__set_bit(REL_X, dev->relbit); +		__set_bit(REL_Y, dev->relbit); +		return; +	} + +	/* Absolute mode */  	__set_bit(EV_ABS, dev->evbit); -	input_set_abs_params(dev, ABS_X, -			     XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0); -	input_set_abs_params(dev, ABS_Y, -			     YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0); +	set_abs_position_params(dev, priv, ABS_X, ABS_Y);  	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); +	if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { +		set_abs_position_params(dev, priv, ABS_MT_POSITION_X, +					ABS_MT_POSITION_Y); +		/* Image sensors can report per-contact pressure */ +		input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); +		input_mt_init_slots(dev, 2, INPUT_MT_POINTER); + +		/* Image sensors can signal 4 and 5 finger clicks */ +		__set_bit(BTN_TOOL_QUADTAP, dev->keybit); +		__set_bit(BTN_TOOL_QUINTTAP, dev->keybit); +	} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { +		/* Non-image sensors with AGM use semi-mt */ +		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit); +		input_mt_init_slots(dev, 2, 0); +		set_abs_position_params(dev, priv, ABS_MT_POSITION_X, +					ABS_MT_POSITION_Y); +	} +  	if (SYN_CAP_PALMDETECT(priv->capabilities))  		input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); -	__set_bit(EV_KEY, dev->evbit);  	__set_bit(BTN_TOUCH, dev->keybit);  	__set_bit(BTN_TOOL_FINGER, dev->keybit); -	__set_bit(BTN_LEFT, dev->keybit); -	__set_bit(BTN_RIGHT, dev->keybit);  	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {  		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);  		__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);  	} -	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) -		__set_bit(BTN_MIDDLE, dev->keybit); -  	if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||  	    SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {  		__set_bit(BTN_FORWARD, dev->keybit); @@ -659,20 +1402,68 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)  	__clear_bit(REL_X, dev->relbit);  	__clear_bit(REL_Y, dev->relbit); -	input_abs_set_res(dev, ABS_X, priv->x_res); -	input_abs_set_res(dev, ABS_Y, priv->y_res); -  	if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { +		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); +		if (matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) +			__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);  		/* Clickpads report only left button */  		__clear_bit(BTN_RIGHT, dev->keybit);  		__clear_bit(BTN_MIDDLE, dev->keybit);  	}  } +static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse, +					      void *data, char *buf) +{ +	struct synaptics_data *priv = psmouse->private; + +	return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0'); +} + +static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse, +					     void *data, const char *buf, +					     size_t len) +{ +	struct synaptics_data *priv = psmouse->private; +	unsigned int value; +	int err; + +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err; + +	if (value > 1) +		return -EINVAL; + +	if (value == priv->disable_gesture) +		return len; + +	priv->disable_gesture = value; +	if (value) +		priv->mode |= SYN_BIT_DISABLE_GESTURE; +	else +		priv->mode &= ~SYN_BIT_DISABLE_GESTURE; + +	if (synaptics_mode_cmd(psmouse, priv->mode)) +		return -EIO; + +	return len; +} + +PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL, +		    synaptics_show_disable_gesture, +		    synaptics_set_disable_gesture); +  static void synaptics_disconnect(struct psmouse *psmouse)  { +	struct synaptics_data *priv = psmouse->private; + +	if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) +		device_remove_file(&psmouse->ps2dev.serio->dev, +				   &psmouse_attr_disable_gesture.dattr); +  	synaptics_reset(psmouse); -	kfree(psmouse->private); +	kfree(priv);  	psmouse->private = NULL;  } @@ -680,25 +1471,52 @@ static int synaptics_reconnect(struct psmouse *psmouse)  {  	struct synaptics_data *priv = psmouse->private;  	struct synaptics_data old_priv = *priv; +	unsigned char param[2]; +	int retry = 0; +	int error; -	psmouse_reset(psmouse); +	do { +		psmouse_reset(psmouse); +		if (retry) { +			/* +			 * On some boxes, right after resuming, the touchpad +			 * needs some time to finish initializing (I assume +			 * it needs time to calibrate) and start responding +			 * to Synaptics-specific queries, so let's wait a +			 * bit. +			 */ +			ssleep(1); +		} +		ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETID); +		error = synaptics_detect(psmouse, 0); +	} while (error && ++retry < 3); -	if (synaptics_detect(psmouse, 0)) +	if (error)  		return -1; +	if (retry > 1) +		psmouse_dbg(psmouse, "reconnected after %d tries\n", retry); +  	if (synaptics_query_hardware(psmouse)) { -		printk(KERN_ERR "Unable to query Synaptics hardware.\n"); +		psmouse_err(psmouse, "Unable to query device.\n"); +		return -1; +	} + +	if (synaptics_set_mode(psmouse)) { +		psmouse_err(psmouse, "Unable to initialize device.\n");  		return -1;  	}  	if (old_priv.identity != priv->identity ||  	    old_priv.model_id != priv->model_id ||  	    old_priv.capabilities != priv->capabilities || -	    old_priv.ext_cap != priv->ext_cap) -		return -1; - -	if (synaptics_set_absolute_mode(psmouse)) { -		printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); +	    old_priv.ext_cap != priv->ext_cap) { +		psmouse_err(psmouse, +			    "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n", +			    old_priv.identity, priv->identity, +			    old_priv.model_id, priv->model_id, +			    old_priv.capabilities, priv->capabilities, +			    old_priv.ext_cap, priv->ext_cap);  		return -1;  	} @@ -707,7 +1525,7 @@ static int synaptics_reconnect(struct psmouse *psmouse)  static bool impaired_toshiba_kbc; -static const struct dmi_system_id __initconst toshiba_dmi_table[] = { +static const struct dmi_system_id toshiba_dmi_table[] __initconst = {  #if defined(CONFIG_DMI) && defined(CONFIG_X86)  	{  		/* Toshiba Satellite */ @@ -740,18 +1558,47 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = {  		},  	}, +#endif  	{ } +}; + +static bool broken_olpc_ec; + +static const struct dmi_system_id olpc_dmi_table[] __initconst = { +#if defined(CONFIG_DMI) && defined(CONFIG_OLPC) +	{ +		/* OLPC XO-1 or XO-1.5 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "OLPC"), +			DMI_MATCH(DMI_PRODUCT_NAME, "XO"), +		}, +	},  #endif +	{ }  };  void __init synaptics_module_init(void)  {  	impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); +	broken_olpc_ec = dmi_check_system(olpc_dmi_table);  } -int synaptics_init(struct psmouse *psmouse) +static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)  {  	struct synaptics_data *priv; +	int err = -1; + +	/* +	 * The OLPC XO has issues with Synaptics' absolute mode; the constant +	 * packet spew overloads the EC such that key presses on the keyboard +	 * are missed.  Given that, don't even attempt to use Absolute mode. +	 * Relative mode seems to work just fine. +	 */ +	if (absolute_mode && broken_olpc_ec) { +		psmouse_info(psmouse, +			     "OLPC XO detected, not enabling Synaptics protocol.\n"); +		return -ENODEV; +	}  	psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);  	if (!priv) @@ -760,23 +1607,30 @@ int synaptics_init(struct psmouse *psmouse)  	psmouse_reset(psmouse);  	if (synaptics_query_hardware(psmouse)) { -		printk(KERN_ERR "Unable to query Synaptics hardware.\n"); +		psmouse_err(psmouse, "Unable to query device.\n");  		goto init_fail;  	} -	if (synaptics_set_absolute_mode(psmouse)) { -		printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); +	priv->absolute_mode = absolute_mode; +	if (SYN_ID_DISGEST_SUPPORTED(priv->identity)) +		priv->disable_gesture = true; + +	if (synaptics_set_mode(psmouse)) { +		psmouse_err(psmouse, "Unable to initialize device.\n");  		goto init_fail;  	}  	priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; -	printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n", -		SYN_ID_MODEL(priv->identity), -		SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), -		priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c); +	psmouse_info(psmouse, +		     "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n", +		     SYN_ID_MODEL(priv->identity), +		     SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), +		     priv->model_id, +		     priv->capabilities, priv->ext_cap, priv->ext_cap_0c, +		     priv->board_id, priv->firmware_id); -	set_input_params(psmouse->dev, priv); +	set_input_params(psmouse, priv);  	/*  	 * Encode touchpad model so that it can be used to set @@ -788,12 +1642,19 @@ int synaptics_init(struct psmouse *psmouse)  	psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |  			  (priv->model_id & 0x000000ff); -	psmouse->protocol_handler = synaptics_process_byte; +	if (absolute_mode) { +		psmouse->protocol_handler = synaptics_process_byte; +		psmouse->pktsize = 6; +	} else { +		/* Relative mode follows standard PS/2 mouse protocol */ +		psmouse->protocol_handler = psmouse_process_byte; +		psmouse->pktsize = 3; +	} +  	psmouse->set_rate = synaptics_set_rate;  	psmouse->disconnect = synaptics_disconnect;  	psmouse->reconnect = synaptics_reconnect;  	psmouse->cleanup = synaptics_reset; -	psmouse->pktsize = 6;  	/* Synaptics can usually stay in sync without extra help */  	psmouse->resync_time = 0; @@ -802,20 +1663,42 @@ int synaptics_init(struct psmouse *psmouse)  	/*  	 * Toshiba's KBC seems to have trouble handling data from -	 * Synaptics as full rate, switch to lower rate which is roughly -	 * thye same as rate of standard PS/2 mouse. +	 * Synaptics at full rate.  Switch to a lower rate (roughly +	 * the same rate as a standard PS/2 mouse).  	 */  	if (psmouse->rate >= 80 && impaired_toshiba_kbc) { -		printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", -			dmi_get_system_info(DMI_PRODUCT_NAME)); +		psmouse_info(psmouse, +			     "Toshiba %s detected, limiting rate to 40pps.\n", +			     dmi_get_system_info(DMI_PRODUCT_NAME));  		psmouse->rate = 40;  	} +	if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) { +		err = device_create_file(&psmouse->ps2dev.serio->dev, +					 &psmouse_attr_disable_gesture.dattr); +		if (err) { +			psmouse_err(psmouse, +				    "Failed to create disable_gesture attribute (%d)", +				    err); +			goto init_fail; +		} +	} +  	return 0;   init_fail:  	kfree(priv); -	return -1; +	return err; +} + +int synaptics_init(struct psmouse *psmouse) +{ +	return __synaptics_init(psmouse, true); +} + +int synaptics_init_relative(struct psmouse *psmouse) +{ +	return __synaptics_init(psmouse, false);  }  bool synaptics_supported(void) @@ -840,4 +1723,3 @@ bool synaptics_supported(void)  }  #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ - diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 613a3652f98..e594af0b264 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -18,8 +18,10 @@  #define SYN_QUE_SERIAL_NUMBER_SUFFIX	0x07  #define SYN_QUE_RESOLUTION		0x08  #define SYN_QUE_EXT_CAPAB		0x09 +#define SYN_QUE_FIRMWARE_ID		0x0a  #define SYN_QUE_EXT_CAPAB_0C		0x0c -#define SYN_QUE_EXT_DIMENSIONS		0x0d +#define SYN_QUE_EXT_MAX_COORDS		0x0d +#define SYN_QUE_EXT_MIN_COORDS		0x0f  /* synatics modes */  #define SYN_BIT_ABSOLUTE_MODE		(1 << 7) @@ -51,8 +53,39 @@  #define SYN_EXT_CAP_REQUESTS(c)		(((c) & 0x700000) >> 20)  #define SYN_CAP_MULTI_BUTTON_NO(ec)	(((ec) & 0x00f000) >> 12)  #define SYN_CAP_PRODUCT_ID(ec)		(((ec) & 0xff0000) >> 16) -#define SYN_CAP_CLICKPAD(ex0c)		((ex0c) & 0x100100) + +/* + * The following describes response for the 0x0c query. + * + * byte	mask	name			meaning + * ----	----	-------			------------ + * 1	0x01	adjustable threshold	capacitive button sensitivity + *					can be adjusted + * 1	0x02	report max		query 0x0d gives max coord reported + * 1	0x04	clearpad		sensor is ClearPad product + * 1	0x08	advanced gesture	not particularly meaningful + * 1	0x10	clickpad bit 0		1-button ClickPad + * 1	0x60	multifinger mode	identifies firmware finger counting + *					(not reporting!) algorithm. + *					Not particularly meaningful + * 1	0x80	covered pad		W clipped to 14, 15 == pad mostly covered + * 2	0x01	clickpad bit 1		2-button ClickPad + * 2	0x02	deluxe LED controls	touchpad support LED commands + *					ala multimedia control bar + * 2	0x04	reduced filtering	firmware does less filtering on + *					position data, driver should watch + *					for noise. + * 2	0x08	image sensor		image sensor tracks 5 fingers, but only + *					reports 2. + * 2	0x20	report min		query 0x0f gives min coord reported + */ +#define SYN_CAP_CLICKPAD(ex0c)		((ex0c) & 0x100000) /* 1-button ClickPad */ +#define SYN_CAP_CLICKPAD2BTN(ex0c)	((ex0c) & 0x000100) /* 2-button ClickPad */  #define SYN_CAP_MAX_DIMENSIONS(ex0c)	((ex0c) & 0x020000) +#define SYN_CAP_MIN_DIMENSIONS(ex0c)	((ex0c) & 0x002000) +#define SYN_CAP_ADV_GESTURE(ex0c)	((ex0c) & 0x080000) +#define SYN_CAP_REDUCED_FILTERING(ex0c)	((ex0c) & 0x000400) +#define SYN_CAP_IMAGE_SENSOR(ex0c)	((ex0c) & 0x000800)  /* synaptics modes query bits */  #define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7)) @@ -68,6 +101,7 @@  #define SYN_ID_MINOR(i)			(((i) >> 16) & 0xff)  #define SYN_ID_FULL(i)			((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i))  #define SYN_ID_IS_SYNAPTICS(i)		((((i) >> 8) & 0xff) == 0x47) +#define SYN_ID_DISGEST_SUPPORTED(i)	(SYN_ID_MAJOR(i) >= 4)  /* synaptics special commands */  #define SYN_PS_SET_MODE2		0x14 @@ -79,10 +113,22 @@  #define SYN_NEWABS_RELAXED		2  #define SYN_OLDABS			3 +/* amount to fuzz position data when touchpad reports reduced filtering */ +#define SYN_REDUCED_FILTER_FUZZ		8 +  /* - * A structure to describe the state of the touchpad hardware (buttons and pad) + * A structure to describe which internal touchpad finger slots are being + * reported in raw packets.   */ +struct synaptics_mt_state { +	int count;			/* num fingers being tracked */ +	int sgm;			/* which slot is reported by sgm pkt */ +	int agm;			/* which slot is reported by agm pkt*/ +}; +/* + * A structure to describe the state of the touchpad hardware (buttons and pad) + */  struct synaptics_hw_state {  	int x;  	int y; @@ -95,28 +141,48 @@ struct synaptics_hw_state {  	unsigned int down:1;  	unsigned char ext_buttons;  	signed char scroll; + +	/* As reported in last AGM-CONTACT packets */ +	struct synaptics_mt_state mt_state;  };  struct synaptics_data {  	/* Data read from the touchpad */  	unsigned long int model_id;		/* Model-ID */ +	unsigned long int firmware_id;		/* Firmware-ID */ +	unsigned long int board_id;		/* Board-ID */  	unsigned long int capabilities;		/* Capabilities */  	unsigned long int ext_cap;		/* Extended Capabilities */  	unsigned long int ext_cap_0c;		/* Ext Caps from 0x0c query */  	unsigned long int identity;		/* Identification */  	unsigned int x_res, y_res;		/* X/Y resolution in units/mm */ -	unsigned int x_max, y_max;		/* Max dimensions (from FW) */ +	unsigned int x_max, y_max;		/* Max coordinates (from FW) */ +	unsigned int x_min, y_min;		/* Min coordinates (from FW) */  	unsigned char pkt_type;			/* packet type - old, new, etc */  	unsigned char mode;			/* current mode byte */  	int scroll; +	bool absolute_mode;			/* run in Absolute mode */ +	bool disable_gesture;			/* disable gestures */ +  	struct serio *pt_port;			/* Pass-through serio port */ + +	struct synaptics_mt_state mt_state;	/* Current mt finger state */ +	bool mt_state_lost;			/* mt_state may be incorrect */ + +	/* +	 * Last received Advanced Gesture Mode (AGM) packet. An AGM packet +	 * contains position data for a second contact, at half resolution. +	 */ +	struct synaptics_hw_state agm; +	bool agm_pending;			/* new AGM packet received */  };  void synaptics_module_init(void);  int synaptics_detect(struct psmouse *psmouse, bool set_properties);  int synaptics_init(struct psmouse *psmouse); +int synaptics_init_relative(struct psmouse *psmouse);  void synaptics_reset(struct psmouse *psmouse);  bool synaptics_supported(void); diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 0ae62f0bcb3..ad822608f6e 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -18,6 +18,7 @@  #include <linux/delay.h>  #include <linux/workqueue.h>  #include <linux/slab.h> +#include <linux/pm.h>  #define DRIVER_NAME		"synaptics_i2c"  /* maximum product id is 15 characters */ @@ -184,17 +185,17 @@  #define NO_DATA_SLEEP_MSECS	(MSEC_PER_SEC / 4)  /* Control touchpad's No Deceleration option */ -static int no_decel = 1; +static bool no_decel = 1;  module_param(no_decel, bool, 0644);  MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)");  /* Control touchpad's Reduced Reporting option */ -static int reduce_report; +static bool reduce_report;  module_param(reduce_report, bool, 0644);  MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)");  /* Control touchpad's No Filter option */ -static int no_filter; +static bool no_filter;  module_param(no_filter, bool, 0644);  MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); @@ -375,12 +376,7 @@ static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,  	spin_lock_irqsave(&touch->lock, flags); -	/* -	 * If work is already scheduled then subsequent schedules will not -	 * change the scheduled time that's why we have to cancel it first. -	 */ -	__cancel_delayed_work(&touch->dwork); -	schedule_delayed_work(&touch->dwork, delay); +	mod_delayed_work(system_wq, &touch->dwork, delay);  	spin_unlock_irqrestore(&touch->lock, flags);  } @@ -461,7 +457,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work)  	 * While interrupt driven, there is no real need to poll the device.  	 * But touchpads are very sensitive, so there could be errors  	 * related to physical environment and the attention line isn't -	 * neccesarily asserted. In such case we can lose the touchpad. +	 * necessarily asserted. In such case we can lose the touchpad.  	 * We poll the device once in THREAD_IRQ_SLEEP_SECS and  	 * if error is detected, we try to reset and reconfigure the touchpad.  	 */ @@ -539,7 +535,7 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien  	return touch;  } -static int __devinit synaptics_i2c_probe(struct i2c_client *client, +static int synaptics_i2c_probe(struct i2c_client *client,  			       const struct i2c_device_id *dev_id)  {  	int ret; @@ -569,7 +565,7 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client,  			 "Requesting IRQ: %d\n", touch->client->irq);  		ret = request_irq(touch->client->irq, synaptics_i2c_irq, -				  IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING, +				  IRQ_TYPE_EDGE_FALLING,  				  DRIVER_NAME, touch);  		if (ret) {  			dev_warn(&touch->client->dev, @@ -605,7 +601,7 @@ err_mem_free:  	return ret;  } -static int __devexit synaptics_i2c_remove(struct i2c_client *client) +static int synaptics_i2c_remove(struct i2c_client *client)  {  	struct synaptics_i2c *touch = i2c_get_clientdata(client); @@ -618,9 +614,10 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM -static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int synaptics_i2c_suspend(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct synaptics_i2c *touch = i2c_get_clientdata(client);  	cancel_delayed_work_sync(&touch->dwork); @@ -631,9 +628,10 @@ static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg)  	return 0;  } -static int synaptics_i2c_resume(struct i2c_client *client) +static int synaptics_i2c_resume(struct device *dev)  {  	int ret; +	struct i2c_client *client = to_i2c_client(dev);  	struct synaptics_i2c *touch = i2c_get_clientdata(client);  	ret = synaptics_i2c_reset_config(client); @@ -645,11 +643,11 @@ static int synaptics_i2c_resume(struct i2c_client *client)  	return 0;  } -#else -#define synaptics_i2c_suspend	NULL -#define synaptics_i2c_resume	NULL  #endif +static SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend, +			 synaptics_i2c_resume); +  static const struct i2c_device_id synaptics_i2c_id_table[] = {  	{ "synaptics_i2c", 0 },  	{ }, @@ -660,28 +658,16 @@ static struct i2c_driver synaptics_i2c_driver = {  	.driver = {  		.name	= DRIVER_NAME,  		.owner	= THIS_MODULE, +		.pm	= &synaptics_i2c_pm,  	},  	.probe		= synaptics_i2c_probe, -	.remove		= __devexit_p(synaptics_i2c_remove), +	.remove		= synaptics_i2c_remove, -	.suspend	= synaptics_i2c_suspend, -	.resume		= synaptics_i2c_resume,  	.id_table	= synaptics_i2c_id_table,  }; -static int __init synaptics_i2c_init(void) -{ -	return i2c_add_driver(&synaptics_i2c_driver); -} - -static void __exit synaptics_i2c_exit(void) -{ -	i2c_del_driver(&synaptics_i2c_driver); -} - -module_init(synaptics_i2c_init); -module_exit(synaptics_i2c_exit); +module_i2c_driver(synaptics_i2c_driver);  MODULE_DESCRIPTION("Synaptics I2C touchpad driver");  MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c new file mode 100644 index 00000000000..e122bda16aa --- /dev/null +++ b/drivers/input/mouse/synaptics_usb.c @@ -0,0 +1,556 @@ +/* + * USB Synaptics device driver + * + *  Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk) + *  Copyright (c) 2003 Ron Lee (ron@debian.org) + *	cPad driver for kernel 2.4 + * + *  Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de) + *  Copyright (c) 2004 Ron Lee (ron@debian.org) + *	rewritten for kernel 2.6 + * + *  cPad display character device part is not included. It can be found at + *  http://jan-steinhoff.de/linux/synaptics-usb.html + * + * Bases on:	usb_skeleton.c v2.2 by Greg Kroah-Hartman + *		drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik + *		drivers/input/mouse/synaptics.c by Peter Osterlund + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Trademarks are the property of their respective owners. + */ + +/* + * There are three different types of Synaptics USB devices: Touchpads, + * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported + * by this driver, touchstick support has not been tested much yet, and + * touchscreens have not been tested at all. + * + * Up to three alternate settings are possible: + *	setting 0: one int endpoint for relative movement (used by usbhid.ko) + *	setting 1: one int endpoint for absolute finger position + *	setting 2 (cPad only): one int endpoint for absolute finger position and + *		   two bulk endpoints for the display (in/out) + * This driver uses setting 1. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/usb.h> +#include <linux/input.h> +#include <linux/usb/input.h> + +#define USB_VENDOR_ID_SYNAPTICS	0x06cb +#define USB_DEVICE_ID_SYNAPTICS_TP	0x0001	/* Synaptics USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_INT_TP	0x0002	/* Integrated USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_CPAD	0x0003	/* Synaptics cPad */ +#define USB_DEVICE_ID_SYNAPTICS_TS	0x0006	/* Synaptics TouchScreen */ +#define USB_DEVICE_ID_SYNAPTICS_STICK	0x0007	/* Synaptics USB Styk */ +#define USB_DEVICE_ID_SYNAPTICS_WP	0x0008	/* Synaptics USB WheelPad */ +#define USB_DEVICE_ID_SYNAPTICS_COMP_TP	0x0009	/* Composite USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_WTP	0x0010	/* Wireless TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_DPAD	0x0013	/* DisplayPad */ + +#define SYNUSB_TOUCHPAD			(1 << 0) +#define SYNUSB_STICK			(1 << 1) +#define SYNUSB_TOUCHSCREEN		(1 << 2) +#define SYNUSB_AUXDISPLAY		(1 << 3) /* For cPad */ +#define SYNUSB_COMBO			(1 << 4) /* Composite device (TP + stick) */ +#define SYNUSB_IO_ALWAYS		(1 << 5) + +#define USB_DEVICE_SYNAPTICS(prod, kind)		\ +	USB_DEVICE(USB_VENDOR_ID_SYNAPTICS,		\ +		   USB_DEVICE_ID_SYNAPTICS_##prod),	\ +	.driver_info = (kind), + +#define SYNUSB_RECV_SIZE	8 + +#define XMIN_NOMINAL		1472 +#define XMAX_NOMINAL		5472 +#define YMIN_NOMINAL		1408 +#define YMAX_NOMINAL		4448 + +struct synusb { +	struct usb_device *udev; +	struct usb_interface *intf; +	struct urb *urb; +	unsigned char *data; + +	/* input device related data structures */ +	struct input_dev *input; +	char name[128]; +	char phys[64]; + +	/* characteristics of the device */ +	unsigned long flags; +}; + +static void synusb_report_buttons(struct synusb *synusb) +{ +	struct input_dev *input_dev = synusb->input; + +	input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04); +	input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01); +	input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02); +} + +static void synusb_report_stick(struct synusb *synusb) +{ +	struct input_dev *input_dev = synusb->input; +	int x, y; +	unsigned int pressure; + +	pressure = synusb->data[6]; +	x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7; +	y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7; + +	if (pressure > 0) { +		input_report_rel(input_dev, REL_X, x); +		input_report_rel(input_dev, REL_Y, -y); +	} + +	input_report_abs(input_dev, ABS_PRESSURE, pressure); + +	synusb_report_buttons(synusb); + +	input_sync(input_dev); +} + +static void synusb_report_touchpad(struct synusb *synusb) +{ +	struct input_dev *input_dev = synusb->input; +	unsigned int num_fingers, tool_width; +	unsigned int x, y; +	unsigned int pressure, w; + +	pressure = synusb->data[6]; +	x = be16_to_cpup((__be16 *)&synusb->data[2]); +	y = be16_to_cpup((__be16 *)&synusb->data[4]); +	w = synusb->data[0] & 0x0f; + +	if (pressure > 0) { +		num_fingers = 1; +		tool_width = 5; +		switch (w) { +		case 0 ... 1: +			num_fingers = 2 + w; +			break; + +		case 2:	                /* pen, pretend its a finger */ +			break; + +		case 4 ... 15: +			tool_width = w; +			break; +		} +	} else { +		num_fingers = 0; +		tool_width = 0; +	} + +	/* +	 * Post events +	 * BTN_TOUCH has to be first as mousedev relies on it when doing +	 * absolute -> relative conversion +	 */ + +	if (pressure > 30) +		input_report_key(input_dev, BTN_TOUCH, 1); +	if (pressure < 25) +		input_report_key(input_dev, BTN_TOUCH, 0); + +	if (num_fingers > 0) { +		input_report_abs(input_dev, ABS_X, x); +		input_report_abs(input_dev, ABS_Y, +				 YMAX_NOMINAL + YMIN_NOMINAL - y); +	} + +	input_report_abs(input_dev, ABS_PRESSURE, pressure); +	input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width); + +	input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1); +	input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); +	input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); + +	synusb_report_buttons(synusb); +	if (synusb->flags & SYNUSB_AUXDISPLAY) +		input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08); + +	input_sync(input_dev); +} + +static void synusb_irq(struct urb *urb) +{ +	struct synusb *synusb = urb->context; +	int error; + +	/* Check our status in case we need to bail out early. */ +	switch (urb->status) { +	case 0: +		usb_mark_last_busy(synusb->udev); +		break; + +	/* Device went away so don't keep trying to read from it. */ +	case -ECONNRESET: +	case -ENOENT: +	case -ESHUTDOWN: +		return; + +	default: +		goto resubmit; +		break; +	} + +	if (synusb->flags & SYNUSB_STICK) +		synusb_report_stick(synusb); +	else +		synusb_report_touchpad(synusb); + +resubmit: +	error = usb_submit_urb(urb, GFP_ATOMIC); +	if (error && error != -EPERM) +		dev_err(&synusb->intf->dev, +			"%s - usb_submit_urb failed with result: %d", +			__func__, error); +} + +static struct usb_endpoint_descriptor * +synusb_get_in_endpoint(struct usb_host_interface *iface) +{ + +	struct usb_endpoint_descriptor *endpoint; +	int i; + +	for (i = 0; i < iface->desc.bNumEndpoints; ++i) { +		endpoint = &iface->endpoint[i].desc; + +		if (usb_endpoint_is_int_in(endpoint)) { +			/* we found our interrupt in endpoint */ +			return endpoint; +		} +	} + +	return NULL; +} + +static int synusb_open(struct input_dev *dev) +{ +	struct synusb *synusb = input_get_drvdata(dev); +	int retval; + +	retval = usb_autopm_get_interface(synusb->intf); +	if (retval) { +		dev_err(&synusb->intf->dev, +			"%s - usb_autopm_get_interface failed, error: %d\n", +			__func__, retval); +		return retval; +	} + +	retval = usb_submit_urb(synusb->urb, GFP_KERNEL); +	if (retval) { +		dev_err(&synusb->intf->dev, +			"%s - usb_submit_urb failed, error: %d\n", +			__func__, retval); +		retval = -EIO; +		goto out; +	} + +	synusb->intf->needs_remote_wakeup = 1; + +out: +	usb_autopm_put_interface(synusb->intf); +	return retval; +} + +static void synusb_close(struct input_dev *dev) +{ +	struct synusb *synusb = input_get_drvdata(dev); +	int autopm_error; + +	autopm_error = usb_autopm_get_interface(synusb->intf); + +	usb_kill_urb(synusb->urb); +	synusb->intf->needs_remote_wakeup = 0; + +	if (!autopm_error) +		usb_autopm_put_interface(synusb->intf); +} + +static int synusb_probe(struct usb_interface *intf, +			const struct usb_device_id *id) +{ +	struct usb_device *udev = interface_to_usbdev(intf); +	struct usb_endpoint_descriptor *ep; +	struct synusb *synusb; +	struct input_dev *input_dev; +	unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber; +	unsigned int altsetting = min(intf->num_altsetting, 1U); +	int error; + +	error = usb_set_interface(udev, intf_num, altsetting); +	if (error) { +		dev_err(&udev->dev, +			"Can not set alternate setting to %i, error: %i", +			altsetting, error); +		return error; +	} + +	ep = synusb_get_in_endpoint(intf->cur_altsetting); +	if (!ep) +		return -ENODEV; + +	synusb = kzalloc(sizeof(*synusb), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!synusb || !input_dev) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	synusb->udev = udev; +	synusb->intf = intf; +	synusb->input = input_dev; + +	synusb->flags = id->driver_info; +	if (synusb->flags & SYNUSB_COMBO) { +		/* +		 * This is a combo device, we need to set proper +		 * capability, depending on the interface. +		 */ +		synusb->flags |= intf_num == 1 ? +					SYNUSB_STICK : SYNUSB_TOUCHPAD; +	} + +	synusb->urb = usb_alloc_urb(0, GFP_KERNEL); +	if (!synusb->urb) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL, +					  &synusb->urb->transfer_dma); +	if (!synusb->data) { +		error = -ENOMEM; +		goto err_free_urb; +	} + +	usb_fill_int_urb(synusb->urb, udev, +			 usb_rcvintpipe(udev, ep->bEndpointAddress), +			 synusb->data, SYNUSB_RECV_SIZE, +			 synusb_irq, synusb, +			 ep->bInterval); +	synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + +	if (udev->manufacturer) +		strlcpy(synusb->name, udev->manufacturer, +			sizeof(synusb->name)); + +	if (udev->product) { +		if (udev->manufacturer) +			strlcat(synusb->name, " ", sizeof(synusb->name)); +		strlcat(synusb->name, udev->product, sizeof(synusb->name)); +	} + +	if (!strlen(synusb->name)) +		snprintf(synusb->name, sizeof(synusb->name), +			 "USB Synaptics Device %04x:%04x", +			 le16_to_cpu(udev->descriptor.idVendor), +			 le16_to_cpu(udev->descriptor.idProduct)); + +	if (synusb->flags & SYNUSB_STICK) +		strlcat(synusb->name, " (Stick)", sizeof(synusb->name)); + +	usb_make_path(udev, synusb->phys, sizeof(synusb->phys)); +	strlcat(synusb->phys, "/input0", sizeof(synusb->phys)); + +	input_dev->name = synusb->name; +	input_dev->phys = synusb->phys; +	usb_to_input_id(udev, &input_dev->id); +	input_dev->dev.parent = &synusb->intf->dev; + +	if (!(synusb->flags & SYNUSB_IO_ALWAYS)) { +		input_dev->open = synusb_open; +		input_dev->close = synusb_close; +	} + +	input_set_drvdata(input_dev, synusb); + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); + +	if (synusb->flags & SYNUSB_STICK) { +		__set_bit(EV_REL, input_dev->evbit); +		__set_bit(REL_X, input_dev->relbit); +		__set_bit(REL_Y, input_dev->relbit); +		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); +	} else { +		input_set_abs_params(input_dev, ABS_X, +				     XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); +		input_set_abs_params(input_dev, ABS_Y, +				     YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); +		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); +		input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); +		__set_bit(BTN_TOUCH, input_dev->keybit); +		__set_bit(BTN_TOOL_FINGER, input_dev->keybit); +		__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); +		__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); +	} + +	__set_bit(BTN_LEFT, input_dev->keybit); +	__set_bit(BTN_RIGHT, input_dev->keybit); +	__set_bit(BTN_MIDDLE, input_dev->keybit); + +	usb_set_intfdata(intf, synusb); + +	if (synusb->flags & SYNUSB_IO_ALWAYS) { +		error = synusb_open(input_dev); +		if (error) +			goto err_free_dma; +	} + +	error = input_register_device(input_dev); +	if (error) { +		dev_err(&udev->dev, +			"Failed to register input device, error %d\n", +			error); +		goto err_stop_io; +	} + +	return 0; + +err_stop_io: +	if (synusb->flags & SYNUSB_IO_ALWAYS) +		synusb_close(synusb->input); +err_free_dma: +	usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, +			  synusb->urb->transfer_dma); +err_free_urb: +	usb_free_urb(synusb->urb); +err_free_mem: +	input_free_device(input_dev); +	kfree(synusb); +	usb_set_intfdata(intf, NULL); + +	return error; +} + +static void synusb_disconnect(struct usb_interface *intf) +{ +	struct synusb *synusb = usb_get_intfdata(intf); +	struct usb_device *udev = interface_to_usbdev(intf); + +	if (synusb->flags & SYNUSB_IO_ALWAYS) +		synusb_close(synusb->input); + +	input_unregister_device(synusb->input); + +	usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, +			  synusb->urb->transfer_dma); +	usb_free_urb(synusb->urb); +	kfree(synusb); + +	usb_set_intfdata(intf, NULL); +} + +static int synusb_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct synusb *synusb = usb_get_intfdata(intf); +	struct input_dev *input_dev = synusb->input; + +	mutex_lock(&input_dev->mutex); +	usb_kill_urb(synusb->urb); +	mutex_unlock(&input_dev->mutex); + +	return 0; +} + +static int synusb_resume(struct usb_interface *intf) +{ +	struct synusb *synusb = usb_get_intfdata(intf); +	struct input_dev *input_dev = synusb->input; +	int retval = 0; + +	mutex_lock(&input_dev->mutex); + +	if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && +	    usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { +		retval = -EIO; +	} + +	mutex_unlock(&input_dev->mutex); + +	return retval; +} + +static int synusb_pre_reset(struct usb_interface *intf) +{ +	struct synusb *synusb = usb_get_intfdata(intf); +	struct input_dev *input_dev = synusb->input; + +	mutex_lock(&input_dev->mutex); +	usb_kill_urb(synusb->urb); + +	return 0; +} + +static int synusb_post_reset(struct usb_interface *intf) +{ +	struct synusb *synusb = usb_get_intfdata(intf); +	struct input_dev *input_dev = synusb->input; +	int retval = 0; + +	if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && +	    usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { +		retval = -EIO; +	} + +	mutex_unlock(&input_dev->mutex); + +	return retval; +} + +static int synusb_reset_resume(struct usb_interface *intf) +{ +	return synusb_resume(intf); +} + +static struct usb_device_id synusb_idtable[] = { +	{ USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) }, +	{ USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) }, +	{ USB_DEVICE_SYNAPTICS(CPAD, +		SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) }, +	{ USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) }, +	{ USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) }, +	{ USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) }, +	{ USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) }, +	{ USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) }, +	{ USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) }, +	{ } +}; +MODULE_DEVICE_TABLE(usb, synusb_idtable); + +static struct usb_driver synusb_driver = { +	.name		= "synaptics_usb", +	.probe		= synusb_probe, +	.disconnect	= synusb_disconnect, +	.id_table	= synusb_idtable, +	.suspend	= synusb_suspend, +	.resume		= synusb_resume, +	.pre_reset	= synusb_pre_reset, +	.post_reset	= synusb_post_reset, +	.reset_resume	= synusb_reset_resume, +	.supports_autosuspend = 1, +}; + +module_usb_driver(synusb_driver); + +MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, " +              "Ron Lee <ron@debian.org>, " +              "Jan Steinhoff <cpad@jan-steinhoff.de>"); +MODULE_DESCRIPTION("Synaptics USB device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 54b2fa892e1..ca843b6cf6b 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -20,9 +20,34 @@  #include "trackpoint.h"  /* + * Power-on Reset: Resets all trackpoint parameters, including RAM values, + * to defaults. + * Returns zero on success, non-zero on failure. + */ +static int trackpoint_power_on_reset(struct ps2dev *ps2dev) +{ +	unsigned char results[2]; +	int tries = 0; + +	/* Issue POR command, and repeat up to once if 0xFC00 received */ +	do { +		if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || +		    ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR))) +			return -1; +	} while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2); + +	/* Check for success response -- 0xAA00 */ +	if (results[0] != 0xAA || results[1] != 0x00) +		return -1; + +	return 0; +} + +/*   * Device IO: read, write and toggle bit   */ -static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results) +static int trackpoint_read(struct ps2dev *ps2dev, +			   unsigned char loc, unsigned char *results)  {  	if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||  	    ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) { @@ -32,7 +57,8 @@ static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned ch  	return 0;  } -static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val) +static int trackpoint_write(struct ps2dev *ps2dev, +			    unsigned char loc, unsigned char val)  {  	if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||  	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) || @@ -44,7 +70,8 @@ static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned c  	return 0;  } -static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask) +static int trackpoint_toggle_bit(struct ps2dev *ps2dev, +				 unsigned char loc, unsigned char mask)  {  	/* Bad things will happen if the loc param isn't in this range */  	if (loc < 0x20 || loc >= 0x2F) @@ -60,6 +87,18 @@ static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsig  	return 0;  } +static int trackpoint_update_bit(struct ps2dev *ps2dev, unsigned char loc, +				 unsigned char mask, unsigned char value) +{ +	int retval = 0; +	unsigned char data; + +	trackpoint_read(ps2dev, loc, &data); +	if (((data & mask) == mask) != !!value) +		retval = trackpoint_toggle_bit(ps2dev, loc, mask); + +	return retval; +}  /*   * Trackpoint-specific attributes @@ -69,6 +108,7 @@ struct trackpoint_attr_data {  	unsigned char command;  	unsigned char mask;  	unsigned char inverted; +	unsigned char power_on_default;  };  static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf) @@ -89,10 +129,12 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,  	struct trackpoint_data *tp = psmouse->private;  	struct trackpoint_attr_data *attr = data;  	unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); -	unsigned long value; +	unsigned char value; +	int err; -	if (strict_strtoul(buf, 10, &value) || value > 255) -		return -EINVAL; +	err = kstrtou8(buf, 10, &value); +	if (err) +		return err;  	*field = value;  	trackpoint_write(&psmouse->ps2dev, attr->command, value); @@ -100,10 +142,11 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,  	return count;  } -#define TRACKPOINT_INT_ATTR(_name, _command)					\ +#define TRACKPOINT_INT_ATTR(_name, _command, _default)				\  	static struct trackpoint_attr_data trackpoint_attr_##_name = {		\  		.field_offset = offsetof(struct trackpoint_data, _name),	\  		.command = _command,						\ +		.power_on_default = _default,					\  	};									\  	PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,				\  			    &trackpoint_attr_##_name,				\ @@ -115,9 +158,14 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,  	struct trackpoint_data *tp = psmouse->private;  	struct trackpoint_attr_data *attr = data;  	unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); -	unsigned long value; +	unsigned int value; +	int err; -	if (strict_strtoul(buf, 10, &value) || value > 1) +	err = kstrtouint(buf, 10, &value); +	if (err) +		return err; + +	if (value > 1)  		return -EINVAL;  	if (attr->inverted) @@ -132,31 +180,60 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,  } -#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv)				\ -	static struct trackpoint_attr_data trackpoint_attr_##_name = {		\ -		.field_offset	= offsetof(struct trackpoint_data, _name),	\ -		.command	= _command,					\ -		.mask		= _mask,					\ -		.inverted	= _inv,						\ -	};									\ -	PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,				\ -			    &trackpoint_attr_##_name,				\ -			    trackpoint_show_int_attr, trackpoint_set_bit_attr) - -TRACKPOINT_INT_ATTR(sensitivity, TP_SENS); -TRACKPOINT_INT_ATTR(speed, TP_SPEED); -TRACKPOINT_INT_ATTR(inertia, TP_INERTIA); -TRACKPOINT_INT_ATTR(reach, TP_REACH); -TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS); -TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG); -TRACKPOINT_INT_ATTR(thresh, TP_THRESH); -TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH); -TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME); -TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV); - -TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0); -TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0); -TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1); +#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default)	\ +static struct trackpoint_attr_data trackpoint_attr_##_name = {		\ +	.field_offset		= offsetof(struct trackpoint_data,	\ +					   _name),			\ +	.command		= _command,				\ +	.mask			= _mask,				\ +	.inverted		= _inv,					\ +	.power_on_default	= _default,				\ +	};								\ +PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,				\ +		    &trackpoint_attr_##_name,				\ +		    trackpoint_show_int_attr, trackpoint_set_bit_attr) + +#define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name)			\ +do {									\ +	struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name;	\ +									\ +	trackpoint_update_bit(&_psmouse->ps2dev,			\ +			_attr->command, _attr->mask, _tp->_name);	\ +} while (0) + +#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name)		\ +do {									\ +	if (!_power_on ||						\ +	    _tp->_name != trackpoint_attr_##_name.power_on_default) {	\ +		if (!trackpoint_attr_##_name.mask)			\ +			trackpoint_write(&_psmouse->ps2dev,		\ +				 trackpoint_attr_##_name.command,	\ +				 _tp->_name);				\ +		else							\ +			TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name);	\ +	}								\ +} while (0) + +#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name)				\ +	(_tp->_name = trackpoint_attr_##_name.power_on_default) + +TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS); +TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED); +TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA); +TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH); +TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS); +TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG); +TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH); +TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH); +TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME); +TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV); + +TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0, +		    TP_DEF_PTSON); +TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0, +		    TP_DEF_SKIPBACK); +TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1, +		    TP_DEF_EXT_DEV);  static struct attribute *trackpoint_attrs[] = {  	&psmouse_attr_sensitivity.dattr.attr, @@ -195,73 +272,72 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir  	return 0;  } -static int trackpoint_sync(struct psmouse *psmouse) +/* + * Write parameters to trackpad. + * in_power_on_state: Set to true if TP is in default / power-on state (ex. if + *		      power-on reset was run). If so, values will only be + *		      written to TP if they differ from power-on default. + */ +static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state)  {  	struct trackpoint_data *tp = psmouse->private; -	unsigned char toggle; - -	/* Disable features that may make device unusable with this driver */ -	trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle); -	if (toggle & TP_MASK_TWOHAND) -		trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND); - -	trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle); -	if (toggle & TP_MASK_SOURCE_TAG) -		trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG); - -	trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle); -	if (toggle & TP_MASK_MB) -		trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB); - -	/* Push the config to the device */ -	trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity); -	trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia); -	trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed); - -	trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach); -	trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys); -	trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag); - -	trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh); -	trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh); -	trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime); -	trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks); +	if (!in_power_on_state) { +		/* +		 * Disable features that may make device unusable +		 * with this driver. +		 */ +		trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, +				      TP_MASK_TWOHAND, TP_DEF_TWOHAND); -	trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle); -	if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select) -		 trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON); +		trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, +				      TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG); -	trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle); -	if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback) -		trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK); +		trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB, +				      TP_MASK_MB, TP_DEF_MB); +	} -	trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle); -	if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev) -		trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV); +	/* +	 * These properties can be changed in this driver. Only +	 * configure them if the values are non-default or if the TP is in +	 * an unknown state. +	 */ +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks); + +	/* toggles */ +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback); +	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev);  	return 0;  }  static void trackpoint_defaults(struct trackpoint_data *tp)  { -	tp->press_to_select = TP_DEF_PTSON; -	tp->sensitivity = TP_DEF_SENS; -	tp->speed = TP_DEF_SPEED; -	tp->reach = TP_DEF_REACH; - -	tp->draghys = TP_DEF_DRAGHYS; -	tp->mindrag = TP_DEF_MINDRAG; - -	tp->thresh = TP_DEF_THRESH; -	tp->upthresh = TP_DEF_UP_THRESH; - -	tp->ztime = TP_DEF_Z_TIME; -	tp->jenks = TP_DEF_JENKS_CURV; - -	tp->inertia = TP_DEF_INERTIA; -	tp->skipback = TP_DEF_SKIPBACK; -	tp->ext_dev = TP_DEF_EXT_DEV; +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia); + +	/* toggles */ +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback); +	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev);  }  static void trackpoint_disconnect(struct psmouse *psmouse) @@ -274,10 +350,13 @@ static void trackpoint_disconnect(struct psmouse *psmouse)  static int trackpoint_reconnect(struct psmouse *psmouse)  { +	int reset_fail; +  	if (trackpoint_start_protocol(psmouse, NULL))  		return -1; -	if (trackpoint_sync(psmouse)) +	reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev); +	if (trackpoint_sync(psmouse, !reset_fail))  		return -1;  	return 0; @@ -297,7 +376,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)  		return 0;  	if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) { -		printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n"); +		psmouse_warn(psmouse, "failed to get extended button data\n");  		button_info = 0;  	} @@ -315,20 +394,27 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)  		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);  	trackpoint_defaults(psmouse->private); -	trackpoint_sync(psmouse); + +	error = trackpoint_power_on_reset(&psmouse->ps2dev); + +	/* Write defaults to TP only if reset fails. */ +	if (error) +		trackpoint_sync(psmouse, false);  	error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);  	if (error) { -		printk(KERN_ERR -			"trackpoint.c: failed to create sysfs attributes, error: %d\n", -			error); +		psmouse_err(psmouse, +			    "failed to create sysfs attributes, error: %d\n", +			    error);  		kfree(psmouse->private);  		psmouse->private = NULL;  		return -1;  	} -	printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n", -		firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f); +	psmouse_info(psmouse, +		     "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n", +		     firmware_id, +		     (button_info & 0xf0) >> 4, button_info & 0x0f);  	return 0;  } diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index e558a709661..ecd0547964a 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -126,6 +126,8 @@  #define TP_DEF_PTSON		0x00  #define TP_DEF_SKIPBACK		0x00  #define TP_DEF_EXT_DEV		0x00	/* 0 means enabled */ +#define TP_DEF_TWOHAND		0x00 +#define TP_DEF_SOURCE_TAG	0x00  #define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd)) @@ -136,9 +138,9 @@ struct trackpoint_data  	unsigned char thresh, upthresh;  	unsigned char ztime, jenks; +	/* toggles */  	unsigned char press_to_select;  	unsigned char skipback; -  	unsigned char ext_dev;  }; diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index bf2c0c80d6c..38298232124 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -82,7 +82,6 @@  #include <linux/interrupt.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" @@ -334,7 +333,7 @@ static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse)  	 * M: manufacturer location code  	 * R: revision code  	 * E: Error code. If it's in the range of 0x00..0x1f, only some -	 *    minor problem occured. Errors >= 0x20 are considered bad +	 *    minor problem occurred. Errors >= 0x20 are considered bad  	 *    and the device may not work properly...  	 * D: <0010> == mouse, <0100> == tablet  	 */ @@ -548,16 +547,4 @@ static struct serio_driver vsxxxaa_drv = {  	.disconnect	= vsxxxaa_disconnect,  }; -static int __init vsxxxaa_init(void) -{ -	return serio_register_driver(&vsxxxaa_drv); -} - -static void __exit vsxxxaa_exit(void) -{ -	serio_unregister_driver(&vsxxxaa_drv); -} - -module_init(vsxxxaa_init); -module_exit(vsxxxaa_exit); - +module_serio_driver(vsxxxaa_drv);  | 
