[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#618422: linux-image-2.6.32-5-amd64: HP DM1-2160la with ALPS toucpad wasn't detect very well



I had the same symptoms with an ALPS touchpad in a Dell Latitude E6510.
I found a patch in the following list: https://bugzilla.redhat.com/show_bug.cgi?id=590880, which I attach for your convenience. The patch applies cleanly to the current stable kernel. It enabled vertical scrolling for me. Also, by a trivial tweak of the patch (look at the code, it really is trivial) I was able to disable click-on-tap, which was
way to sensitive on my machine.

I'm not quite sure how Dell-specific the patch is, but it may be worth a try.
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index a9f461e..27ec20e 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -29,15 +29,17 @@
 #define dbg(format, arg...) do {} while (0)
 #endif
 
-#define ALPS_DUALPOINT	0x01
-#define ALPS_WHEEL	0x02
-#define ALPS_FW_BK_1	0x04
-#define ALPS_4BTN	0x08
-#define ALPS_OLDPROTO	0x10
-#define ALPS_PASS	0x20
-#define ALPS_FW_BK_2	0x40
-#define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
+#define ALPS_DUALPOINT	0x001
+#define ALPS_WHEEL	0x002
+#define ALPS_FW_BK_1	0x004
+#define ALPS_4BTN	0x008
+#define ALPS_OLDPROTO	0x010
+#define ALPS_PASS	0x020
+#define ALPS_FW_BK_2	0x040
+#define ALPS_PS2_INTERLEAVED	0x080	/* 3-byte PS/2 packet interleaved with
 					   6-byte ALPS packet */
+#define ALPS_EC_PROTO	0x100		/* EC memory access protocol */
+#define ALPS_IMPS	0x200		/* IMPS emulation */
 
 static const struct alps_model_info alps_model_data[] = {
 	{ { 0x32, 0x02, 0x14 },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
@@ -64,6 +66,7 @@ static const struct alps_model_info alps_model_data[] = {
 	{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 },		  /* Dell Vostro 1400 */
 	{ { 0x52, 0x01, 0x14 }, 0xff, 0xff,
 		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },	  /* Toshiba Tecra A11-11L */
+	{ { 0x73, 0x02, 0x64 }, 0x00, 0x00, ALPS_EC_PROTO | ALPS_IMPS },  /* Dell E2 series */
 };
 
 /*
@@ -516,6 +519,89 @@ static int alps_absolute_mode(struct psmouse *psmouse)
 	return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
 }
 
+static int alps_ec_mode(struct psmouse *psmouse, bool enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM))
+		return -1;
+	if (enable) {
+		/* EC EC EC E9 */
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP))
+			return -1;
+		param[0] = param[1] = param[2] = 0xff;
+		if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+			return -1;
+
+		dbg("EC report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+		if (param[0] != 0x88 || param[1] != 0x07 || (param[2] != 0x9b && param[2] != 0x9d))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int alps_ec_nibble(struct psmouse *psmouse, uint8_t nibble)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param;
+	static const int cmds[] = {
+		PSMOUSE_CMD_SETPOLL,
+		PSMOUSE_CMD_RESET_DIS,
+		PSMOUSE_CMD_SETSCALE21,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_SETRATE,
+		PSMOUSE_CMD_GETINFO,
+		PSMOUSE_CMD_SETRES,
+		PSMOUSE_CMD_SETRES,
+		PSMOUSE_CMD_SETRES,
+		PSMOUSE_CMD_SETRES,
+		PSMOUSE_CMD_SETSCALE11 };
+	static const unsigned char params[] = {
+		0xff, 0xff, 0xff, 10, 20, 40, 60, 80, 100, 200, 0xff, 0, 1, 2, 3, 0xff };
+
+	nibble &= 0xf;
+	param = params[nibble];
+	if (ps2_command(ps2dev, &param, cmds[nibble]))
+		return -1;
+
+	return 0;
+}
+
+static int alps_ec_write(struct psmouse *psmouse, uint16_t addr, uint8_t value)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	/* Select new address: EC addr3 addr2 addr1 addr0 */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
+	    alps_ec_nibble(psmouse, addr >> 12) ||
+	    alps_ec_nibble(psmouse, addr >> 8) ||
+	    alps_ec_nibble(psmouse, addr >> 4) ||
+	    alps_ec_nibble(psmouse, addr))
+		return -1;
+
+	/*
+	 * PSMOUSE_CMD_GETINFO can be used to read from the current address,
+	 * returning { addr_high, addr_low, value }  Useful when working with bit fields.
+	 */
+
+	/* Write byte: value1 value0 */
+	if (alps_ec_nibble(psmouse, value >> 4) ||
+	    alps_ec_nibble(psmouse, value))
+		return -1;
+
+	return 0;
+}
+
 static int alps_get_status(struct psmouse *psmouse, char *param)
 {
 	struct ps2dev *ps2dev = &psmouse->ps2dev;
@@ -602,30 +688,65 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
 	if (!priv->i)
 		return -1;
 
-	if ((priv->i->flags & ALPS_PASS) &&
-	    alps_passthrough_mode(psmouse, true)) {
+	if (!priv->dev2 && !(priv->i->flags & ALPS_IMPS)) {
+		/* Because we never initialized dev2 */
+		printk(KERN_ERR "alps.c: Failed to reconnect to IMPS emulation mode\n");
 		return -1;
 	}
 
-	if (alps_tap_mode(psmouse, true)) {
-		printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
-		return -1;
-	}
+	if (priv->i->flags & ALPS_EC_PROTO) {
+		if (alps_ec_mode(psmouse, true)) {
+			printk(KERN_ERR "alps.c: Failed to enable EC memory access mode\n");
+			return -1;
+		}
+		/* Reset touchpad memory (disabling EC mode in the process) */
+		if (alps_ec_write(psmouse, 0x0003, 0x01))
+			return -1;
+		if (alps_ec_mode(psmouse, true)) {
+			printk(KERN_ERR "alps.c: Failed to re-enable EC memory access mode\n");
+			return -1;
+		}
+		if (priv->i->flags & ALPS_IMPS) {
+			/*
+			 * Enable IntelliMouse protocol.
+			 *
+			 * Other known bits at address 0005 are:
+			 *   01 - Enable IntelliMouse protocol
+			 *   02 - Disable hardware tapping entirely
+			 *   04 - Disable corner tap for right-click
+			 *   80 - Upper-left corner tap (default upper-right)
+			 */
+			if (alps_ec_write(psmouse, 0x0005, 0x01))
+				return -1;
+		}
+		if (alps_ec_mode(psmouse, false))
+			return -1;
+	} else {
+		if ((priv->i->flags & ALPS_PASS) &&
+		    alps_passthrough_mode(psmouse, true)) {
+			return -1;
+		}
 
-	if (alps_absolute_mode(psmouse)) {
-		printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
-		return -1;
-	}
+		if (alps_tap_mode(psmouse, true)) {
+			printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
+			return -1;
+		}
 
-	if ((priv->i->flags & ALPS_PASS) &&
-	    alps_passthrough_mode(psmouse, false)) {
-		return -1;
-	}
+		if (alps_absolute_mode(psmouse)) {
+			printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+			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");
-		return -1;
+		if ((priv->i->flags & ALPS_PASS) &&
+		    alps_passthrough_mode(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");
+			return -1;
+		}
 	}
 
 	return 0;
@@ -648,7 +769,8 @@ static void alps_disconnect(struct psmouse *psmouse)
 
 	psmouse_reset(psmouse);
 	del_timer_sync(&priv->timer);
-	input_unregister_device(priv->dev2);
+	if (priv->dev2)
+		input_unregister_device(priv->dev2);
 	kfree(priv);
 }
 
@@ -668,8 +790,23 @@ int alps_init(struct psmouse *psmouse)
 
 	psmouse->private = priv;
 
+	priv->i = NULL;
 	if (alps_hw_init(psmouse, &version))
 		goto init_fail;
+	if (priv->i->flags & ALPS_IMPS) {
+		input_free_device(priv->dev2);
+		priv->dev2 = NULL;
+
+		__set_bit(BTN_MIDDLE, dev1->keybit);
+		__set_bit(REL_WHEEL, dev1->relbit);
+
+		psmouse->disconnect = alps_disconnect;
+		psmouse->reconnect = alps_reconnect;
+		psmouse->type = PSMOUSE_IMPS;
+		psmouse->pktsize = 4;
+
+		return 0;
+	}
 
 	dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);
 	dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 904ed8b..ba4a7f5 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -15,7 +15,7 @@
 struct alps_model_info {
         unsigned char signature[3];
         unsigned char byte0, mask0;
-        unsigned char flags;
+        unsigned int flags;
 };
 
 struct alps_data {
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 9451e28..1fb42d4 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -632,8 +632,9 @@ static int psmouse_extensions(struct psmouse *psmouse,
 	if (max_proto > PSMOUSE_IMEX) {
 		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
 		if (alps_detect(psmouse, set_properties) == 0) {
+			psmouse->type = PSMOUSE_NONE;
 			if (!set_properties || alps_init(psmouse) == 0)
-				return PSMOUSE_ALPS;
+				return (psmouse->type != PSMOUSE_NONE) ? psmouse->type : PSMOUSE_ALPS;
 /*
  * Init failed, try basic relative protocols
  */
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index e053bdd..207ca61 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -6,6 +6,8 @@
 #define PSMOUSE_CMD_SETRES	0x10e8
 #define PSMOUSE_CMD_GETINFO	0x03e9
 #define PSMOUSE_CMD_SETSTREAM	0x00ea
+#define PSMOUSE_CMD_RESET_WRAP	0x00ec
+#define PSMOUSE_CMD_SETWRAP	0x00ee
 #define PSMOUSE_CMD_SETPOLL	0x00f0
 #define PSMOUSE_CMD_POLL	0x00eb	/* caller sets number of bytes to receive */
 #define PSMOUSE_CMD_GETID	0x02f2

Reply to: