diff mbox

[2/2] Alps Dualpoint, Interleaved packets

Message ID 1260742145.9901.18.camel@sardelle.necksus.de (mailing list archive)
State New, archived
Headers show

Commit Message

Sebastian Kapfer Dec. 13, 2009, 10:09 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 71b40ae..10b591c 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -39,6 +39,7 @@ 
 #define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
 #define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
 					   6-byte ALPS packet */
+#define ALPS_SHARED_BTNSTATE	0x100	/* PS/2 and touchpad share button st. */
 
 static const struct alps_model_info alps_model_data[] = {
 	{ { 0x32, 0x02, 0x14 },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
@@ -61,7 +62,7 @@  static const struct alps_model_info alps_model_data[] = {
 	{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
 	/* Dell Latitude E5500, E6400, E6500, Precision M4400 */
 	{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf,
-		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
+		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED | ALPS_SHARED_BTNSTATE },
 	{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },	  /* Dell Vostro 1400 */
 };
 
@@ -108,18 +109,49 @@  static const struct alps_model_info alps_model_data[] = {
  * on a dualpoint, etc.
  */
 
-static void alps_report_buttons(struct input_dev *dev,
-				int left, int right, int middle,
-				bool release_only)
+static void alps_report_single_button(struct psmouse *psmouse,
+				      int keycode, int mask, int state,
+				      struct input_dev *dev)
 {
-	if (!left || !release_only)
-		input_report_key(dev, BTN_LEFT, left);
+	struct alps_data *priv = psmouse->private;
+	const struct alps_model_info *model = priv->i;
 
-	if (!right || !release_only)
-		input_report_key(dev, BTN_RIGHT, right);
+	/* some Alps units do not report touchpad and trackpoint
+	   buttons separately (e.g. even the trackpoint movement
+	   packets have buttons set while touchpad buttons are down) */
+	if (model->flags & ALPS_SHARED_BTNSTATE) {
+		if (state) {
+			/* need to avoid sending a button-down to both
+			   devices (can cause spurious double-click */
+			if (priv->btn_state & mask)
+				return;
+
+			priv->btn_state |= mask;
+		} else {
+			/* we don't know which device got the button
+			   down event.  just release both to be sure. */
+			struct input_dev *other = psmouse->dev;
+			if (dev == other)
+				other = priv->dev2;
+			input_report_key(other, keycode, state);
+			input_sync(other);
+
+			priv->btn_state &= 7 ^ mask;
+		}
+	}
 
-	if (!middle || !release_only)
-		input_report_key(dev, BTN_MIDDLE, middle);
+	/* input_sync called by our caller */
+	input_report_key(dev, keycode, state);
+}
+
+
+static void alps_report_buttons(struct psmouse *psmouse,
+				struct input_dev *dev,
+				int left, int right, int middle)
+{
+	alps_report_single_button(psmouse, BTN_LEFT,   1, left,   dev);
+	alps_report_single_button(psmouse, BTN_RIGHT,  2, right,  dev);
+	alps_report_single_button(psmouse, BTN_MIDDLE, 4, middle, dev);
 }
 
 static void alps_process_packet(struct psmouse *psmouse)
@@ -167,25 +199,13 @@  static void alps_process_packet(struct psmouse *psmouse)
 		input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
 		input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
 
-		alps_report_buttons(dev2, left, right, middle, false);
+		alps_report_buttons(psmouse, dev2, left, right, middle);
 
 		input_sync(dev2);
-		input_sync(dev);
 		return;
 	}
 
-	alps_report_buttons(dev, left, right, middle, false);
-
-	if (model->flags & ALPS_PS2_INTERLEAVED) {
-		/*
-		 * On devices using interleaved packets, when user presses
-		 * the same button on both trackpoint and touchpad, the
-		 * release for the trackpoint is not reported so we have
-		 * send release events to both devices.
-		 */
-		alps_report_buttons(dev2, left, right, middle, true);
-		input_sync(dev2);
-	}
+	alps_report_buttons(psmouse, dev, left, right, middle);
 
 	/* Convert hardware tap to a reasonable Z value */
 	if (ges && !fin)
@@ -248,14 +268,7 @@  static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
 		int right = packet[0] & 2;
 		int middle = packet[0] & 4;
 
-
-		if (priv->i->flags & ALPS_PS2_INTERLEAVED) {
-			alps_report_buttons(psmouse->dev,
-					    left, right, middle, true);
-			input_sync(psmouse->dev);
-		}
-
-		alps_report_buttons(dev2, left, right, middle, false);
+		alps_report_buttons(psmouse, dev2, left, right, middle);
 	}
 
 	input_report_rel(dev2, REL_X,
@@ -675,6 +688,7 @@  int alps_init(struct psmouse *psmouse)
 
 	priv->dev2 = dev2;
 	setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);
+	priv->btn_state = 0;
 
 	psmouse->private = priv;
 
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 904ed8b..b477063 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 {
@@ -23,6 +23,7 @@  struct alps_data {
 	char phys[32];			/* Phys */
 	const struct alps_model_info *i;/* Info */
 	int prev_fin;			/* Finger bit from previous packet */
+	int btn_state;			/* buttons reported to input_dev */
 	struct timer_list timer;
 };