diff mbox

[v2] input: Support Clickpad devices in ClickZone mode

Message ID s5hljh5we8y.wl%tiwai@suse.de
State New, archived
Headers show

Commit Message

Takashi Iwai Dec. 14, 2009, 7:24 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 05689e7..b2db417 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -327,6 +327,45 @@  static void synaptics_pt_create(struct psmouse *psmouse)
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
 
+/* left and right clickpad button ranges;
+ * the gap between them is interpreted as a middle-button click
+ */
+#define CLICKPAD_LEFT_BTN_X \
+	((XMAX_NOMINAL - XMIN_NOMINAL) * 2 / 5 + XMIN_NOMINAL)
+#define CLICKPAD_RIGHT_BTN_X \
+	((XMAX_NOMINAL - XMIN_NOMINAL) * 3 / 5 + XMIN_NOMINAL)
+
+/* handle clickpad events */
+static void clickpad_process_packet(struct synaptics_data *priv,
+				    struct synaptics_hw_state *hw)
+{
+	/* clickpad mode reports Y range from 0 to YMAX_NOMINAL,
+	 * where the area Y < YMIN_NOMINAL is used as click buttons
+	 */
+	if (hw->y < YMIN_NOMINAL) {
+		/* button area */
+		hw->z = 0; /* don't move pointer */
+		/* clickpad reports only the middle button, and we need
+		 * to fake left/right buttons depending on the touch position
+		 */
+		if (hw->middle) { /* clicked? */
+			hw->middle = 0;
+			if (hw->x < CLICKPAD_LEFT_BTN_X)
+				hw->left = 1;
+			else if (hw->x > CLICKPAD_RIGHT_BTN_X)
+				hw->right = 1;
+			else
+				hw->middle = 1;
+		}
+	} else if (hw->middle) {
+		/* dragging */
+		hw->left = priv->prev_hw.left;
+		hw->right = priv->prev_hw.right;
+		hw->middle = priv->prev_hw.middle;
+	}
+	priv->prev_hw = *hw;
+}
+
 static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
 {
 	memset(hw, 0, sizeof(struct synaptics_hw_state));
@@ -407,6 +446,9 @@  static void synaptics_process_packet(struct psmouse *psmouse)
 
 	synaptics_parse_hw_state(psmouse->packet, priv, &hw);
 
+	if (SYN_CAP_CLICKPAD(priv->ext_cap))
+		clickpad_process_packet(priv, &hw);
+
 	if (hw.scroll) {
 		priv->scroll += hw.scroll;
 
@@ -701,6 +743,12 @@  int synaptics_init(struct psmouse *psmouse)
 		SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
 		priv->model_id, priv->capabilities, priv->ext_cap);
 
+	if (SYN_CAP_CLICKPAD(priv->ext_cap)) {
+		printk(KERN_INFO "Synaptics: Clickpad mode enabled\n");
+		/* force to enable the middle button */
+		priv->capabilities |= (1 << 18);
+	}
+
 	set_input_params(psmouse->dev, priv);
 
 	/*
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 838e7f2..76df393 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -48,6 +48,8 @@ 
 #define SYN_CAP_VALID(c)		((((c) & 0x00ff00) >> 8) == 0x47)
 #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(ec)		(SYN_CAP_PRODUCT_ID(ec) == 0xe4)
 
 /* synaptics modes query bits */
 #define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7))
@@ -103,6 +105,7 @@  struct synaptics_data {
 	unsigned char pkt_type;			/* packet type - old, new, etc */
 	unsigned char mode;			/* current mode byte */
 	int scroll;
+	struct synaptics_hw_state prev_hw;
 };
 
 void synaptics_module_init(void);