aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/mouse/synaptics.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/mouse/synaptics.c')
-rw-r--r--drivers/input/mouse/synaptics.c197
1 files changed, 142 insertions, 55 deletions
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a6dcd18e9ad..8081a0a5d60 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -269,19 +269,49 @@ 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))
+ return 0;
+
+ if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+ return -1;
+
+ if (ps2_command(&psmouse->ps2dev, &param, 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;
}
@@ -300,26 +330,6 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
synaptics_mode_cmd(psmouse, priv->mode);
}
-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, &param, PSMOUSE_CMD_SETRATE))
- return -1;
-
- /* Advanced gesture mode also sends multi finger data */
- priv->capabilities |= BIT(1);
-
- return 0;
-}
-
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
@@ -1143,8 +1153,24 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
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);
set_abs_position_params(dev, priv, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
@@ -1170,20 +1196,14 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
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);
@@ -1205,10 +1225,58 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
}
}
+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;
}
@@ -1245,17 +1313,11 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return -1;
}
- if (synaptics_set_absolute_mode(psmouse)) {
+ if (synaptics_set_mode(psmouse)) {
psmouse_err(psmouse, "Unable to initialize device.\n");
return -1;
}
- if (synaptics_set_advanced_gesture_mode(psmouse)) {
- psmouse_err(psmouse,
- "Advanced gesture mode reconnect failed.\n");
- return -1;
- }
-
if (old_priv.identity != priv->identity ||
old_priv.model_id != priv->model_id ||
old_priv.capabilities != priv->capabilities ||
@@ -1332,20 +1394,18 @@ void __init synaptics_module_init(void)
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; similarly to
- * the HGPK, it quickly degrades and the hardware becomes jumpy and
- * overly sensitive. Not only that, but the constant packet spew
- * (even at a lowered 40pps rate) overloads the EC such that key
- * presses on the keyboard are missed. Given all of that, don't
- * even attempt to use Synaptics mode. Relative mode seems to work
- * just fine.
+ * 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 (broken_olpc_ec) {
+ if (absolute_mode && broken_olpc_ec) {
psmouse_info(psmouse,
"OLPC XO detected, not enabling Synaptics protocol.\n");
return -ENODEV;
@@ -1362,13 +1422,12 @@ int synaptics_init(struct psmouse *psmouse)
goto init_fail;
}
- if (synaptics_set_absolute_mode(psmouse)) {
- psmouse_err(psmouse, "Unable to initialize device.\n");
- goto init_fail;
- }
+ priv->absolute_mode = absolute_mode;
+ if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+ priv->disable_gesture = true;
- if (synaptics_set_advanced_gesture_mode(psmouse)) {
- psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+ if (synaptics_set_mode(psmouse)) {
+ psmouse_err(psmouse, "Unable to initialize device.\n");
goto init_fail;
}
@@ -1393,12 +1452,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;
@@ -1417,11 +1483,32 @@ int synaptics_init(struct psmouse *psmouse)
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)