Commit cdd9dc19 authored by Benjamin Tissoires's avatar Benjamin Tissoires Committed by Dmitry Torokhov

Input: synaptics - re-route tracksticks buttons on the Lenovo 2015 series

The 2015 series of the Lenovo thinkpads added back the hardware buttons on
top of the touchpad for the trackstick.

Unfortunately, they are wired to the touchpad, and not the trackstick.
Thus, they are seen as extra buttons from the kernel point of view.

This leads to a problem in user space because extra buttons on synaptics
devices used to be used as scroll up/down buttons. So in the end, the
experience for the user is scroll events for buttons left and right when
using the trackstick. Yay!

Fortunately, the firmware advertises such behavior in the extended
capability $10, and so we can re-route the buttons through the pass-through
interface.
Hallelujah-expressed-by: default avatarPeter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Acked-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 3adde1f5
...@@ -579,18 +579,22 @@ static int synaptics_is_pt_packet(unsigned char *buf) ...@@ -579,18 +579,22 @@ static int synaptics_is_pt_packet(unsigned char *buf)
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
} }
static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) static void synaptics_pass_pt_packet(struct psmouse *psmouse,
struct serio *ptport,
unsigned char *packet)
{ {
struct synaptics_data *priv = psmouse->private;
struct psmouse *child = serio_get_drvdata(ptport); struct psmouse *child = serio_get_drvdata(ptport);
if (child && child->state == PSMOUSE_ACTIVATED) { if (child && child->state == PSMOUSE_ACTIVATED) {
serio_interrupt(ptport, packet[1], 0); serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0);
serio_interrupt(ptport, packet[4], 0); serio_interrupt(ptport, packet[4], 0);
serio_interrupt(ptport, packet[5], 0); serio_interrupt(ptport, packet[5], 0);
if (child->pktsize == 4) if (child->pktsize == 4)
serio_interrupt(ptport, packet[2], 0); serio_interrupt(ptport, packet[2], 0);
} else } else {
serio_interrupt(ptport, packet[1], 0); serio_interrupt(ptport, packet[1], 0);
}
} }
static void synaptics_pt_activate(struct psmouse *psmouse) static void synaptics_pt_activate(struct psmouse *psmouse)
...@@ -847,6 +851,7 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, ...@@ -847,6 +851,7 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private; struct synaptics_data *priv = psmouse->private;
int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int i; int i;
if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
...@@ -857,12 +862,30 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, ...@@ -857,12 +862,30 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
!((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
return; return;
for (i = 0; i < ext_bits; i++) { if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
input_report_key(dev, BTN_0 + 2 * i, for (i = 0; i < ext_bits; i++) {
hw->ext_buttons & (1 << i)); input_report_key(dev, BTN_0 + 2 * i,
input_report_key(dev, BTN_1 + 2 * i, hw->ext_buttons & (1 << i));
hw->ext_buttons & (1 << (i + ext_bits))); input_report_key(dev, BTN_1 + 2 * i,
hw->ext_buttons & (1 << (i + ext_bits)));
}
return;
} }
/*
* This generation of touchpads has the trackstick buttons
* physically wired to the touchpad. Re-route them through
* the pass-through interface.
*/
if (!priv->pt_port)
return;
/* The trackstick expects at most 3 buttons */
priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) |
SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
synaptics_pass_pt_packet(psmouse, priv->pt_port, buf);
} }
static void synaptics_report_buttons(struct psmouse *psmouse, static void synaptics_report_buttons(struct psmouse *psmouse,
...@@ -1459,7 +1482,8 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) ...@@ -1459,7 +1482,8 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
synaptics_is_pt_packet(psmouse->packet)) { synaptics_is_pt_packet(psmouse->packet)) {
if (priv->pt_port) if (priv->pt_port)
synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); synaptics_pass_pt_packet(psmouse, priv->pt_port,
psmouse->packet);
} else } else
synaptics_process_packet(psmouse); synaptics_process_packet(psmouse);
...@@ -1561,8 +1585,9 @@ static void set_input_params(struct psmouse *psmouse, ...@@ -1561,8 +1585,9 @@ static void set_input_params(struct psmouse *psmouse,
__set_bit(BTN_BACK, dev->keybit); __set_bit(BTN_BACK, dev->keybit);
} }
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
__set_bit(BTN_0 + i, dev->keybit); for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
__set_bit(BTN_0 + i, dev->keybit);
__clear_bit(EV_REL, dev->evbit); __clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit); __clear_bit(REL_X, dev->relbit);
......
...@@ -111,6 +111,10 @@ ...@@ -111,6 +111,10 @@
#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000) #define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000)
#define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000) #define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000)
#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01))
#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02))
#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04))
/* synaptics modes query bits */ /* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
#define SYN_MODE_RATE(m) ((m) & (1 << 6)) #define SYN_MODE_RATE(m) ((m) & (1 << 6))
...@@ -192,6 +196,7 @@ struct synaptics_data { ...@@ -192,6 +196,7 @@ struct synaptics_data {
bool disable_gesture; /* disable gestures */ bool disable_gesture; /* disable gestures */
struct serio *pt_port; /* Pass-through serio port */ struct serio *pt_port; /* Pass-through serio port */
unsigned char pt_buttons; /* Pass-through buttons */
struct synaptics_mt_state mt_state; /* Current mt finger state */ struct synaptics_mt_state mt_state; /* Current mt finger state */
bool mt_state_lost; /* mt_state may be incorrect */ bool mt_state_lost; /* mt_state may be incorrect */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment