Hi Dmitry,
well, this year, Lenovo gave us a surprise and decided to not use
the same touchpad/trackstick in all its model. And by default, the
support under Linux is less than ideal.
Please find a series that should fix those issues. Compared to the 60
series, there do not seem to e BIOS table issues this time, and suspend/resume
works fine thanks to your latest trackstick fixes.
The T480s is a different beast, as it uses an Elan touchpad.
I have been carrying the patches 3-6 for a while and tested previous
versions on various Elan PS/2 hardware without an issue as far as I
could tell. I was lacking tests from users with SMBus as all the laptops
I tried where puer PS/2.
Anyway, it would be cool if you could have a look at the series.
Cheers,
Benjamin
Benjamin Tissoires (8):
Input: synaptics - add Lenovo 80 series ids to SMBus
input: elan_i2c_smbus - fix corrupted stack
Input: elan_i2c - add trackstick report
Input: elantech - split device info into a separate structure
Input: elantech - query the resolution in query_info
Input: elantech - add support for SMBus devices
Input: elantech - detect new ICs and setup Host Notify for them
input: psmouse-smbus: allow to control psmouse_deactivate
drivers/input/mouse/Kconfig | 12 +
drivers/input/mouse/elan_i2c.h | 10 +
drivers/input/mouse/elan_i2c_core.c | 91 ++++++-
drivers/input/mouse/elan_i2c_smbus.c | 22 +-
drivers/input/mouse/elantech.c | 475 ++++++++++++++++++++++++++---------
drivers/input/mouse/elantech.h | 69 ++++-
drivers/input/mouse/psmouse-base.c | 21 +-
drivers/input/mouse/psmouse-smbus.c | 13 +-
drivers/input/mouse/psmouse.h | 2 +
drivers/input/mouse/synaptics.c | 5 +-
10 files changed, 564 insertions(+), 156 deletions(-)
--
2.14.3
The command ETP_RESOLUTION_QUERY also contains the bus information.
It is better to fetch it once, while we are querying for device
information.
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/elantech.c | 27 +++++++++++++++------------
drivers/input/mouse/elantech.h | 2 ++
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index d485664f1563..41af0c9098af 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1179,7 +1179,6 @@ static int elantech_set_input_params(struct psmouse *psmouse)
struct elantech_data *etd = psmouse->private;
struct elantech_device_info *info = &etd->info;
unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
- unsigned int x_res = 31, y_res = 31;
if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
return -1;
@@ -1232,13 +1231,6 @@ static int elantech_set_input_params(struct psmouse *psmouse)
break;
case 4:
- if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
- /*
- * if query failed, print a warning and leave the values
- * zero to resemble synaptics.c behavior.
- */
- psmouse_warn(psmouse, "couldn't query resolution data.\n");
- }
elantech_set_buttonpad_prop(psmouse);
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
/* For X to recognize me as touchpad. */
@@ -1267,11 +1259,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
break;
}
- input_abs_set_res(dev, ABS_X, x_res);
- input_abs_set_res(dev, ABS_Y, y_res);
+ input_abs_set_res(dev, ABS_X, info->x_res);
+ input_abs_set_res(dev, ABS_Y, info->y_res);
if (info->hw_version > 1) {
- input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
- input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
+ input_abs_set_res(dev, ABS_MT_POSITION_X, info->x_res);
+ input_abs_set_res(dev, ABS_MT_POSITION_Y, info->y_res);
}
etd->y_max = y_max;
@@ -1720,6 +1712,17 @@ int elantech_query_info(struct psmouse *psmouse,
/* The MSB indicates the presence of the trackpoint */
info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
+ info->x_res = 31;
+ info->y_res = 31;
+ if (info->hw_version == 4) {
+ if (elantech_get_resolution_v4(psmouse,
+ &info->x_res,
+ &info->y_res)) {
+ psmouse_warn(psmouse,
+ "failed to query resolution data.\n");
+ }
+ }
+
return 0;
}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index d8ac27fe4597..851df4ce6232 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -120,6 +120,8 @@ struct elantech_device_info {
unsigned char debug;
unsigned char hw_version;
unsigned int fw_version;
+ unsigned int x_res;
+ unsigned int y_res;
bool paritycheck;
bool jumpy_cursor;
bool reports_pressure;
--
2.14.3
This seems to be Synaptics specific, as some Elan touchpads are not
correctly switching to SMBus if we call deactivate before switching to
SMBus on cold boot and on resume.
Tested with the T480s
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/elantech.c | 2 +-
drivers/input/mouse/psmouse-smbus.c | 13 ++++++++++---
drivers/input/mouse/psmouse.h | 1 +
drivers/input/mouse/synaptics.c | 2 +-
4 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 29f55e8e07e9..6009fb7d0d31 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1771,7 +1771,7 @@ static int elantech_create_smbus(struct psmouse *psmouse,
};
return psmouse_smbus_init(psmouse, &smbus_board, &pdata,
- sizeof(pdata), leave_breadcrumbs);
+ sizeof(pdata), false, leave_breadcrumbs);
}
/**
diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
index c7ac24d119c1..facfe902b073 100644
--- a/drivers/input/mouse/psmouse-smbus.c
+++ b/drivers/input/mouse/psmouse-smbus.c
@@ -23,6 +23,7 @@ struct psmouse_smbus_dev {
struct i2c_client *client;
struct list_head node;
bool dead;
+ bool need_deactivate;
};
static LIST_HEAD(psmouse_smbus_list);
@@ -118,7 +119,10 @@ static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
static int psmouse_smbus_reconnect(struct psmouse *psmouse)
{
- psmouse_deactivate(psmouse);
+ struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+ if (smbdev->need_deactivate)
+ psmouse_deactivate(psmouse);
return 0;
}
@@ -225,6 +229,7 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse)
int psmouse_smbus_init(struct psmouse *psmouse,
const struct i2c_board_info *board,
const void *pdata, size_t pdata_size,
+ bool need_deactivate,
bool leave_breadcrumbs)
{
struct psmouse_smbus_dev *smbdev;
@@ -236,6 +241,7 @@ int psmouse_smbus_init(struct psmouse *psmouse,
smbdev->psmouse = psmouse;
smbdev->board = *board;
+ smbdev->need_deactivate = need_deactivate;
smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
if (!smbdev->board.platform_data) {
@@ -243,6 +249,9 @@ int psmouse_smbus_init(struct psmouse *psmouse,
return -ENOMEM;
}
+ if (need_deactivate)
+ psmouse_deactivate(psmouse);
+
psmouse->private = smbdev;
psmouse->protocol_handler = psmouse_smbus_process_byte;
psmouse->reconnect = psmouse_smbus_reconnect;
@@ -250,8 +259,6 @@ int psmouse_smbus_init(struct psmouse *psmouse,
psmouse->disconnect = psmouse_smbus_disconnect;
psmouse->resync_time = 0;
- psmouse_deactivate(psmouse);
-
mutex_lock(&psmouse_smbus_mutex);
list_add_tail(&smbdev->node, &psmouse_smbus_list);
mutex_unlock(&psmouse_smbus_mutex);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index dd4ec1f602d7..64c3a5d3fb3e 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -225,6 +225,7 @@ struct i2c_board_info;
int psmouse_smbus_init(struct psmouse *psmouse,
const struct i2c_board_info *board,
const void *pdata, size_t pdata_size,
+ bool need_deactivate,
bool leave_breadcrumbs);
void psmouse_smbus_cleanup(struct psmouse *psmouse);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 14a1188561aa..6b1ed53a8b03 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -1751,7 +1751,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse,
};
return psmouse_smbus_init(psmouse, &intertouch_board,
- &pdata, sizeof(pdata),
+ &pdata, sizeof(pdata), true,
leave_breadcrumbs);
}
--
2.14.3
New ICs are using a different scheme for the alternate bus parameter.
Given that they are new and are only using either PS2 only or PS2 + SMBus
Host Notify, we force those new ICs to use the SMBus solution for enhanced
reporting.
This allows the touchpad found on the Lenovo T480s to report 5 fingers
every 8 ms, instead of having a limit of 2 every 8 ms.
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/elantech.c | 11 +++++++----
drivers/input/mouse/elantech.h | 15 +++++++++++++++
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index a61fbddd6b92..29f55e8e07e9 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1789,11 +1789,11 @@ static int elantech_setup_smbus(struct psmouse *psmouse,
if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
/*
- * FIXME:
- * constraint the I2C capable devices by using FW version,
- * board version, or by using DMI matching
+ * New ICs are enabled by default.
+ * Old ICs are up to the user to decide.
*/
- return -ENXIO;
+ if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
+ return -ENXIO;
}
psmouse_info(psmouse, "Trying to set up SMBus access\n");
@@ -1814,6 +1814,9 @@ static int elantech_setup_smbus(struct psmouse *psmouse,
static bool elantech_use_host_notify(struct psmouse *psmouse,
struct elantech_device_info *info)
{
+ if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
+ return true;
+
switch (info->bus) {
case ETP_BUS_PS2_ONLY:
/* expected case */
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index f9b1c485e8d9..119727085a60 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -115,6 +115,21 @@
#define ETP_BUS_PS2_SMB_ALERT 3
#define ETP_BUS_PS2_SMB_HST_NTFY 4
+/*
+ * New ICs are either using SMBus Host Notify or just plain PS2.
+ *
+ * ETP_FW_VERSION_QUERY is:
+ * Byte 1:
+ * - bit 0..3: IC BODY
+ * Byte 2:
+ * - bit 4: HiddenButton
+ * - bit 5: PS2_SMBUS_NOTIFY
+ * - bit 6: PS2CRCCheck
+ */
+#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version) \
+ ((((fw_version) & 0x0f2000) == 0x0f2000) && \
+ ((fw_version) & 0x0000ff) > 0)
+
/*
* The base position for one finger, v4 hardware
*/
--
2.14.3
Many of the Elantech devices are connected through PS/2 and a different
bus (SMBus or plain I2C).
To not break any existing device, we only enable SMBus based
on a module parameter. If some laptops require the quirk to
be set, we will have to rely on a list of PNPIds or MDI matching
to individually expose those hardware over SMBus.
the parameter mentioned above is elantech_smbus from the psmouse
module.
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/Kconfig | 12 +++
drivers/input/mouse/elantech.c | 184 ++++++++++++++++++++++++++++++++++++-
drivers/input/mouse/elantech.h | 24 +++++
drivers/input/mouse/psmouse-base.c | 21 ++++-
drivers/input/mouse/psmouse.h | 1 +
5 files changed, 235 insertions(+), 7 deletions(-)
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 89ebb8f39fee..f27f23f2d99a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH
If unsure, say N.
+config MOUSE_PS2_ELANTECH_SMBUS
+ bool "Elantech PS/2 SMbus companion" if EXPERT
+ default y
+ depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
+ depends on I2C=y || I2C=MOUSE_PS2
+ select MOUSE_PS2_SMBUS
+ help
+ Say Y here if you have a Elantech touchpad connected to
+ to an SMBus, but enumerated through PS/2.
+
+ If unsure, say Y.
+
config MOUSE_PS2_SENTELIC
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
depends on MOUSE_PS2
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 41af0c9098af..a61fbddd6b92 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -14,13 +14,16 @@
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
+#include <linux/platform_device.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <asm/unaligned.h>
#include "psmouse.h"
#include "elantech.h"
+#include "elan_i2c.h"
#define elantech_debug(fmt, ...) \
do { \
@@ -1084,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val)
static int elantech_get_resolution_v4(struct psmouse *psmouse,
unsigned int *x_res,
- unsigned int *y_res)
+ unsigned int *y_res,
+ unsigned int *bus)
{
unsigned char param[3];
@@ -1093,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
*x_res = elantech_convert_res(param[1] & 0x0f);
*y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
+ *bus = param[2];
return 0;
}
@@ -1474,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse)
{
struct elantech_data *etd = psmouse->private;
+ /*
+ * We might have left a breadcrumb when trying to
+ * set up SMbus companion.
+ */
+ psmouse_smbus_cleanup(psmouse);
+
if (etd->tp_dev)
input_unregister_device(etd->tp_dev);
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
@@ -1659,6 +1670,8 @@ int elantech_query_info(struct psmouse *psmouse,
{
unsigned char param[3];
+ memset(info, 0, sizeof(*info));
+
/*
* Do the version query again so we can store the result
*/
@@ -1717,7 +1730,8 @@ int elantech_query_info(struct psmouse *psmouse,
if (info->hw_version == 4) {
if (elantech_get_resolution_v4(psmouse,
&info->x_res,
- &info->y_res)) {
+ &info->y_res,
+ &info->bus)) {
psmouse_warn(psmouse,
"failed to query resolution data.\n");
}
@@ -1726,6 +1740,125 @@ int elantech_query_info(struct psmouse *psmouse,
return 0;
}
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+
+/*
+ * The newest Elantech device can use a secondary bus (over SMBus) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+ ELANTECH_SMBUS_NOT_SET = -1,
+ ELANTECH_SMBUS_OFF,
+ ELANTECH_SMBUS_ON,
+};
+
+static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
+ ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
+module_param_named(elantech_smbus, elantech_smbus, int, 0644);
+MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
+
+static int elantech_create_smbus(struct psmouse *psmouse,
+ struct elantech_device_info *info,
+ bool leave_breadcrumbs)
+{
+ struct elan_platform_data pdata = {
+ .trackpoint = info->has_trackpoint,
+ };
+ struct i2c_board_info smbus_board = {
+ I2C_BOARD_INFO("elan_i2c", 0x15),
+ .flags = I2C_CLIENT_HOST_NOTIFY,
+ };
+
+ return psmouse_smbus_init(psmouse, &smbus_board, &pdata,
+ sizeof(pdata), leave_breadcrumbs);
+}
+
+/**
+ * elantech_setup_smbus - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static int elantech_setup_smbus(struct psmouse *psmouse,
+ struct elantech_device_info *info,
+ bool leave_breadcrumbs)
+{
+ int error;
+
+ if (elantech_smbus == ELANTECH_SMBUS_OFF)
+ return -ENXIO;
+
+ if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
+ /*
+ * FIXME:
+ * constraint the I2C capable devices by using FW version,
+ * board version, or by using DMI matching
+ */
+ return -ENXIO;
+ }
+
+ psmouse_info(psmouse, "Trying to set up SMBus access\n");
+
+ error = elantech_create_smbus(psmouse, info, leave_breadcrumbs);
+ if (error) {
+ if (error == -EAGAIN)
+ psmouse_info(psmouse, "SMbus companion is not ready yet\n");
+ else
+ psmouse_err(psmouse, "unable to create intertouch device\n");
+
+ return error;
+ }
+
+ return 0;
+}
+
+static bool elantech_use_host_notify(struct psmouse *psmouse,
+ struct elantech_device_info *info)
+{
+ switch (info->bus) {
+ case ETP_BUS_PS2_ONLY:
+ /* expected case */
+ break;
+ case ETP_BUS_SMB_ALERT_ONLY:
+ /* fall-through */
+ case ETP_BUS_PS2_SMB_ALERT:
+ psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
+ break;
+ case ETP_BUS_SMB_HST_NTFY_ONLY:
+ /* fall-through */
+ case ETP_BUS_PS2_SMB_HST_NTFY:
+ return true;
+ default:
+ psmouse_dbg(psmouse,
+ "Ignoring SMBus bus provider %d.\n",
+ info->bus);
+ }
+
+ return false;
+}
+
+int elantech_init_smbus(struct psmouse *psmouse)
+{
+ struct elantech_device_info info;
+ int error = -EINVAL;
+
+ psmouse_reset(psmouse);
+
+ error = elantech_query_info(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+ if (info.hw_version < 4) {
+ error = -ENXIO;
+ goto init_fail;
+ }
+
+ return elantech_create_smbus(psmouse, &info, false);
+ init_fail:
+ psmouse_reset(psmouse);
+ return error;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
/*
* Initialize the touchpad and create sysfs entries
*/
@@ -1734,7 +1867,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
{
struct elantech_data *etd;
int i;
- int error;
+ int error = -EINVAL;
struct input_dev *tp_dev;
psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
@@ -1821,7 +1954,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
return error;
}
-int elantech_init(struct psmouse *psmouse)
+int elantech_init_ps2(struct psmouse *psmouse)
{
struct elantech_device_info info;
int error = -EINVAL;
@@ -1841,3 +1974,46 @@ int elantech_init(struct psmouse *psmouse)
psmouse_reset(psmouse);
return error;
}
+
+int elantech_init(struct psmouse *psmouse)
+{
+ struct elantech_device_info info;
+ int error = -EINVAL;
+
+ psmouse_reset(psmouse);
+
+ error = elantech_query_info(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+
+ if (elantech_use_host_notify(psmouse, &info)) {
+ if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
+ !IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
+ psmouse_warn(psmouse,
+ "The touchpad can support a better bus than the too old PS/2 protocol. "
+ "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
+ }
+ error = elantech_setup_smbus(psmouse, &info, true);
+ if (!error)
+ return PSMOUSE_ELANTECH_SMBUS;
+ }
+
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
+ error = elantech_setup_ps2(psmouse, &info);
+ if (error < 0) {
+ /*
+ * Not using any flavor of Elantech support, so clean up
+ * SMbus breadcrumbs, if any.
+ */
+ psmouse_smbus_cleanup(psmouse);
+ goto init_fail;
+ }
+
+ return PSMOUSE_ELANTECH;
+ init_fail:
+ psmouse_reset(psmouse);
+ return error;
+}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index 851df4ce6232..f9b1c485e8d9 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -106,6 +106,15 @@
*/
#define ETP_WEIGHT_VALUE 5
+/*
+ * Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
+ */
+#define ETP_BUS_PS2_ONLY 0
+#define ETP_BUS_SMB_ALERT_ONLY 1
+#define ETP_BUS_SMB_HST_NTFY_ONLY 2
+#define ETP_BUS_PS2_SMB_ALERT 3
+#define ETP_BUS_PS2_SMB_HST_NTFY 4
+
/*
* The base position for one finger, v4 hardware
*/
@@ -122,6 +131,7 @@ struct elantech_device_info {
unsigned int fw_version;
unsigned int x_res;
unsigned int y_res;
+ unsigned int bus;
bool paritycheck;
bool jumpy_cursor;
bool reports_pressure;
@@ -156,6 +166,7 @@ struct elantech_data {
#ifdef CONFIG_MOUSE_PS2_ELANTECH
int elantech_detect(struct psmouse *psmouse, bool set_properties);
+int elantech_init_ps2(struct psmouse *psmouse);
int elantech_init(struct psmouse *psmouse);
#else
static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
@@ -166,6 +177,19 @@ static inline int elantech_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
+static inline int elantech_init_ps2(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
#endif /* CONFIG_MOUSE_PS2_ELANTECH */
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+int elantech_init_smbus(struct psmouse *psmouse);
+#else
+static inline int elantech_init_smbus(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 8900c3166ebf..5ff5b1952be0 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.name = "ETPS/2",
.alias = "elantech",
.detect = elantech_detect,
- .init = elantech_init,
+ .init = elantech_init_ps2,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
+ {
+ .type = PSMOUSE_ELANTECH_SMBUS,
+ .name = "ETSMBus",
+ .alias = "elantech-smbus",
+ .detect = elantech_detect,
+ .init = elantech_init_smbus,
+ .smbus_companion = true,
},
#endif
#ifdef CONFIG_MOUSE_PS2_SENTELIC
@@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
/* Try Elantech touchpad */
if (max_proto > PSMOUSE_IMEX &&
psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
- &max_proto, set_properties, true)) {
- return PSMOUSE_ELANTECH;
+ &max_proto, set_properties, false)) {
+ if (!set_properties)
+ return PSMOUSE_ELANTECH;
+
+ ret = elantech_init(psmouse);
+ if (ret >= 0)
+ return ret;
}
if (max_proto > PSMOUSE_IMEX) {
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 71ac50082c8b..dd4ec1f602d7 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -68,6 +68,7 @@ enum psmouse_type {
PSMOUSE_VMMOUSE,
PSMOUSE_BYD,
PSMOUSE_SYNAPTICS_SMBUS,
+ PSMOUSE_ELANTECH_SMBUS,
PSMOUSE_AUTO /* This one should always be last */
};
--
2.14.3
The Elan touchpads over I2C/SMBus also can handle a
trackstick. Unfortunately, nothing tells us if the
device supports trackstick (the information lies in
the PS/2 node), so rely on a platform data to enable
or not the trackstick node.
Link: https://bugzilla.redhat.com/show_bug.cgi?id=1313939
Signed-off-by: Benjamin Tissoires <[email protected]>
---
changes in v2:
- renamed ETP_PT_REPORT_ID into ETP_TP_REPORT_ID (pass-through to track-point)
to stay more in line with the rest
- removed Host Notify support so amended to match parent
---
drivers/input/mouse/elan_i2c.h | 10 ++++
drivers/input/mouse/elan_i2c_core.c | 91 +++++++++++++++++++++++++++++++++++--
2 files changed, 98 insertions(+), 3 deletions(-)
diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h
index 599544c1a91c..8f9c97267bc9 100644
--- a/drivers/input/mouse/elan_i2c.h
+++ b/drivers/input/mouse/elan_i2c.h
@@ -87,4 +87,14 @@ struct elan_transport_ops {
extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
+/**
+ * struct elan_platform_data - system specific configuration info.
+ *
+ * @trackpoint - specify whether or not you want the trackstick input node to
+ * be created and handled by the driver.
+ */
+struct elan_platform_data {
+ bool trackpoint;
+};
+
#endif /* _ELAN_I2C_H */
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 75e757520ef0..8ed1e2a8a4dd 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -51,6 +51,7 @@
#define ETP_MAX_FINGERS 5
#define ETP_FINGER_DATA_LEN 5
#define ETP_REPORT_ID 0x5D
+#define ETP_TP_REPORT_ID 0x5E
#define ETP_REPORT_ID_OFFSET 2
#define ETP_TOUCH_INFO_OFFSET 3
#define ETP_FINGER_DATA_OFFSET 4
@@ -61,6 +62,7 @@
struct elan_tp_data {
struct i2c_client *client;
struct input_dev *input;
+ struct input_dev *tp_input; /* trackpoint input node */
struct regulator *vcc;
const struct elan_transport_ops *ops;
@@ -930,6 +932,34 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
input_sync(input);
}
+static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
+{
+ struct input_dev *input = data->tp_input;
+ u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
+ int x, y;
+
+ if (!data->tp_input) {
+ dev_warn_once(&data->client->dev,
+ "received a trackpoint report while no trackpoint device has been created.\n"
+ "Please report upstream.\n");
+ return;
+ }
+
+ input_report_key(input, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
+ input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
+
+ if ((packet[3] & 0x0F) == 0x06) {
+ x = packet[4] - (int)((packet[1]^0x80) << 1);
+ y = (int)((packet[2]^0x80) << 1) - packet[5];
+
+ input_report_rel(input, REL_X, x);
+ input_report_rel(input, REL_Y, y);
+ }
+
+ input_sync(input);
+}
+
static irqreturn_t elan_isr(int irq, void *dev_id)
{
struct elan_tp_data *data = dev_id;
@@ -951,11 +981,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
if (error)
goto out;
- if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
+ switch (report[ETP_REPORT_ID_OFFSET]) {
+ case ETP_REPORT_ID:
+ elan_report_absolute(data, report);
+ break;
+ case ETP_TP_REPORT_ID:
+ elan_report_trackpoint(data, report);
+ break;
+ default:
dev_err(dev, "invalid report id data (%x)\n",
report[ETP_REPORT_ID_OFFSET]);
- else
- elan_report_absolute(data, report);
+ }
out:
return IRQ_HANDLED;
@@ -966,6 +1002,37 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
* Elan initialization functions
******************************************************************
*/
+
+static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input;
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = "Elan TrackPoint";
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = ELAN_VENDOR_ID;
+ input->id.product = data->product_id;
+ input_set_drvdata(input, data);
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input->relbit[BIT_WORD(REL_X)] =
+ BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ input->keybit[BIT_WORD(BTN_LEFT)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+ BIT_MASK(BTN_RIGHT);
+
+ __set_bit(INPUT_PROP_POINTER, input->propbit);
+ __set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
+
+ data->tp_input = input;
+
+ return 0;
+}
+
static int elan_setup_input_device(struct elan_tp_data *data)
{
struct device *dev = &data->client->dev;
@@ -1042,10 +1109,12 @@ static void elan_remove_sysfs_groups(void *_data)
static int elan_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
+ struct elan_platform_data *pdata = dev_get_platdata(&client->dev);
const struct elan_transport_ops *transport_ops;
struct device *dev = &client->dev;
struct elan_tp_data *data;
unsigned long irqflags;
+ bool has_trackpoint = pdata && pdata->trackpoint;
int error;
if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_I2C) &&
@@ -1140,6 +1209,12 @@ static int elan_probe(struct i2c_client *client,
if (error)
return error;
+ if (has_trackpoint) {
+ error = elan_setup_trackpoint_input_device(data);
+ if (error)
+ return error;
+ }
+
/*
* Platform code (ACPI, DTS) should normally set up interrupt
* for us, but in case it did not let's fall back to using falling
@@ -1177,6 +1252,16 @@ static int elan_probe(struct i2c_client *client,
return error;
}
+ if (data->tp_input) {
+ error = input_register_device(data->tp_input);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to register TrackPoint input device: %d\n",
+ error);
+ return error;
+ }
+ }
+
/*
* Systems using device tree should set up wakeup via DTS,
* the rest will configure device as wakeup source by default.
--
2.14.3
In preparation for SMBus device support, move static device
information that we query form the touchpad upon initialization into
separate structure. This will allow us to query the device without
allocating memory first.
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/elantech.c | 271 ++++++++++++++++++++++++-----------------
drivers/input/mouse/elantech.h | 28 +++--
2 files changed, 175 insertions(+), 124 deletions(-)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index db47a5e1d114..d485664f1563 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -24,7 +24,7 @@
#define elantech_debug(fmt, ...) \
do { \
- if (etd->debug) \
+ if (etd->info.debug) \
psmouse_printk(KERN_DEBUG, psmouse, \
fmt, ##__VA_ARGS__); \
} while (0)
@@ -105,7 +105,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
if (reg > 0x11 && reg < 0x20)
return -1;
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
ps2_sliced_command(&psmouse->ps2dev, reg) ||
@@ -137,7 +137,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
if (rc)
psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg);
- else if (etd->hw_version != 4)
+ else if (etd->info.hw_version != 4)
*val = param[0];
else
*val = param[1];
@@ -160,7 +160,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
if (reg > 0x11 && reg < 0x20)
return -1;
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
ps2_sliced_command(&psmouse->ps2dev, reg) ||
@@ -237,7 +237,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
int fingers;
- if (etd->fw_version < 0x020000) {
+ if (etd->info.fw_version < 0x020000) {
/*
* byte 0: D U p1 p2 1 p3 R L
* byte 1: f 0 th tw x9 x8 y9 y8
@@ -252,7 +252,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
fingers = (packet[0] & 0xc0) >> 6;
}
- if (etd->jumpy_cursor) {
+ if (etd->info.jumpy_cursor) {
if (fingers != 1) {
etd->single_finger_reports = 0;
} else if (etd->single_finger_reports < 2) {
@@ -282,8 +282,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
psmouse_report_standard_buttons(dev, packet[0]);
- if (etd->fw_version < 0x020000 &&
- (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+ if (etd->info.fw_version < 0x020000 &&
+ (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) {
/* rocker up */
input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
/* rocker down */
@@ -391,7 +391,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
psmouse_report_standard_buttons(dev, packet[0]);
- if (etd->reports_pressure) {
+ if (etd->info.reports_pressure) {
input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width);
}
@@ -444,7 +444,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
default:
/* Dump unexpected packet sequences if debug=1 (default) */
- if (etd->debug == 1)
+ if (etd->info.debug == 1)
elantech_packet_dump(psmouse);
break;
@@ -523,7 +523,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
/* For clickpads map both buttons to BTN_LEFT */
- if (etd->fw_version & 0x001000)
+ if (etd->info.fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
else
psmouse_report_standard_buttons(dev, packet[0]);
@@ -541,7 +541,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
/* For clickpads map both buttons to BTN_LEFT */
- if (etd->fw_version & 0x001000)
+ if (etd->info.fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
else
psmouse_report_standard_buttons(dev, packet[0]);
@@ -669,7 +669,7 @@ static int elantech_packet_check_v1(struct psmouse *psmouse)
unsigned char p1, p2, p3;
/* Parity bits are placed differently */
- if (etd->fw_version < 0x020000) {
+ if (etd->info.fw_version < 0x020000) {
/* byte 0: D U p1 p2 1 p3 R L */
p1 = (packet[0] & 0x20) >> 5;
p2 = (packet[0] & 0x10) >> 4;
@@ -714,7 +714,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse)
* With all three cases, if the constant bits are not exactly what I
* expected, I consider them invalid.
*/
- if (etd->reports_pressure)
+ if (etd->info.reports_pressure)
return (packet[0] & 0x0c) == 0x04 &&
(packet[3] & 0x0f) == 0x02;
@@ -751,7 +751,7 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
* If the hardware flag 'crc_enabled' is set the packets have
* different signatures.
*/
- if (etd->crc_enabled) {
+ if (etd->info.crc_enabled) {
if ((packet[3] & 0x09) == 0x08)
return PACKET_V3_HEAD;
@@ -782,7 +782,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
return PACKET_TRACKPOINT;
/* This represents the version of IC body. */
- ic_version = (etd->fw_version & 0x0f0000) >> 16;
+ ic_version = (etd->info.fw_version & 0x0f0000) >> 16;
/*
* Sanity check based on the constant bits of a packet.
@@ -791,9 +791,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
* the IC body, but are the same for every packet,
* regardless of the type.
*/
- if (etd->crc_enabled)
+ if (etd->info.crc_enabled)
sanity_check = ((packet[3] & 0x08) == 0x00);
- else if (ic_version == 7 && etd->samples[1] == 0x2A)
+ else if (ic_version == 7 && etd->info.samples[1] == 0x2A)
sanity_check = ((packet[3] & 0x1c) == 0x10);
else
sanity_check = ((packet[0] & 0x0c) == 0x04 &&
@@ -827,12 +827,12 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
- if (etd->debug > 1)
+ if (etd->info.debug > 1)
elantech_packet_dump(psmouse);
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
- if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
+ if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse))
return PSMOUSE_BAD_DATA;
elantech_report_absolute_v1(psmouse);
@@ -843,7 +843,7 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
if (elantech_debounce_check_v2(psmouse))
return PSMOUSE_FULL_PACKET;
- if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
+ if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse))
return PSMOUSE_BAD_DATA;
elantech_report_absolute_v2(psmouse);
@@ -916,7 +916,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
int tries = ETP_READ_BACK_TRIES;
int rc = 0;
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
etd->reg_10 = 0x16;
etd->reg_11 = 0x8f;
@@ -939,7 +939,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
break;
case 3:
- if (etd->set_hw_resolution)
+ if (etd->info.set_hw_resolution)
etd->reg_10 = 0x0b;
else
etd->reg_10 = 0x01;
@@ -976,7 +976,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
if (rc) {
psmouse_err(psmouse,
"failed to read back register 0x10.\n");
- } else if (etd->hw_version == 1 &&
+ } else if (etd->info.hw_version == 1 &&
!(val & ETP_R10_ABSOLUTE_MODE)) {
psmouse_err(psmouse,
"touchpad refuses to switch to absolute mode.\n");
@@ -997,10 +997,11 @@ static int elantech_set_range(struct psmouse *psmouse,
unsigned int *width)
{
struct elantech_data *etd = psmouse->private;
+ struct elantech_device_info *info = &etd->info;
unsigned char param[3];
unsigned char traces;
- switch (etd->hw_version) {
+ switch (info->hw_version) {
case 1:
*x_min = ETP_XMIN_V1;
*y_min = ETP_YMIN_V1;
@@ -1009,9 +1010,9 @@ static int elantech_set_range(struct psmouse *psmouse,
break;
case 2:
- if (etd->fw_version == 0x020800 ||
- etd->fw_version == 0x020b00 ||
- etd->fw_version == 0x020030) {
+ if (info->fw_version == 0x020800 ||
+ info->fw_version == 0x020b00 ||
+ info->fw_version == 0x020030) {
*x_min = ETP_XMIN_V2;
*y_min = ETP_YMIN_V2;
*x_max = ETP_XMAX_V2;
@@ -1020,35 +1021,35 @@ static int elantech_set_range(struct psmouse *psmouse,
int i;
int fixed_dpi;
- i = (etd->fw_version > 0x020800 &&
- etd->fw_version < 0x020900) ? 1 : 2;
+ i = (info->fw_version > 0x020800 &&
+ info->fw_version < 0x020900) ? 1 : 2;
- if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
return -1;
fixed_dpi = param[1] & 0x10;
- if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
- if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+ if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
+ if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
return -1;
- *x_max = (etd->capabilities[1] - i) * param[1] / 2;
- *y_max = (etd->capabilities[2] - i) * param[2] / 2;
- } else if (etd->fw_version == 0x040216) {
+ *x_max = (info->capabilities[1] - i) * param[1] / 2;
+ *y_max = (info->capabilities[2] - i) * param[2] / 2;
+ } else if (info->fw_version == 0x040216) {
*x_max = 819;
*y_max = 405;
- } else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
+ } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
*x_max = 900;
*y_max = 500;
} else {
- *x_max = (etd->capabilities[1] - i) * 64;
- *y_max = (etd->capabilities[2] - i) * 64;
+ *x_max = (info->capabilities[1] - i) * 64;
+ *y_max = (info->capabilities[2] - i) * 64;
}
}
break;
case 3:
- if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
return -1;
*x_max = (0x0f & param[0]) << 8 | param[1];
@@ -1056,12 +1057,12 @@ static int elantech_set_range(struct psmouse *psmouse,
break;
case 4:
- if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
return -1;
*x_max = (0x0f & param[0]) << 8 | param[1];
*y_max = (0xf0 & param[0]) << 4 | param[2];
- traces = etd->capabilities[1];
+ traces = info->capabilities[1];
if ((traces < 2) || (traces > *x_max))
return -1;
@@ -1140,7 +1141,7 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private;
- if (etd->fw_version & 0x001000) {
+ if (etd->info.fw_version & 0x001000) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
__clear_bit(BTN_RIGHT, dev->keybit);
}
@@ -1176,6 +1177,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private;
+ struct elantech_device_info *info = &etd->info;
unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
unsigned int x_res = 31, y_res = 31;
@@ -1197,11 +1199,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
- switch (etd->hw_version) {
+ switch (info->hw_version) {
case 1:
/* Rocker button */
- if (etd->fw_version < 0x020000 &&
- (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+ if (info->fw_version < 0x020000 &&
+ (info->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
__set_bit(BTN_FORWARD, dev->keybit);
__set_bit(BTN_BACK, dev->keybit);
}
@@ -1214,11 +1216,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
/* fall through */
case 3:
- if (etd->hw_version == 3)
+ if (info->hw_version == 3)
elantech_set_buttonpad_prop(psmouse);
input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
- if (etd->reports_pressure) {
+ if (info->reports_pressure) {
input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
ETP_PMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
@@ -1267,7 +1269,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
input_abs_set_res(dev, ABS_X, x_res);
input_abs_set_res(dev, ABS_Y, y_res);
- if (etd->hw_version > 1) {
+ if (info->hw_version > 1) {
input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
}
@@ -1317,7 +1319,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
return err;
/* Do we need to preserve some bits for version 2 hardware too? */
- if (etd->hw_version == 1) {
+ if (etd->info.hw_version == 1) {
if (attr->reg == 0x10)
/* Force absolute mode always on */
value |= ETP_R10_ABSOLUTE_MODE;
@@ -1337,11 +1339,22 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
.field_offset = offsetof(struct elantech_data, _name), \
.reg = _register, \
}; \
- PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ PSMOUSE_DEFINE_ATTR(_name, 0644, \
&elantech_attr_##_name, \
elantech_show_int_attr, \
elantech_set_int_attr)
+#define ELANTECH_INFO_ATTR(_name) \
+ static struct elantech_attr_data elantech_attr_##_name = { \
+ .field_offset = offsetof(struct elantech_data, info) + \
+ offsetof(struct elantech_device_info, _name), \
+ .reg = 0, \
+ }; \
+ PSMOUSE_DEFINE_ATTR(_name, 0644, \
+ &elantech_attr_##_name, \
+ elantech_show_int_attr, \
+ elantech_set_int_attr)
+
ELANTECH_INT_ATTR(reg_07, 0x07);
ELANTECH_INT_ATTR(reg_10, 0x10);
ELANTECH_INT_ATTR(reg_11, 0x11);
@@ -1352,9 +1365,9 @@ ELANTECH_INT_ATTR(reg_23, 0x23);
ELANTECH_INT_ATTR(reg_24, 0x24);
ELANTECH_INT_ATTR(reg_25, 0x25);
ELANTECH_INT_ATTR(reg_26, 0x26);
-ELANTECH_INT_ATTR(debug, 0);
-ELANTECH_INT_ATTR(paritycheck, 0);
-ELANTECH_INT_ATTR(crc_enabled, 0);
+ELANTECH_INFO_ATTR(debug);
+ELANTECH_INFO_ATTR(paritycheck);
+ELANTECH_INFO_ATTR(crc_enabled);
static struct attribute *elantech_attrs[] = {
&psmouse_attr_reg_07.dattr.attr,
@@ -1588,25 +1601,25 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = {
/*
* determine hardware version and set some properties according to it.
*/
-static int elantech_set_properties(struct elantech_data *etd)
+static int elantech_set_properties(struct elantech_device_info *info)
{
/* This represents the version of IC body. */
- int ver = (etd->fw_version & 0x0f0000) >> 16;
+ int ver = (info->fw_version & 0x0f0000) >> 16;
/* Early version of Elan touchpads doesn't obey the rule. */
- if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
- etd->hw_version = 1;
+ if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
+ info->hw_version = 1;
else {
switch (ver) {
case 2:
case 4:
- etd->hw_version = 2;
+ info->hw_version = 2;
break;
case 5:
- etd->hw_version = 3;
+ info->hw_version = 3;
break;
case 6 ... 15:
- etd->hw_version = 4;
+ info->hw_version = 4;
break;
default:
return -1;
@@ -1614,100 +1627,86 @@ static int elantech_set_properties(struct elantech_data *etd)
}
/* decide which send_cmd we're gonna use early */
- etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
- synaptics_send_cmd;
+ info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
+ synaptics_send_cmd;
/* Turn on packet checking by default */
- etd->paritycheck = 1;
+ info->paritycheck = 1;
/*
* This firmware suffers from misreporting coordinates when
* a touch action starts causing the mouse cursor or scrolled page
* to jump. Enable a workaround.
*/
- etd->jumpy_cursor =
- (etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
+ info->jumpy_cursor =
+ (info->fw_version == 0x020022 || info->fw_version == 0x020600);
- if (etd->hw_version > 1) {
+ if (info->hw_version > 1) {
/* For now show extra debug information */
- etd->debug = 1;
+ info->debug = 1;
- if (etd->fw_version >= 0x020800)
- etd->reports_pressure = true;
+ if (info->fw_version >= 0x020800)
+ info->reports_pressure = true;
}
/*
* The signatures of v3 and v4 packets change depending on the
* value of this hardware flag.
*/
- etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
- dmi_check_system(elantech_dmi_force_crc_enabled);
+ info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 ||
+ dmi_check_system(elantech_dmi_force_crc_enabled);
/* Enable real hardware resolution on hw_version 3 ? */
- etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
+ info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
return 0;
}
-/*
- * Initialize the touchpad and create sysfs entries
- */
-int elantech_init(struct psmouse *psmouse)
+int elantech_query_info(struct psmouse *psmouse,
+ struct elantech_device_info *info)
{
- struct elantech_data *etd;
- int i;
- int error = -EINVAL;
unsigned char param[3];
- struct input_dev *tp_dev;
-
- psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
- if (!etd)
- return -ENOMEM;
-
- psmouse_reset(psmouse);
-
- etd->parity[0] = 1;
- for (i = 1; i < 256; i++)
- etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
/*
* Do the version query again so we can store the result
*/
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
psmouse_err(psmouse, "failed to query firmware version.\n");
- goto init_fail;
+ return -EINVAL;
}
- etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
+ info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
- if (elantech_set_properties(etd)) {
+ if (elantech_set_properties(info)) {
psmouse_err(psmouse, "unknown hardware version, aborting...\n");
- goto init_fail;
+ return -EINVAL;
}
psmouse_info(psmouse,
"assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
- etd->hw_version, param[0], param[1], param[2]);
+ info->hw_version, param[0], param[1], param[2]);
- if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
- etd->capabilities)) {
+ if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+ info->capabilities)) {
psmouse_err(psmouse, "failed to query capabilities.\n");
- goto init_fail;
+ return -EINVAL;
}
psmouse_info(psmouse,
"Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
- etd->capabilities[0], etd->capabilities[1],
- etd->capabilities[2]);
+ info->capabilities[0], info->capabilities[1],
+ info->capabilities[2]);
- if (etd->hw_version != 1) {
- if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) {
+ if (info->hw_version != 1) {
+ if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
psmouse_err(psmouse, "failed to query sample data\n");
- goto init_fail;
+ return -EINVAL;
}
psmouse_info(psmouse,
"Elan sample query result %02x, %02x, %02x\n",
- etd->samples[0], etd->samples[1], etd->samples[2]);
+ info->samples[0],
+ info->samples[1],
+ info->samples[2]);
}
- if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) {
+ if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
/*
* This module has a bug which makes absolute mode
* unusable, so let's abort so we'll be using standard
@@ -1715,16 +1714,43 @@ int elantech_init(struct psmouse *psmouse)
*/
psmouse_info(psmouse,
"absolute mode broken, forcing standard PS/2 protocol\n");
- goto init_fail;
+ return -ENODEV;
}
+ /* The MSB indicates the presence of the trackpoint */
+ info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
+
+ return 0;
+}
+
+/*
+ * Initialize the touchpad and create sysfs entries
+ */
+static int elantech_setup_ps2(struct psmouse *psmouse,
+ struct elantech_device_info *info)
+{
+ struct elantech_data *etd;
+ int i;
+ int error;
+ struct input_dev *tp_dev;
+
+ psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
+ if (!etd)
+ return -ENOMEM;
+
+ etd->info = *info;
+
+ etd->parity[0] = 1;
+ for (i = 1; i < 256; i++)
+ etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+
if (elantech_set_absolute_mode(psmouse)) {
psmouse_err(psmouse,
"failed to put touchpad into absolute mode.\n");
goto init_fail;
}
- if (etd->fw_version == 0x381f17) {
+ if (info->fw_version == 0x381f17) {
etd->original_set_rate = psmouse->set_rate;
psmouse->set_rate = elantech_set_rate_restore_reg_07;
}
@@ -1743,8 +1769,7 @@ int elantech_init(struct psmouse *psmouse)
goto init_fail;
}
- /* The MSB indicates the presence of the trackpoint */
- if ((etd->capabilities[0] & 0x80) == 0x80) {
+ if (info->has_trackpoint) {
tp_dev = input_allocate_device();
if (!tp_dev) {
@@ -1780,7 +1805,7 @@ int elantech_init(struct psmouse *psmouse)
psmouse->protocol_handler = elantech_process_byte;
psmouse->disconnect = elantech_disconnect;
psmouse->reconnect = elantech_reconnect;
- psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
+ psmouse->pktsize = info->hw_version > 1 ? 6 : 4;
return 0;
init_fail_tp_reg:
@@ -1789,7 +1814,27 @@ int elantech_init(struct psmouse *psmouse)
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group);
init_fail:
- psmouse_reset(psmouse);
kfree(etd);
return error;
}
+
+int elantech_init(struct psmouse *psmouse)
+{
+ struct elantech_device_info info;
+ int error = -EINVAL;
+
+ psmouse_reset(psmouse);
+
+ error = elantech_query_info(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+ error = elantech_setup_ps2(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+ return 0;
+ init_fail:
+ psmouse_reset(psmouse);
+ return error;
+}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index e1cbf409d9c8..d8ac27fe4597 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -114,6 +114,22 @@ struct finger_pos {
unsigned int y;
};
+struct elantech_device_info {
+ unsigned char capabilities[3];
+ unsigned char samples[3];
+ unsigned char debug;
+ unsigned char hw_version;
+ unsigned int fw_version;
+ bool paritycheck;
+ bool jumpy_cursor;
+ bool reports_pressure;
+ bool crc_enabled;
+ bool set_hw_resolution;
+ bool has_trackpoint;
+ int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
+ unsigned char *param);
+};
+
struct elantech_data {
struct input_dev *tp_dev; /* Relative device for trackpoint */
char tp_phys[32];
@@ -127,22 +143,12 @@ struct elantech_data {
unsigned char reg_24;
unsigned char reg_25;
unsigned char reg_26;
- unsigned char debug;
- unsigned char capabilities[3];
- unsigned char samples[3];
- bool paritycheck;
- bool jumpy_cursor;
- bool reports_pressure;
- bool crc_enabled;
- bool set_hw_resolution;
- unsigned char hw_version;
- unsigned int fw_version;
unsigned int single_finger_reports;
unsigned int y_max;
unsigned int width;
struct finger_pos mt[ETP_MAX_FINGERS];
unsigned char parity[256];
- int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
+ struct elantech_device_info info;
void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
};
--
2.14.3
This time, Lenovo decided to go with different pieces in its latest series
of Thinkpads.
For those we have been able to test:
- the T480 is using Synaptics with an IBM trackpoint
-> it behaves properly with or without intertouch, there is no point
not using RMI4
- the X1 Carbon 6th gen is using Synaptics with an IBM trackpoint
-> the touchpad doesn't behave properly under PS/2 so we have to
switch it to RMI4 if we do not want to have disappointed users
- the X280 is using Synaptics with an ALPS trackpoint
-> the recent fixes in the trackpoint handling fixed it so upstream
now works fine with or without RMI4, and there is no point not
using RMI4
- the T480s is using an Elan touchpad, so that's a different story
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/synaptics.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 60f2c463d1cc..14a1188561aa 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -173,6 +173,9 @@ static const char * const smbus_pnp_ids[] = {
"LEN0046", /* X250 */
"LEN004a", /* W541 */
"LEN200f", /* T450s */
+ "LEN0071", /* T480 */
+ "LEN0092", /* X1 Carbon 6th gen */
+ "LEN0097", /* X280 -> ALPS trackpoint */
NULL
};
--
2.14.3
New ICs (like the one on the Lenovo T480s) answer to
ETP_SMBUS_IAP_VERSION_CMD 4 bytes instead of 3. This corrupts the stack
as i2c_smbus_read_block_data() uses the values returned by the i2c
device to know how many data it need to return.
i2c_smbus_read_block_data() can read up to 32 bytes (I2C_SMBUS_BLOCK_MAX)
and there is no safeguard on how many bytes are provided in the return
value. Ensure we always have enough space for any future firmware.
Also 0-initialize the values to prevent any access to uninitialized memory.
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/mouse/elan_i2c_smbus.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c
index 29f99529b187..cfcb32559925 100644
--- a/drivers/input/mouse/elan_i2c_smbus.c
+++ b/drivers/input/mouse/elan_i2c_smbus.c
@@ -130,7 +130,7 @@ static int elan_smbus_get_baseline_data(struct i2c_client *client,
bool max_baseline, u8 *value)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
max_baseline ?
@@ -149,7 +149,7 @@ static int elan_smbus_get_version(struct i2c_client *client,
bool iap, u8 *version)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
iap ? ETP_SMBUS_IAP_VERSION_CMD :
@@ -170,7 +170,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client,
u8 *clickpad)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_SM_VERSION_CMD, val);
@@ -188,7 +188,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client,
static int elan_smbus_get_product_id(struct i2c_client *client, u16 *id)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_UNIQUEID_CMD, val);
@@ -205,7 +205,7 @@ static int elan_smbus_get_checksum(struct i2c_client *client,
bool iap, u16 *csum)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
iap ? ETP_SMBUS_FW_CHECKSUM_CMD :
@@ -226,7 +226,7 @@ static int elan_smbus_get_max(struct i2c_client *client,
{
int ret;
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
if (ret != 3) {
@@ -246,7 +246,7 @@ static int elan_smbus_get_resolution(struct i2c_client *client,
{
int ret;
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val);
if (ret != 3) {
@@ -267,7 +267,7 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
{
int ret;
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val);
if (ret != 3) {
@@ -294,7 +294,7 @@ static int elan_smbus_iap_get_mode(struct i2c_client *client,
{
int error;
u16 constant;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client, ETP_SMBUS_IAP_CTRL_CMD, val);
if (error < 0) {
@@ -345,7 +345,7 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client)
int len;
int error;
enum tp_mode mode;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
u16 password;
@@ -419,7 +419,7 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
struct device *dev = &client->dev;
int error;
u16 result;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
/*
* Due to the limitation of smbus protocol limiting
--
2.14.3
Hi Benjamin,
On Thu, Apr 05, 2018 at 03:25:32PM +0200, Benjamin Tissoires wrote:
> The Elan touchpads over I2C/SMBus also can handle a
> trackstick. Unfortunately, nothing tells us if the
> device supports trackstick (the information lies in
> the PS/2 node), so rely on a platform data to enable
> or not the trackstick node.
Hmm, I'd prefer if we did not add more platform data but rather user
generic device properties.
Thanks.
--
Dmitry
Hi Benjamin,
On Thu, Apr 05, 2018 at 03:25:29PM +0200, Benjamin Tissoires wrote:
> Hi Dmitry,
>
> well, this year, Lenovo gave us a surprise and decided to not use
> the same touchpad/trackstick in all its model. And by default, the
> support under Linux is less than ideal.
>
> Please find a series that should fix those issues. Compared to the 60
> series, there do not seem to e BIOS table issues this time, and suspend/resume
> works fine thanks to your latest trackstick fixes.
>
> The T480s is a different beast, as it uses an Elan touchpad.
> I have been carrying the patches 3-6 for a while and tested previous
> versions on various Elan PS/2 hardware without an issue as far as I
> could tell. I was lacking tests from users with SMBus as all the laptops
> I tried where puer PS/2.
>
> Anyway, it would be cool if you could have a look at the series.
I am mostly happy with the series, but I would love to hear KT's take on
it.
Thanks.
--
Dmitry
Hi Dmitry,
On Fri, Apr 6, 2018 at 1:51 AM, Dmitry Torokhov
<[email protected]> wrote:
> Hi Benjamin,
>
> On Thu, Apr 05, 2018 at 03:25:29PM +0200, Benjamin Tissoires wrote:
>> Hi Dmitry,
>>
>> well, this year, Lenovo gave us a surprise and decided to not use
>> the same touchpad/trackstick in all its model. And by default, the
>> support under Linux is less than ideal.
>>
>> Please find a series that should fix those issues. Compared to the 60
>> series, there do not seem to e BIOS table issues this time, and suspend/resume
>> works fine thanks to your latest trackstick fixes.
>>
>> The T480s is a different beast, as it uses an Elan touchpad.
>> I have been carrying the patches 3-6 for a while and tested previous
>> versions on various Elan PS/2 hardware without an issue as far as I
>> could tell. I was lacking tests from users with SMBus as all the laptops
>> I tried where puer PS/2.
>>
>> Anyway, it would be cool if you could have a look at the series.
>
> I am mostly happy with the series, but I would love to hear KT's take on
> it.
thanks for the quick review.
I worked closely with KT for this series. He helped me a lot for the
tiny firmware changes that were required. However, quoting his email
from Tuesday:
"There will be a spring vacation in Taiwan from tomorrow." I guess we
won't hear from him until the end of next week as we always have a
backlog of urgent things to do after holidays...
Cheers,
Benjamin
On Fri, Apr 6, 2018 at 1:46 AM, Dmitry Torokhov
<[email protected]> wrote:
> Hi Benjamin,
>
> On Thu, Apr 05, 2018 at 03:25:32PM +0200, Benjamin Tissoires wrote:
>> The Elan touchpads over I2C/SMBus also can handle a
>> trackstick. Unfortunately, nothing tells us if the
>> device supports trackstick (the information lies in
>> the PS/2 node), so rely on a platform data to enable
>> or not the trackstick node.
>
> Hmm, I'd prefer if we did not add more platform data but rather user
> generic device properties.
>
Fair enough, I'll work on it for v2.
Cheers,
Benjamin
Hi Benjamin,
I love your patch! Perhaps something to improve:
[auto build test WARNING on next-20180405]
[cannot apply to input/next v4.16 v4.16-rc7 v4.16-rc6 v4.16]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Benjamin-Tissoires/Input-support-for-latest-Lenovo-thinkpads-series-80/20180406-110729
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
sparse warnings: (new ones prefixed by >>)
>> drivers/input/mouse/elantech.c:1665:5: sparse: symbol 'elantech_query_info' was not declared. Should it be static?
drivers/input/mouse/elantech.c: In function 'elantech_init':
drivers/input/mouse/elantech.c:1839:9: warning: 'error' may be used uninitialized in this function [-Wmaybe-uninitialized]
return error;
^~~~~
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Fixes: 3fedcdbcc4b9 ("Input: elantech - split device info into a separate structure")
Signed-off-by: Fengguang Wu <[email protected]>
---
elantech.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index d485664..980dfd7 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1662,8 +1662,8 @@ static int elantech_set_properties(struct elantech_device_info *info)
return 0;
}
-int elantech_query_info(struct psmouse *psmouse,
- struct elantech_device_info *info)
+static int elantech_query_info(struct psmouse *psmouse,
+ struct elantech_device_info *info)
{
unsigned char param[3];
Hi Benjamin,
Thanks so much for your patch.
I have tested them for Elan Gen5/Gen6(new) touchpad with SMbus/PS2.
It works fine in my thinkpad so far but I find an issue today after lid-close/open.
I am not sure if you can see it in T480S , I "guess" it may be relative to i2c_i801.
The lid-close will enter deep sleep and cut touchpad power.
I can see the resume flow after lid-open and SMbus-initial try to request hello package but fail.
Strangely, I can't see any SMbus host signal on LA scope after power-on.
I can't switch to SMbus after rmmod/modprobe psmouse because error happen in elantech_create_smbus.
It will be recovered only if I rmmod/modprobe i2c_i801 first.
Do you have any idea about it?
Thanks
KT
-----Original Message-----
From: Benjamin Tissoires [mailto:[email protected]]
Sent: Friday, April 06, 2018 2:51 PM
To: Dmitry Torokhov
Cc: 廖崇榮; Oliver Haessler; Benjamin Berg; open list:HID CORE LAYER; lkml
Subject: Re: [PATCH 0/8] Input: support for latest Lenovo thinkpads (series 80)
Hi Dmitry,
On Fri, Apr 6, 2018 at 1:51 AM, Dmitry Torokhov <[email protected]> wrote:
> Hi Benjamin,
>
> On Thu, Apr 05, 2018 at 03:25:29PM +0200, Benjamin Tissoires wrote:
>> Hi Dmitry,
>>
>> well, this year, Lenovo gave us a surprise and decided to not use the
>> same touchpad/trackstick in all its model. And by default, the
>> support under Linux is less than ideal.
>>
>> Please find a series that should fix those issues. Compared to the 60
>> series, there do not seem to e BIOS table issues this time, and
>> suspend/resume works fine thanks to your latest trackstick fixes.
>>
>> The T480s is a different beast, as it uses an Elan touchpad.
>> I have been carrying the patches 3-6 for a while and tested previous
>> versions on various Elan PS/2 hardware without an issue as far as I
>> could tell. I was lacking tests from users with SMBus as all the
>> laptops I tried where puer PS/2.
>>
>> Anyway, it would be cool if you could have a look at the series.
>
> I am mostly happy with the series, but I would love to hear KT's take
> on it.
thanks for the quick review.
I worked closely with KT for this series. He helped me a lot for the tiny firmware changes that were required. However, quoting his email from Tuesday:
"There will be a spring vacation in Taiwan from tomorrow." I guess we won't hear from him until the end of next week as we always have a backlog of urgent things to do after holidays...
Cheers,
Benjamin
Hi KT,
On Tue, Apr 10, 2018 at 7:45 AM, 廖崇榮 <[email protected]> wrote:
> Hi Benjamin,
>
> Thanks so much for your patch.
>
> I have tested them for Elan Gen5/Gen6(new) touchpad with SMbus/PS2.
> It works fine in my thinkpad so far but I find an issue today after lid-close/open.
>
> I am not sure if you can see it in T480S , I "guess" it may be relative to i2c_i801.
>
> The lid-close will enter deep sleep and cut touchpad power.
> I can see the resume flow after lid-open and SMbus-initial try to request hello package but fail.
> Strangely, I can't see any SMbus host signal on LA scope after power-on.
That's weird. I do not see this, by either closing the lid or directly
calling 'systemctl suspend'.
On my system, i2c-i801 is also compiled as a module but psmouse is not
(directly in vmlinuz).
>
> I can't switch to SMbus after rmmod/modprobe psmouse because error happen in elantech_create_smbus.
If the SMBus adapter is failing, it is somewhat expected. Reloading
psmouse will force a re-trigger of the SMBus probe function, but if
the underlying communication fails, there is no way for psmouse to
know it failed, so the PS/2 node will disappear. And the SMBus device
will not be there.
> It will be recovered only if I rmmod/modprobe i2c_i801 first.
Just to be sure, what happens if you rmmod/modprobe elan_i2c instead
of i2c_i801?
And which kernel are you running? On a vanilla 4.16 + Dmitry's next
branch I do not see such issues.
>
> Do you have any idea about it?
If reloading elan_i2c doesn't fix the situation, it must be in
i2c_i801. But this is weird that this happens on your platform but not
on my t480s going into S3.
Cheers,
Benjamin
>
> Thanks
> KT
> -----Original Message-----
> From: Benjamin Tissoires [mailto:[email protected]]
> Sent: Friday, April 06, 2018 2:51 PM
> To: Dmitry Torokhov
> Cc: 廖崇榮; Oliver Haessler; Benjamin Berg; open list:HID CORE LAYER; lkml
> Subject: Re: [PATCH 0/8] Input: support for latest Lenovo thinkpads (series 80)
>
> Hi Dmitry,
>
> On Fri, Apr 6, 2018 at 1:51 AM, Dmitry Torokhov <[email protected]> wrote:
>> Hi Benjamin,
>>
>> On Thu, Apr 05, 2018 at 03:25:29PM +0200, Benjamin Tissoires wrote:
>>> Hi Dmitry,
>>>
>>> well, this year, Lenovo gave us a surprise and decided to not use the
>>> same touchpad/trackstick in all its model. And by default, the
>>> support under Linux is less than ideal.
>>>
>>> Please find a series that should fix those issues. Compared to the 60
>>> series, there do not seem to e BIOS table issues this time, and
>>> suspend/resume works fine thanks to your latest trackstick fixes.
>>>
>>> The T480s is a different beast, as it uses an Elan touchpad.
>>> I have been carrying the patches 3-6 for a while and tested previous
>>> versions on various Elan PS/2 hardware without an issue as far as I
>>> could tell. I was lacking tests from users with SMBus as all the
>>> laptops I tried where puer PS/2.
>>>
>>> Anyway, it would be cool if you could have a look at the series.
>>
>> I am mostly happy with the series, but I would love to hear KT's take
>> on it.
>
> thanks for the quick review.
> I worked closely with KT for this series. He helped me a lot for the tiny firmware changes that were required. However, quoting his email from Tuesday:
> "There will be a spring vacation in Taiwan from tomorrow." I guess we won't hear from him until the end of next week as we always have a backlog of urgent things to do after holidays...
>
> Cheers,
> Benjamin
>
Hi Benjamin,
-----Original Message-----
From: Benjamin Tissoires [mailto:[email protected]]
Sent: Tuesday, April 10, 2018 3:35 PM
To: 廖崇榮
Cc: Dmitry Torokhov; Oliver Haessler; Benjamin Berg; open list:HID CORE LAYER; lkml
Subject: Re: [PATCH 0/8] Input: support for latest Lenovo thinkpads (series 80)
Hi KT,
On Tue, Apr 10, 2018 at 7:45 AM, 廖崇榮 <[email protected]> wrote:
> Hi Benjamin,
>
> Thanks so much for your patch.
>
> I have tested them for Elan Gen5/Gen6(new) touchpad with SMbus/PS2.
> It works fine in my thinkpad so far but I find an issue today after lid-close/open.
>
> I am not sure if you can see it in T480S , I "guess" it may be relative to i2c_i801.
>
> The lid-close will enter deep sleep and cut touchpad power.
> I can see the resume flow after lid-open and SMbus-initial try to request hello package but fail.
> Strangely, I can't see any SMbus host signal on LA scope after power-on.
That's weird. I do not see this, by either closing the lid or directly calling 'systemctl suspend'.
On my system, i2c-i801 is also compiled as a module but psmouse is not (directly in vmlinuz).
[KT] : It's good to know your system doesn't meet this problem.
My SMbus laptop is an engineer sample in 2015, and PM tell me that NFC attaches to the same bus.
I guess it's a single case because it's not a stable platform.
>
> I can't switch to SMbus after rmmod/modprobe psmouse because error happen in elantech_create_smbus.
If the SMBus adapter is failing, it is somewhat expected. Reloading psmouse will force a re-trigger of the SMBus probe function, but if the underlying communication fails, there is no way for psmouse to know it failed, so the PS/2 node will disappear. And the SMBus device will not be there.
[KT] : It switch to PS/2 interface after rmmod/modprobe psmouse.
> It will be recovered only if I rmmod/modprobe i2c_i801 first.
Just to be sure, what happens if you rmmod/modprobe elan_i2c instead of i2c_i801?
[KT] :I tried rmmod/modprobe elan_i2c first , but can't recover touchpad.
No error printed in the dmesg. I will add more message to check it later.
And which kernel are you running? On a vanilla 4.16 + Dmitry's next branch I do not see such issues.
[KT]: I use 4.15, I may try 4.16 later.
>
> Do you have any idea about it?
If reloading elan_i2c doesn't fix the situation, it must be in i2c_i801. But this is weird that this happens on your platform but not on my t480s going into S3.
[KT] I think so. As I mention that I can't see bus signal on scope. That make me guess it's the bus adaptor issue.
It's an early-stage engineer system, power Issue will be expected.
Cheers,
Benjamin
>
> Thanks
> KT
> -----Original Message-----
> From: Benjamin Tissoires [mailto:[email protected]]
> Sent: Friday, April 06, 2018 2:51 PM
> To: Dmitry Torokhov
> Cc: 廖崇榮; Oliver Haessler; Benjamin Berg; open list:HID CORE LAYER;
> lkml
> Subject: Re: [PATCH 0/8] Input: support for latest Lenovo thinkpads
> (series 80)
>
> Hi Dmitry,
>
> On Fri, Apr 6, 2018 at 1:51 AM, Dmitry Torokhov <[email protected]> wrote:
>> Hi Benjamin,
>>
>> On Thu, Apr 05, 2018 at 03:25:29PM +0200, Benjamin Tissoires wrote:
>>> Hi Dmitry,
>>>
>>> well, this year, Lenovo gave us a surprise and decided to not use
>>> the same touchpad/trackstick in all its model. And by default, the
>>> support under Linux is less than ideal.
>>>
>>> Please find a series that should fix those issues. Compared to the
>>> 60 series, there do not seem to e BIOS table issues this time, and
>>> suspend/resume works fine thanks to your latest trackstick fixes.
>>>
>>> The T480s is a different beast, as it uses an Elan touchpad.
>>> I have been carrying the patches 3-6 for a while and tested previous
>>> versions on various Elan PS/2 hardware without an issue as far as I
>>> could tell. I was lacking tests from users with SMBus as all the
>>> laptops I tried where puer PS/2.
>>>
>>> Anyway, it would be cool if you could have a look at the series.
>>
>> I am mostly happy with the series, but I would love to hear KT's take
>> on it.
>
> thanks for the quick review.
> I worked closely with KT for this series. He helped me a lot for the tiny firmware changes that were required. However, quoting his email from Tuesday:
> "There will be a spring vacation in Taiwan from tomorrow." I guess we won't hear from him until the end of next week as we always have a backlog of urgent things to do after holidays...
>
> Cheers,
> Benjamin
>