From: Takashi Iwai on
Add the detection of Synaptics Clickpad device.
The device can be detected a new query command 0x0c. The clickpad
flags are in cap[0]:4 and cap[1]:0 bits. But, the driver checks
first the product id bits in the ext capabilities to be sure, so
that it skips the new check on older devices.

When the device is detected, the driver now reports only the left
button as the supported buttons so that X11 driver can detect that
the device is Clickpad. A Clickpad device gives the button events
only as the middle button. The kernel driver morphs to the left
button. The real handling of Clickpad is done rather in X driver
side.

Signed-off-by: Takashi Iwai <tiwai(a)suse.de>
---
drivers/input/mouse/synaptics.c | 32 ++++++++++++++++++++++++++++++++
drivers/input/mouse/synaptics.h | 4 ++++
2 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 026df60..6a51542 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -328,6 +328,24 @@ static void synaptics_pt_create(struct psmouse *psmouse)
* Functions to interpret the absolute mode packets
****************************************************************************/

+static void synaptics_check_clickpad(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ unsigned char ncap[3];
+
+ /* check the new capability bits only on known working devices */
+ if (SYN_CAP_PRODUCT_ID(priv->ext_cap) != 0xe4)
+ return;
+ if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB2, ncap))
+ return;
+ printk(KERN_INFO "Synaptics: newcap: %02x:%02x:%02x\n",
+ ncap[0], ncap[1], ncap[2]);
+ priv->clickpad = ((ncap[0] & 0x10) >> 4) | ((ncap[1] & 0x01) << 1);
+ if (priv->clickpad)
+ printk(KERN_INFO "Synaptics: Clickpad device detected: %d\n",
+ priv->clickpad);
+}
+
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));
@@ -354,6 +372,13 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
hw->scroll = (signed char)(buf[1]);
}

+ if (priv->clickpad) {
+ /* clickpad reports only the middle button, report
+ * it as the left button
+ */
+ hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ }
+
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
@@ -593,6 +618,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)

dev->absres[ABS_X] = priv->x_res;
dev->absres[ABS_Y] = priv->y_res;
+
+ if (priv->clickpad) {
+ __clear_bit(BTN_RIGHT, dev->keybit); /* only left-button */
+ __clear_bit(BTN_MIDDLE, dev->keybit);
+ }
}

static void synaptics_disconnect(struct psmouse *psmouse)
@@ -702,6 +732,8 @@ int synaptics_init(struct psmouse *psmouse)
SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
priv->model_id, priv->capabilities, priv->ext_cap);

+ synaptics_check_clickpad(psmouse);
+
set_input_params(psmouse->dev, priv);

/*
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index f0f40a3..b824851 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -18,6 +18,7 @@
#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
#define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09
+#define SYN_QUE_EXT_CAPAB2 0x0c

/* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
@@ -48,6 +49,7 @@
#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)

/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
@@ -103,6 +105,8 @@ struct synaptics_data {
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */
int scroll;
+
+ unsigned char clickpad;
};

void synaptics_module_init(void);
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/