From d4b347b29b4d14647c7394f7167bf6785dc98e50 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 7 Nov 2011 19:53:15 -0800 Subject: Input: ALPS - move protocol information to Documentation In preparation for new protocol support, move the protocol information currently documented in alps.c to Documentation/input/alps.txt, where it can be expanded without cluttering up the driver. Signed-off-by: Seth Forshee Acked-by: Chase Douglas Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 003587c71f43..19d09431addd 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -67,42 +67,7 @@ static const struct alps_model_info alps_model_data[] = { * isn't valid per PS/2 spec. */ -/* - * PS/2 packet format - * - * byte 0: 0 0 YSGN XSGN 1 M R L - * byte 1: X7 X6 X5 X4 X3 X2 X1 X0 - * byte 2: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 - * - * Note that the device never signals overflow condition. - * - * ALPS absolute Mode - new format - * - * byte 0: 1 ? ? ? 1 ? ? ? - * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 - * byte 2: 0 x10 x9 x8 x7 ? fin ges - * byte 3: 0 y9 y8 y7 1 M R L - * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 - * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 - * - * Dualpoint device -- interleaved packet format - * - * byte 0: 1 1 0 0 1 1 1 1 - * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 - * byte 2: 0 x10 x9 x8 x7 0 fin ges - * byte 3: 0 0 YSGN XSGN 1 1 1 1 - * byte 4: X7 X6 X5 X4 X3 X2 X1 X0 - * byte 5: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 - * byte 6: 0 y9 y8 y7 1 m r l - * byte 7: 0 y6 y5 y4 y3 y2 y1 y0 - * byte 8: 0 z6 z5 z4 z3 z2 z1 z0 - * - * CAPITALS = stick, miniscules = touchpad - * - * ?'s can have different meanings on different models, - * such as wheel rotation, extra buttons, stick buttons - * on a dualpoint, etc. - */ +/* Packet formats are described in Documentation/input/alps.txt */ static bool alps_is_valid_first_byte(const struct alps_model_info *model, unsigned char data) -- cgit v1.2.3 From fa629ef5222193214da9a2b3c94369f79353bec9 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 7 Nov 2011 19:53:24 -0800 Subject: Input: ALPS - add protocol version field in alps_model_info In preparation for adding support for more ALPS protocol versions, add a field for the protocol version to the model info instead of using a field in the flags. OLDPROTO and !OLDPROTO are now called version 1 and version 2, repsectively. Signed-off-by: Seth Forshee Acked-by: Chase Douglas Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 47 +++++++++++++++++++++++----------------------- drivers/input/mouse/alps.h | 4 ++++ 2 files changed, 27 insertions(+), 24 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 19d09431addd..77b776d99377 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -23,7 +23,6 @@ #include "psmouse.h" #include "alps.h" -#define ALPS_OLDPROTO 0x01 /* old style input */ #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ #define ALPS_PASS 0x04 /* device has a pass-through port */ @@ -35,30 +34,30 @@ 6-byte ALPS packet */ static const struct alps_model_info alps_model_data[] = { - { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ - { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ - { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, - { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, - { { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 }, /* HP ze1115 */ - { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, - { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, - { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ - { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ - { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ - { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, - { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ - { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ - { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, - { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ - { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ - { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, - { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ + { { 0x32, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ + { { 0x33, 0x02, 0x0a }, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ + { { 0x53, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x53, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x60, 0x03, 0xc8 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ + { { 0x63, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x28 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ + { { 0x63, 0x02, 0x3c }, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ + { { 0x63, 0x02, 0x50 }, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x64 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x03, 0xc8 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ + { { 0x73, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x73, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ + { { 0x20, 0x02, 0x0e }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ + { { 0x22, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + { { 0x22, 0x02, 0x14 }, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ - { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, + { { 0x62, 0x02, 0x14 }, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, - { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ - { { 0x52, 0x01, 0x14 }, 0xff, 0xff, - ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ + { { 0x73, 0x02, 0x50 }, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ + { { 0x52, 0x01, 0x14 }, ALPS_PROTO_V2, 0xff, 0xff, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ }; /* @@ -112,7 +111,7 @@ static void alps_process_packet(struct psmouse *psmouse) int x, y, z, ges, fin, left, right, middle; int back = 0, forward = 0; - if (model->flags & ALPS_OLDPROTO) { + if (model->proto_version == ALPS_PROTO_V1) { left = packet[2] & 0x10; right = packet[2] & 0x08; middle = 0; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 904ed8b3c8be..4ce9bba6a3cd 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,8 +12,12 @@ #ifndef _ALPS_H #define _ALPS_H +#define ALPS_PROTO_V1 0 +#define ALPS_PROTO_V2 1 + struct alps_model_info { unsigned char signature[3]; + unsigned char proto_version; unsigned char byte0, mask0; unsigned char flags; }; -- cgit v1.2.3 From b46615fe9215214ac00e26d35fc54dbe1c510803 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 7 Nov 2011 19:53:30 -0800 Subject: Input: ALPS - remove assumptions about packet size In preparation for version 4 protocol support, which has 8-byte data packets, remove all hard-coded assumptions about packet size and use psmouse->pktsize instead. Signed-off-by: Seth Forshee Acked-by: Chase Douglas Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 77b776d99377..44a0a712aaa2 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -308,7 +308,7 @@ static void alps_flush_packet(unsigned long data) serio_pause_rx(psmouse->ps2dev.serio); - if (psmouse->pktcnt == 6) { + if (psmouse->pktcnt == psmouse->pktsize) { /* * We did not any more data in reasonable amount of time. @@ -359,8 +359,8 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; } - /* Bytes 2 - 6 should have 0 in the highest bit */ - if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 && + /* Bytes 2 - pktsize should have 0 in the highest bit */ + if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", psmouse->pktcnt - 1, @@ -368,7 +368,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; } - if (psmouse->pktcnt == 6) { + if (psmouse->pktcnt == psmouse->pktsize) { alps_process_packet(psmouse); return PSMOUSE_FULL_PACKET; } @@ -529,7 +529,7 @@ static int alps_tap_mode(struct psmouse *psmouse, int enable) static int alps_poll(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - unsigned char buf[6]; + unsigned char buf[sizeof(psmouse->packet)]; bool poll_failed; if (priv->i->flags & ALPS_PASS) -- cgit v1.2.3 From 25bded7cd60fa460e520e9f819bd06f4c5cb53f0 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 7 Nov 2011 19:53:36 -0800 Subject: Input: ALPS - add support for protocol versions 3 and 4 This patch adds support for two ALPS touchpad protocols not supported currently by the driver, which I am arbitrarily naming version 3 and version 4. Support is single-touch only at this time, although both protocols are capable of limited multitouch support. Thanks to Andrew Skalski, who did the initial reverse-engineering of the v3 protocol. Signed-off-by: Seth Forshee Acked-by: Chase Douglas Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 791 ++++++++++++++++++++++++++++++++++++++++-- drivers/input/mouse/alps.h | 14 + drivers/input/mouse/psmouse.h | 1 + 3 files changed, 768 insertions(+), 38 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 44a0a712aaa2..a0248fd62ef8 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -23,6 +23,50 @@ #include "psmouse.h" #include "alps.h" +/* + * Definitions for ALPS version 3 and 4 command mode protocol + */ +#define ALPS_CMD_NIBBLE_10 0x01f2 + +static const struct alps_nibble_commands alps_v3_nibble_commands[] = { + { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ + { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ + { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ + { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ + { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ + { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ + { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ + { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ + { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ + { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ + { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ + { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ + { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ + { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ + { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ + { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ +}; + +static const struct alps_nibble_commands alps_v4_nibble_commands[] = { + { PSMOUSE_CMD_ENABLE, 0x00 }, /* 0 */ + { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ + { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ + { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ + { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ + { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ + { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ + { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ + { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ + { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ + { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ + { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ + { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ + { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ + { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ + { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ +}; + + #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ #define ALPS_PASS 0x04 /* device has a pass-through port */ @@ -34,30 +78,33 @@ 6-byte ALPS packet */ static const struct alps_model_info alps_model_data[] = { - { { 0x32, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ - { { 0x33, 0x02, 0x0a }, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ - { { 0x53, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, - { { 0x53, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, - { { 0x60, 0x03, 0xc8 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ - { { 0x63, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, - { { 0x63, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, - { { 0x63, 0x02, 0x28 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ - { { 0x63, 0x02, 0x3c }, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ - { { 0x63, 0x02, 0x50 }, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ - { { 0x63, 0x02, 0x64 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, - { { 0x63, 0x03, 0xc8 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ - { { 0x73, 0x00, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ - { { 0x73, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, - { { 0x73, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ - { { 0x20, 0x02, 0x0e }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ - { { 0x22, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, - { { 0x22, 0x02, 0x14 }, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ + { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ + { { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ + { { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ + { { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ + { { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ + { { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ + { { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, + { { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ + { { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ + { { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, + { { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ - { { 0x62, 0x02, 0x14 }, ALPS_PROTO_V2, 0xcf, 0xcf, + { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, - { { 0x73, 0x02, 0x50 }, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ - { { 0x52, 0x01, 0x14 }, ALPS_PROTO_V2, 0xff, 0xff, - ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ + { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ + { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ + { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, + { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, + { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, }; /* @@ -101,7 +148,7 @@ static void alps_report_buttons(struct psmouse *psmouse, input_sync(dev2); } -static void alps_process_packet(struct psmouse *psmouse) +static void alps_process_packet_v1_v2(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; const struct alps_model_info *model = priv->i; @@ -203,6 +250,224 @@ static void alps_process_packet(struct psmouse *psmouse) input_sync(dev); } +static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = priv->dev2; + int x, y, z, left, right, middle; + + /* Sanity check packet */ + if (!(packet[0] & 0x40)) { + psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); + return; + } + + /* + * There's a special packet that seems to indicate the end + * of a stream of trackstick data. Filter these out. + */ + if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) + return; + + x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); + y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); + z = (packet[4] & 0x7c) >> 2; + + /* + * The x and y values tend to be quite large, and when used + * alone the trackstick is difficult to use. Scale them down + * to compensate. + */ + x /= 8; + y /= 8; + + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, -y); + + /* + * Most ALPS models report the trackstick buttons in the touchpad + * packets, but a few report them here. No reliable way has been + * found to differentiate between the models upfront, so we enable + * the quirk in response to seeing a button press in the trackstick + * packet. + */ + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && + (left || right || middle)) + priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; + + if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_MIDDLE, middle); + } + + input_sync(dev); + return; +} + +static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + struct input_dev *dev2 = priv->dev2; + int x, y, z; + int left, right, middle; + + /* + * There's no single feature of touchpad position and bitmap + * packets that can be used to distinguish between them. We + * rely on the fact that a bitmap packet should always follow + * a position packet with bit 6 of packet[4] set. + */ + if (priv->multi_packet) { + priv->multi_packet = 0; + + /* + * Sometimes a position packet will indicate a multi-packet + * sequence, but then what follows is another position + * packet. Check for this, and when it happens process the + * position packet as usual. + */ + if (packet[0] & 0x40) { + /* + * Bitmap packets are not yet supported, so for now + * just ignore them. + */ + return; + } + } + + if (!priv->multi_packet && (packet[4] & 0x40)) + priv->multi_packet = 1; + else + priv->multi_packet = 0; + + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f); + z = packet[5] & 0x7f; + + /* + * Sometimes the hardware sends a single packet with z = 0 + * in the middle of a stream. Real releases generate packets + * with x, y, and z all zero, so these seem to be flukes. + * Ignore them. + */ + if (x && y && !z) + return; + + if (z >= 64) + input_report_key(dev, BTN_TOUCH, 1); + else + input_report_key(dev, BTN_TOUCH, 0); + + if (z > 0) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + } + input_report_abs(dev, ABS_PRESSURE, z); + + input_report_key(dev, BTN_TOOL_FINGER, z > 0); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_MIDDLE, middle); + + input_sync(dev); + + if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { + left = packet[3] & 0x10; + right = packet[3] & 0x20; + middle = packet[3] & 0x40; + + input_report_key(dev2, BTN_LEFT, left); + input_report_key(dev2, BTN_RIGHT, right); + input_report_key(dev2, BTN_MIDDLE, middle); + input_sync(dev2); + } +} + +static void alps_process_packet_v3(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + /* + * v3 protocol packets come in three types, two representing + * touchpad data and one representing trackstick data. + * Trackstick packets seem to be distinguished by always + * having 0x3f in the last byte. This value has never been + * observed in the last byte of either of the other types + * of packets. + */ + if (packet[5] == 0x3f) { + alps_process_trackstick_packet_v3(psmouse); + return; + } + + alps_process_touchpad_packet_v3(psmouse); +} + +static void alps_process_packet_v4(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + int x, y, z; + int left, right; + + left = packet[4] & 0x01; + right = packet[4] & 0x02; + + x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + z = packet[5] & 0x7f; + + if (z >= 64) + input_report_key(dev, BTN_TOUCH, 1); + else + input_report_key(dev, BTN_TOUCH, 0); + + if (z > 0) { + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + } + input_report_abs(dev, ABS_PRESSURE, z); + + input_report_key(dev, BTN_TOOL_FINGER, z > 0); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + + input_sync(dev); +} + +static void alps_process_packet(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + + switch (model->proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + alps_process_packet_v1_v2(psmouse); + break; + case ALPS_PROTO_V3: + alps_process_packet_v3(psmouse); + break; + case ALPS_PROTO_V4: + alps_process_packet_v4(psmouse); + break; + } +} + static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) @@ -376,11 +641,127 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) return PSMOUSE_GOOD_DATA; } +static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct alps_data *priv = psmouse->private; + int command; + unsigned char *param; + unsigned char dummy[4]; + + BUG_ON(nibble > 0xf); + + command = priv->nibble_commands[nibble].command; + param = (command & 0x0f00) ? + dummy : (unsigned char *)&priv->nibble_commands[nibble].data; + + if (ps2_command(ps2dev, param, command)) + return -1; + + return 0; +} + +static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct alps_data *priv = psmouse->private; + int i, nibble; + + if (ps2_command(ps2dev, NULL, priv->addr_command)) + return -1; + + for (i = 12; i >= 0; i -= 4) { + nibble = (addr >> i) & 0xf; + if (alps_command_mode_send_nibble(psmouse, nibble)) + return -1; + } + + return 0; +} + +static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + /* + * The address being read is returned in the first two bytes + * of the result. Check that this address matches the expected + * address. + */ + if (addr != ((param[0] << 8) | param[1])) + return -1; + + return param[2]; +} + +static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr) +{ + if (alps_command_mode_set_addr(psmouse, addr)) + return -1; + return __alps_command_mode_read_reg(psmouse, addr); +} + +static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value) +{ + if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) + return -1; + if (alps_command_mode_send_nibble(psmouse, value & 0xf)) + return -1; + return 0; +} + +static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, + u8 value) +{ + if (alps_command_mode_set_addr(psmouse, addr)) + return -1; + return __alps_command_mode_write_reg(psmouse, value); +} + +static int alps_enter_command_mode(struct psmouse *psmouse, + unsigned char *resp) +{ + unsigned char param[4]; + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_err(psmouse, "failed to enter command mode\n"); + return -1; + } + + if (param[0] != 0x88 && param[1] != 0x07) { + psmouse_dbg(psmouse, + "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", + param[0], param[1], param[2]); + return -1; + } + + if (resp) + *resp = param[2]; + return 0; +} + +static inline int alps_exit_command_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) + return -1; + return 0; +} + static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) { struct ps2dev *ps2dev = &psmouse->ps2dev; static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; unsigned char param[4]; + const struct alps_model_info *model = NULL; int i; /* @@ -428,12 +809,41 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version = (param[0] << 8) | (param[1] << 4) | i; } - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { if (!memcmp(param, alps_model_data[i].signature, - sizeof(alps_model_data[i].signature))) - return alps_model_data + i; + sizeof(alps_model_data[i].signature))) { + model = alps_model_data + i; + break; + } + } - return NULL; + if (model && model->proto_version > ALPS_PROTO_V2) { + /* + * Need to check command mode response to identify + * model + */ + model = NULL; + if (alps_enter_command_mode(psmouse, param)) { + psmouse_warn(psmouse, + "touchpad failed to enter command mode\n"); + } else { + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && + alps_model_data[i].command_mode_resp == param[0]) { + model = alps_model_data + i; + break; + } + } + alps_exit_command_mode(psmouse); + + if (!model) + psmouse_dbg(psmouse, + "Unknown command mode response %2.2x\n", + param[0]); + } + } + + return model; } /* @@ -441,7 +851,7 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int * subsequent commands. It looks like glidepad is behind stickpointer, * I'd thought it would be other way around... */ -static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) +static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable) { struct ps2dev *ps2dev = &psmouse->ps2dev; int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; @@ -458,7 +868,7 @@ static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) return 0; } -static int alps_absolute_mode(struct psmouse *psmouse) +static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; @@ -533,13 +943,13 @@ static int alps_poll(struct psmouse *psmouse) bool poll_failed; if (priv->i->flags & ALPS_PASS) - alps_passthrough_mode(psmouse, true); + alps_passthrough_mode_v2(psmouse, true); poll_failed = ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; if (priv->i->flags & ALPS_PASS) - alps_passthrough_mode(psmouse, false); + alps_passthrough_mode_v2(psmouse, false); if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) return -1; @@ -556,13 +966,13 @@ static int alps_poll(struct psmouse *psmouse) return 0; } -static int alps_hw_init(struct psmouse *psmouse) +static int alps_hw_init_v1_v2(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; const struct alps_model_info *model = priv->i; if ((model->flags & ALPS_PASS) && - alps_passthrough_mode(psmouse, true)) { + alps_passthrough_mode_v2(psmouse, true)) { return -1; } @@ -571,13 +981,13 @@ static int alps_hw_init(struct psmouse *psmouse) return -1; } - if (alps_absolute_mode(psmouse)) { + if (alps_absolute_mode_v1_v2(psmouse)) { psmouse_err(psmouse, "Failed to enable absolute mode\n"); return -1; } if ((model->flags & ALPS_PASS) && - alps_passthrough_mode(psmouse, false)) { + alps_passthrough_mode_v2(psmouse, false)) { return -1; } @@ -590,6 +1000,297 @@ static int alps_hw_init(struct psmouse *psmouse) return 0; } +/* + * Enable or disable passthrough mode to the trackstick. Must be in + * command mode when calling this function. + */ +static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) +{ + int reg_val; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0008); + if (reg_val == -1) + return -1; + + if (enable) + reg_val |= 0x01; + else + reg_val &= ~0x01; + + if (__alps_command_mode_write_reg(psmouse, reg_val)) + return -1; + + return 0; +} + +/* Must be in command mode when calling this function */ +static int alps_absolute_mode_v3(struct psmouse *psmouse) +{ + int reg_val; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0004); + if (reg_val == -1) + return -1; + + reg_val |= 0x06; + if (__alps_command_mode_write_reg(psmouse, reg_val)) + return -1; + + return 0; +} + +static int alps_hw_init_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val; + unsigned char param[4]; + + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_enter_command_mode(psmouse, NULL)) + goto error; + + /* Check for trackstick */ + reg_val = alps_command_mode_read_reg(psmouse, 0x0008); + if (reg_val == -1) + goto error; + if (reg_val & 0x80) { + if (alps_passthrough_mode_v3(psmouse, true)) + goto error; + if (alps_exit_command_mode(psmouse)) + goto error; + + /* + * E7 report for the trackstick + * + * There have been reports of failures to seem to trace back + * to the above trackstick check failing. When these occur + * this E7 report fails, so when that happens we continue + * with the assumption that there isn't a trackstick after + * all. + */ + param[0] = 0x64; + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_warn(psmouse, "trackstick E7 report failed\n"); + } else { + psmouse_dbg(psmouse, + "trackstick E7 report: %2.2x %2.2x %2.2x\n", + param[0], param[1], param[2]); + + /* + * Not sure what this does, but it is absolutely + * essential. Without it, the touchpad does not + * work at all and the trackstick just emits normal + * PS/2 packets. + */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + alps_command_mode_send_nibble(psmouse, 0x9) || + alps_command_mode_send_nibble(psmouse, 0x4)) { + psmouse_err(psmouse, + "Error sending magic E6 sequence\n"); + goto error_passthrough; + } + } + + if (alps_enter_command_mode(psmouse, NULL)) + goto error_passthrough; + if (alps_passthrough_mode_v3(psmouse, false)) + goto error; + } + + if (alps_absolute_mode_v3(psmouse)) { + psmouse_err(psmouse, "Failed to enter absolute mode\n"); + goto error; + } + + reg_val = alps_command_mode_read_reg(psmouse, 0x0006); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0007); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, 0x04)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, 0x03)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) + goto error; + if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) + goto error; + + if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) + goto error; + if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) + goto error; + + /* + * This ensures the trackstick packets are in the format + * supported by this driver. If bit 1 isn't set the packet + * format is different. + */ + if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) + goto error; + + alps_exit_command_mode(psmouse); + + /* Set rate and enable data reporting */ + param[0] = 0x64; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + psmouse_err(psmouse, "Failed to enable data reporting\n"); + return -1; + } + + return 0; + +error_passthrough: + /* Something failed while in passthrough mode, so try to get out */ + if (!alps_enter_command_mode(psmouse, NULL)) + alps_passthrough_mode_v3(psmouse, false); +error: + /* + * Leaving the touchpad in command mode will essentially render + * it unusable until the machine reboots, so exit it here just + * to be safe + */ + alps_exit_command_mode(psmouse); + return -1; +} + +/* Must be in command mode when calling this function */ +static int alps_absolute_mode_v4(struct psmouse *psmouse) +{ + int reg_val; + + reg_val = alps_command_mode_read_reg(psmouse, 0x0004); + if (reg_val == -1) + return -1; + + reg_val |= 0x02; + if (__alps_command_mode_write_reg(psmouse, reg_val)) + return -1; + + return 0; +} + +static int alps_hw_init_v4(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + priv->nibble_commands = alps_v4_nibble_commands; + priv->addr_command = PSMOUSE_CMD_DISABLE; + + if (alps_enter_command_mode(psmouse, NULL)) + goto error; + + if (alps_absolute_mode_v4(psmouse)) { + psmouse_err(psmouse, "Failed to enter absolute mode\n"); + goto error; + } + + if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03)) + goto error; + + alps_exit_command_mode(psmouse); + + /* + * This sequence changes the output from a 9-byte to an + * 8-byte format. All the same data seems to be present, + * just in a more compact format. + */ + param[0] = 0xc8; + param[1] = 0x64; + param[2] = 0x50; + if (ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, ¶m[2], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) + return -1; + + /* Set rate and enable data reporting */ + param[0] = 0x64; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + psmouse_err(psmouse, "Failed to enable data reporting\n"); + return -1; + } + + return 0; + +error: + /* + * Leaving the touchpad in command mode will essentially render + * it unusable until the machine reboots, so exit it here just + * to be safe + */ + alps_exit_command_mode(psmouse); + return -1; +} + +static int alps_hw_init(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; + int ret = -1; + + switch (model->proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + ret = alps_hw_init_v1_v2(psmouse); + break; + case ALPS_PROTO_V3: + ret = alps_hw_init_v3(psmouse); + break; + case ALPS_PROTO_V4: + ret = alps_hw_init_v4(psmouse); + break; + } + + return ret; +} + static int alps_reconnect(struct psmouse *psmouse) { const struct alps_model_info *model; @@ -630,6 +1331,8 @@ int alps_init(struct psmouse *psmouse) psmouse->private = priv; + psmouse_reset(psmouse); + model = alps_get_model(psmouse, &version); if (!model) goto init_fail; @@ -657,8 +1360,20 @@ int alps_init(struct psmouse *psmouse) BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); - input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); + + switch (model->proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); + break; + case ALPS_PROTO_V3: + case ALPS_PROTO_V4: + input_set_abs_params(dev1, ABS_X, 0, 2000, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, 1400, 0, 0); + break; + } + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); if (model->flags & ALPS_WHEEL) { @@ -701,7 +1416,7 @@ int alps_init(struct psmouse *psmouse) psmouse->poll = alps_poll; psmouse->disconnect = alps_disconnect; psmouse->reconnect = alps_reconnect; - psmouse->pktsize = 6; + psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; /* We are having trouble resyncing ALPS touchpads so disable it for now */ psmouse->resync_time = 0; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 4ce9bba6a3cd..62db7f489a59 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -14,22 +14,36 @@ #define ALPS_PROTO_V1 0 #define ALPS_PROTO_V2 1 +#define ALPS_PROTO_V3 2 +#define ALPS_PROTO_V4 3 struct alps_model_info { unsigned char signature[3]; + unsigned char command_mode_resp; /* v3/v4 only */ unsigned char proto_version; unsigned char byte0, mask0; unsigned char flags; }; +struct alps_nibble_commands { + int command; + unsigned char data; +}; + struct alps_data { struct input_dev *dev2; /* Relative device */ char phys[32]; /* Phys */ const struct alps_model_info *i;/* Info */ + const struct alps_nibble_commands *nibble_commands; + int addr_command; /* Command to set register address */ int prev_fin; /* Finger bit from previous packet */ + int multi_packet; /* Multi-packet data in progress */ + u8 quirks; struct timer_list timer; }; +#define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */ + #ifdef CONFIG_MOUSE_PS2_ALPS int alps_detect(struct psmouse *psmouse, bool set_properties); int alps_init(struct psmouse *psmouse); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 9b84b0c4e371..11a9c6c8bca4 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -8,6 +8,7 @@ #define PSMOUSE_CMD_SETSTREAM 0x00ea #define PSMOUSE_CMD_SETPOLL 0x00f0 #define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ +#define PSMOUSE_CMD_RESET_WRAP 0x00ec #define PSMOUSE_CMD_GETID 0x02f2 #define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_ENABLE 0x00f4 -- cgit v1.2.3 From 01ce661fc83005947dc958a5739c153843af8a73 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 7 Nov 2011 19:54:13 -0800 Subject: Input: ALPS - add semi-MT support for v3 protocol Signed-off-by: Seth Forshee Acked-by: Chase Douglas Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 233 +++++++++++++++++++++++++++++++++++++++++---- drivers/input/mouse/alps.h | 1 + 2 files changed, 215 insertions(+), 19 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index a0248fd62ef8..bd87380bd879 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -26,6 +27,12 @@ /* * Definitions for ALPS version 3 and 4 command mode protocol */ +#define ALPS_V3_X_MAX 2000 +#define ALPS_V3_Y_MAX 1400 + +#define ALPS_BITMAP_X_BITS 15 +#define ALPS_BITMAP_Y_BITS 11 + #define ALPS_CMD_NIBBLE_10 0x01f2 static const struct alps_nibble_commands alps_v3_nibble_commands[] = { @@ -250,6 +257,137 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) input_sync(dev); } +/* + * Process bitmap data from v3 and v4 protocols. Returns the number of + * fingers detected. A return value of 0 means at least one of the + * bitmaps was empty. + * + * The bitmaps don't have enough data to track fingers, so this function + * only generates points representing a bounding box of all contacts. + * These points are returned in x1, y1, x2, and y2 when the return value + * is greater than 0. + */ +static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, + int *x1, int *y1, int *x2, int *y2) +{ + struct alps_bitmap_point { + int start_bit; + int num_bits; + }; + + int fingers_x = 0, fingers_y = 0, fingers; + int i, bit, prev_bit; + struct alps_bitmap_point x_low = {0,}, x_high = {0,}; + struct alps_bitmap_point y_low = {0,}, y_high = {0,}; + struct alps_bitmap_point *point; + + if (!x_map || !y_map) + return 0; + + *x1 = *y1 = *x2 = *y2 = 0; + + prev_bit = 0; + point = &x_low; + for (i = 0; x_map != 0; i++, x_map >>= 1) { + bit = x_map & 1; + if (bit) { + if (!prev_bit) { + point->start_bit = i; + fingers_x++; + } + point->num_bits++; + } else { + if (prev_bit) + point = &x_high; + else + point->num_bits = 0; + } + prev_bit = bit; + } + + /* + * y bitmap is reversed for what we need (lower positions are in + * higher bits), so we process from the top end. + */ + y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS); + prev_bit = 0; + point = &y_low; + for (i = 0; y_map != 0; i++, y_map <<= 1) { + bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1)); + if (bit) { + if (!prev_bit) { + point->start_bit = i; + fingers_y++; + } + point->num_bits++; + } else { + if (prev_bit) + point = &y_high; + else + point->num_bits = 0; + } + prev_bit = bit; + } + + /* + * Fingers can overlap, so we use the maximum count of fingers + * on either axis as the finger count. + */ + fingers = max(fingers_x, fingers_y); + + /* + * If total fingers is > 1 but either axis reports only a single + * contact, we have overlapping or adjacent fingers. For the + * purposes of creating a bounding box, divide the single contact + * (roughly) equally between the two points. + */ + if (fingers > 1) { + if (fingers_x == 1) { + i = x_low.num_bits / 2; + x_low.num_bits = x_low.num_bits - i; + x_high.start_bit = x_low.start_bit + i; + x_high.num_bits = max(i, 1); + } else if (fingers_y == 1) { + i = y_low.num_bits / 2; + y_low.num_bits = y_low.num_bits - i; + y_high.start_bit = y_low.start_bit + i; + y_high.num_bits = max(i, 1); + } + } + + *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (ALPS_BITMAP_X_BITS - 1)); + *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (ALPS_BITMAP_Y_BITS - 1)); + + if (fingers > 1) { + *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (ALPS_BITMAP_X_BITS - 1)); + *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (ALPS_BITMAP_Y_BITS - 1)); + } + + return fingers; +} + +static void alps_set_slot(struct input_dev *dev, int slot, bool active, + int x, int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + } +} + +static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, + int x1, int y1, int x2, int y2) +{ + alps_set_slot(dev, 0, num_fingers != 0, x1, y1); + alps_set_slot(dev, 1, num_fingers == 2, x2, y2); +} + static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; @@ -318,16 +456,17 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) struct input_dev *dev2 = priv->dev2; int x, y, z; int left, right, middle; + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + int fingers = 0, bmap_fingers; + unsigned int x_bitmap, y_bitmap; /* - * There's no single feature of touchpad position and bitmap - * packets that can be used to distinguish between them. We - * rely on the fact that a bitmap packet should always follow - * a position packet with bit 6 of packet[4] set. + * There's no single feature of touchpad position and bitmap packets + * that can be used to distinguish between them. We rely on the fact + * that a bitmap packet should always follow a position packet with + * bit 6 of packet[4] set. */ if (priv->multi_packet) { - priv->multi_packet = 0; - /* * Sometimes a position packet will indicate a multi-packet * sequence, but then what follows is another position @@ -335,18 +474,49 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * position packet as usual. */ if (packet[0] & 0x40) { + fingers = (packet[5] & 0x3) + 1; + x_bitmap = ((packet[4] & 0x7e) << 8) | + ((packet[1] & 0x7f) << 2) | + ((packet[0] & 0x30) >> 4); + y_bitmap = ((packet[3] & 0x70) << 4) | + ((packet[2] & 0x7f) << 1) | + (packet[4] & 0x01); + + bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap, + &x1, &y1, &x2, &y2); + /* - * Bitmap packets are not yet supported, so for now - * just ignore them. + * We shouldn't report more than one finger if + * we don't have two coordinates. */ - return; + if (fingers > 1 && bmap_fingers < 2) + fingers = bmap_fingers; + + /* Now process position packet */ + packet = priv->multi_data; + } else { + priv->multi_packet = 0; } } - if (!priv->multi_packet && (packet[4] & 0x40)) + /* + * Bit 6 of byte 0 is not usually set in position packets. The only + * times it seems to be set is in situations where the data is + * suspect anyway, e.g. a palm resting flat on the touchpad. Given + * this combined with the fact that this bit is useful for filtering + * out misidentified bitmap packets, we reject anything with this + * bit set. + */ + if (packet[0] & 0x40) + return; + + if (!priv->multi_packet && (packet[4] & 0x40)) { priv->multi_packet = 1; - else - priv->multi_packet = 0; + memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); + return; + } + + priv->multi_packet = 0; left = packet[3] & 0x01; right = packet[3] & 0x02; @@ -366,22 +536,38 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) if (x && y && !z) return; + /* + * If we don't have MT data or the bitmaps were empty, we have + * to rely on ST data. + */ + if (!fingers) { + x1 = x; + y1 = y; + fingers = z > 0 ? 1 : 0; + } + if (z >= 64) input_report_key(dev, BTN_TOUCH, 1); else input_report_key(dev, BTN_TOUCH, 0); + alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); + input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_MIDDLE, middle); + if (z > 0) { input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); } input_report_abs(dev, ABS_PRESSURE, z); - input_report_key(dev, BTN_TOOL_FINGER, z > 0); - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - input_report_key(dev, BTN_MIDDLE, middle); - input_sync(dev); if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { @@ -1368,9 +1554,18 @@ int alps_init(struct psmouse *psmouse) input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); break; case ALPS_PROTO_V3: + set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); + input_mt_init_slots(dev1, 2); + input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); + + set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); + set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); + set_bit(BTN_TOOL_QUADTAP, dev1->keybit); + /* fall through */ case ALPS_PROTO_V4: - input_set_abs_params(dev1, ABS_X, 0, 2000, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, 1400, 0, 0); + input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); break; } diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 62db7f489a59..a00a4ab92a0f 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -38,6 +38,7 @@ struct alps_data { int addr_command; /* Command to set register address */ int prev_fin; /* Finger bit from previous packet */ int multi_packet; /* Multi-packet data in progress */ + unsigned char multi_data[6]; /* Saved multi-packet data */ u8 quirks; struct timer_list timer; }; -- cgit v1.2.3 From 76496e7a02e99d42844f4fffa145b81e513e7acd Mon Sep 17 00:00:00 2001 From: JJ Ding Date: Wed, 9 Nov 2011 10:20:14 -0800 Subject: Input: convert obsolete strict_strtox to kstrtox With commit 67d0a0754455f89ef3946946159d8ec9e45ce33a we mark strict_strtox as obsolete. Convert all remaining such uses in drivers/input/. Also change long to appropriate types, and return error conditions from kstrtox separately, as Dmitry sugguests. Signed-off-by: JJ Ding Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 7 ++----- drivers/input/mouse/hgpk.c | 18 ++++++++++------ drivers/input/mouse/logips2pp.c | 9 ++++++-- drivers/input/mouse/psmouse-base.c | 27 +++++++++++++----------- drivers/input/mouse/sentelic.c | 43 ++++++++++++++++++++++++++++---------- drivers/input/mouse/trackpoint.c | 17 ++++++++++----- 6 files changed, 80 insertions(+), 41 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 09b93b11a274..b562392d0cd8 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1031,16 +1031,13 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse, struct elantech_data *etd = psmouse->private; struct elantech_attr_data *attr = data; unsigned char *reg = (unsigned char *) etd + attr->field_offset; - unsigned long value; + unsigned char value; int err; - err = strict_strtoul(buf, 16, &value); + err = kstrtou8(buf, 16, &value); if (err) return err; - if (value > 0xff) - return -EINVAL; - /* Do we need to preserve some bits for version 2 hardware too? */ if (etd->hw_version == 1) { if (attr->reg == 0x10) diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 0470dd46b566..1c5d521de600 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -789,11 +789,14 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, const char *buf, size_t count) { struct hgpk_data *priv = psmouse->private; - unsigned long value; + unsigned int value; int err; - err = strict_strtoul(buf, 10, &value); - if (err || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (value != priv->powered) { @@ -881,11 +884,14 @@ static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, const char *buf, size_t count) { struct hgpk_data *priv = psmouse->private; - unsigned long value; + unsigned int value; int err; - err = strict_strtoul(buf, 10, &value); - if (err || value != 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value != 1) return -EINVAL; /* diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index faac2c3bef74..84de2fc6acc1 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -155,9 +155,14 @@ static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long value; + unsigned int value; + int err; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; ps2pp_set_smartscroll(psmouse, value); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 9f352fbd7b4f..a6973e575aa9 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1558,13 +1558,12 @@ static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count) { unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); - unsigned long value; - - if (strict_strtoul(buf, 10, &value)) - return -EINVAL; + unsigned int value; + int err; - if ((unsigned int)value != value) - return -EINVAL; + err = kstrtouint(buf, 10, &value); + if (err) + return err; *field = value; @@ -1671,10 +1670,12 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long value; + unsigned int value; + int err; - if (strict_strtoul(buf, 10, &value)) - return -EINVAL; + err = kstrtouint(buf, 10, &value); + if (err) + return err; psmouse->set_rate(psmouse, value); return count; @@ -1682,10 +1683,12 @@ static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long value; + unsigned int value; + int err; - if (strict_strtoul(buf, 10, &value)) - return -EINVAL; + err = kstrtouint(buf, 10, &value); + if (err) + return err; psmouse->set_resolution(psmouse, value); return count; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index c5b12d2e955a..5babc47b39aa 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -408,7 +408,7 @@ static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long reg, val; + int reg, val; char *rest; ssize_t retval; @@ -416,7 +416,11 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, if (rest == buf || *rest != ' ' || reg > 0xff) return -EINVAL; - if (strict_strtoul(rest + 1, 16, &val) || val > 0xff) + retval = kstrtoint(rest + 1, 16, &val); + if (retval) + return retval; + + if (val > 0xff) return -EINVAL; if (fsp_reg_write_enable(psmouse, true)) @@ -448,10 +452,13 @@ static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, const char *buf, size_t count) { struct fsp_data *pad = psmouse->private; - unsigned long reg; - int val; + int reg, val, err; + + err = kstrtoint(buf, 16, ®); + if (err) + return err; - if (strict_strtoul(buf, 16, ®) || reg > 0xff) + if (reg > 0xff) return -EINVAL; if (fsp_reg_read(psmouse, reg, &val)) @@ -480,9 +487,13 @@ static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse, static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long val; + int val, err; - if (strict_strtoul(buf, 16, &val) || val > 0xff) + err = kstrtoint(buf, 16, &val); + if (err) + return err; + + if (val > 0xff) return -EINVAL; if (fsp_page_reg_write(psmouse, val)) @@ -505,9 +516,14 @@ static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse, static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long val; + unsigned int val; + int err; + + err = kstrtouint(buf, 10, &val); + if (err) + return err; - if (strict_strtoul(buf, 10, &val) || val > 1) + if (val > 1) return -EINVAL; fsp_onpad_vscr(psmouse, val); @@ -529,9 +545,14 @@ static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse, static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count) { - unsigned long val; + unsigned int val; + int err; + + err = kstrtouint(buf, 10, &val); + if (err) + return err; - if (strict_strtoul(buf, 10, &val) || val > 1) + if (val > 1) return -EINVAL; fsp_onpad_hscr(psmouse, val); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 54b2fa892e19..22b218018137 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -89,10 +89,12 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, struct trackpoint_data *tp = psmouse->private; struct trackpoint_attr_data *attr = data; unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); - unsigned long value; + unsigned char value; + int err; - if (strict_strtoul(buf, 10, &value) || value > 255) - return -EINVAL; + err = kstrtou8(buf, 10, &value); + if (err) + return err; *field = value; trackpoint_write(&psmouse->ps2dev, attr->command, value); @@ -115,9 +117,14 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, struct trackpoint_data *tp = psmouse->private; struct trackpoint_attr_data *attr = data; unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); - unsigned long value; + unsigned int value; + int err; + + err = kstrtouint(buf, 10, &value); + if (err) + return err; - if (strict_strtoul(buf, 10, &value) || value > 1) + if (value > 1) return -EINVAL; if (attr->inverted) -- cgit v1.2.3 From 7968a5dd492ccc38345013e534ad4c8d6eb60ed1 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 8 Nov 2011 00:00:35 -0800 Subject: Input: synaptics - add support for Relative mode Currently, the synaptics driver puts the device into Absolute mode. As explained in the synaptics documentation section 3.2, in this mode, the device sends a continuous stream of packets at the maximum rate to the host when the user's fingers are near or on the pad or pressing buttons, and continues streaming for 1 second afterwards. These packets are even sent when there is no new information to report, even when they are duplicates of the previous packet. For embedded systems this is a bit much - it results in a huge and uninterrupted stream of interrupts at high rate. This patch adds support for Relative mode, which can be selected as a new psmouse protocol. In this mode, the device does not send duplicate packets and acts like a standard PS/2 mouse. However, synaptics-specific functionality is still available, such as the ability to set the packet rate, and rather than disabling gestures and taps at the hardware level unconditionally, a 'synaptics_disable_gesture' sysfs attribute has been added to allow control of this functionality. This solves a long standing OLPC issue: synaptics hardware enables tap to click by default (even in the default relative mode), but we have found this to be inappropriate for young children and first time computer users. Enabling the synaptics driver disables tap-to-click, but we have previously been unable to use this because it also enables Absolute mode, which is too "spammy" for our desires and actually overloads our EC with its continuous stream of packets. Now we can enable the synaptics driver, disabling tap to click while retaining the less noisy Relative mode. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 9 +- drivers/input/mouse/psmouse.h | 2 + drivers/input/mouse/synaptics.c | 184 +++++++++++++++++++++++++++---------- drivers/input/mouse/synaptics.h | 5 + 4 files changed, 152 insertions(+), 48 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index a6973e575aa9..200be9c9dbc7 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -127,7 +127,7 @@ struct psmouse_protocol { * relevant events to the input module once full packet has arrived. */ -static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) +psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; @@ -819,6 +819,13 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = synaptics_detect, .init = synaptics_init, }, + { + .type = PSMOUSE_SYNAPTICS_RELATIVE, + .name = "SynRelPS/2", + .alias = "synaptics-relative", + .detect = synaptics_detect, + .init = synaptics_init_relative, + }, #endif #ifdef CONFIG_MOUSE_PS2_ALPS { diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 11a9c6c8bca4..6a417092d010 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -94,6 +94,7 @@ enum psmouse_type { PSMOUSE_HGPK, PSMOUSE_ELANTECH, PSMOUSE_FSP, + PSMOUSE_SYNAPTICS_RELATIVE, PSMOUSE_AUTO /* This one should always be last */ }; @@ -103,6 +104,7 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); int psmouse_reset(struct psmouse *psmouse); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); +psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); struct psmouse_attribute { struct device_attribute dattr; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index c080b828e5dc..a3bb49cb691c 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -268,19 +268,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, ¶m, 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; } @@ -299,26 +329,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, ¶m, 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 ****************************************************************************/ @@ -1142,8 +1152,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); @@ -1169,20 +1195,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); @@ -1204,10 +1224,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; } @@ -1234,17 +1302,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 || @@ -1321,9 +1383,10 @@ 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 @@ -1351,13 +1414,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; } @@ -1382,12 +1444,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; @@ -1406,11 +1475,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) diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 622aea8dd7e0..fd26ccca13d7 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -100,6 +100,7 @@ #define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) #define SYN_ID_FULL(i) ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i)) #define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) +#define SYN_ID_DISGEST_SUPPORTED(i) (SYN_ID_MAJOR(i) >= 4) /* synaptics special commands */ #define SYN_PS_SET_MODE2 0x14 @@ -159,6 +160,9 @@ struct synaptics_data { unsigned char mode; /* current mode byte */ int scroll; + bool absolute_mode; /* run in Absolute mode */ + bool disable_gesture; /* disable gestures */ + struct serio *pt_port; /* Pass-through serio port */ struct synaptics_mt_state mt_state; /* Current mt finger state */ @@ -175,6 +179,7 @@ struct synaptics_data { void synaptics_module_init(void); int synaptics_detect(struct psmouse *psmouse, bool set_properties); int synaptics_init(struct psmouse *psmouse); +int synaptics_init_relative(struct psmouse *psmouse); void synaptics_reset(struct psmouse *psmouse); bool synaptics_supported(void); -- cgit v1.2.3 From 83551c0159e9101b39b2d727ca1be0fd76daaf73 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Fri, 11 Nov 2011 16:05:04 -0800 Subject: Input: synaptics - update OLPC XO exclusion We have determined that the jumpiness previously seen when using the synaptics kernel mouse driver on OLPC XO was due to not using the synaptics X11 userspace driver - the xf86-input-evdev driver was interpreting 'finger near pad' signals as movements. Newer versions of xf86-input-evdev fix this issue. Additionally, the synaptics kernel driver is now usable on this platform, but only when run in relative mode. Update the comment and refine the check to allow the synaptics driver to run on OLPC XO in relative mode. We will continue investigating the EC issue as time becomes available. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index a3bb49cb691c..06c9ee57951e 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1389,15 +1389,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) 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; -- cgit v1.2.3 From b56b92a9a175faad4c182309a63f221219de9191 Mon Sep 17 00:00:00 2001 From: JJ Ding Date: Sun, 20 Nov 2011 22:21:45 -0800 Subject: Input: elantech - add support for elantech fast command Starting with v3 hardware, the firmware supports this shorter elantech_send_cmd. Teach the driver to use it. Signed-off-by: JJ Ding Reviewed-by: Daniel Kurtz Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 36 ++++++++++++++++++++++++++++-------- drivers/input/mouse/elantech.h | 1 + 2 files changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index b562392d0cd8..24bb2b5f5667 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -42,6 +42,24 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, return 0; } +/* + * V3 and later support this fast command + */ +static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c, + unsigned char *param) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || + ps2_command(ps2dev, NULL, c) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); + return -1; + } + + return 0; +} + /* * A retrying version of ps2_command */ @@ -863,13 +881,13 @@ static int elantech_set_range(struct psmouse *psmouse, i = (etd->fw_version > 0x020800 && etd->fw_version < 0x020900) ? 1 : 2; - if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) return -1; fixed_dpi = param[1] & 0x10; if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) { - if (synaptics_send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) + if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) return -1; *x_max = (etd->capabilities[1] - i) * param[1] / 2; @@ -888,7 +906,7 @@ static int elantech_set_range(struct psmouse *psmouse, break; case 3: - if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) return -1; *x_max = (0x0f & param[0]) << 8 | param[1]; @@ -896,7 +914,7 @@ static int elantech_set_range(struct psmouse *psmouse, break; case 4: - if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) + if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) return -1; *x_max = (0x0f & param[0]) << 8 | param[1]; @@ -1220,9 +1238,11 @@ static int elantech_set_properties(struct elantech_data *etd) else return -1; - /* - * Turn on packet checking by default. - */ + /* decide which send_cmd we're gonna use early */ + etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd : + synaptics_send_cmd; + + /* Turn on packet checking by default */ etd->paritycheck = 1; /* @@ -1278,7 +1298,7 @@ int elantech_init(struct psmouse *psmouse) "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n", etd->hw_version, param[0], param[1], param[2]); - if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, + if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY, etd->capabilities)) { psmouse_err(psmouse, "failed to query capabilities.\n"); goto init_fail; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 9e5f1aabea7e..08d00bd9d1f8 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -135,6 +135,7 @@ struct elantech_data { 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); }; #ifdef CONFIG_MOUSE_PS2_ELANTECH -- cgit v1.2.3 From 3d95fd6ad8d3cf582a70ed65660017114b6e4065 Mon Sep 17 00:00:00 2001 From: JJ Ding Date: Sun, 20 Nov 2011 22:26:56 -0800 Subject: Input: elantech - add resolution query support for v4 hardware It turns out that v4's firmware provides a command so we can query the resolution. Let's use it. Signed-off-by: JJ Ding Reviewed-by: Daniel Kurtz Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/input/mouse/elantech.h | 1 + 2 files changed, 38 insertions(+) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 24bb2b5f5667..59bfb70d330a 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -930,6 +930,30 @@ static int elantech_set_range(struct psmouse *psmouse, return 0; } +/* + * (value from firmware) * 10 + 790 = dpi + * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) + */ +static unsigned int elantech_convert_res(unsigned int val) +{ + return (val * 10 + 790) * 10 / 254; +} + +static int elantech_get_resolution_v4(struct psmouse *psmouse, + unsigned int *x_res, + unsigned int *y_res) +{ + unsigned char param[3]; + + if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param)) + return -1; + + *x_res = elantech_convert_res(param[1] & 0x0f); + *y_res = elantech_convert_res((param[1] & 0xf0) >> 4); + + return 0; +} + /* * Set the appropriate event bits for the input subsystem */ @@ -938,6 +962,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; + unsigned int x_res = 0, y_res = 0; if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) return -1; @@ -985,10 +1010,20 @@ 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"); + } + __set_bit(BTN_TOOL_QUADTAP, dev->keybit); /* For X to recognize me as touchpad. */ 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); + input_abs_set_res(dev, ABS_X, x_res); + input_abs_set_res(dev, ABS_Y, y_res); /* * range of pressure and width is the same as v2, * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility. @@ -1001,6 +1036,8 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_mt_init_slots(dev, ETP_MAX_FINGERS); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); + input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res); input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2, ETP_PMAX_V2, 0, 0); /* diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 08d00bd9d1f8..46db3be45ac9 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -20,6 +20,7 @@ #define ETP_FW_VERSION_QUERY 0x01 #define ETP_CAPABILITIES_QUERY 0x02 #define ETP_SAMPLE_QUERY 0x03 +#define ETP_RESOLUTION_QUERY 0x04 /* * Command values for register reading or writing -- cgit v1.2.3 From 4fcdeac5acf4a7a81efc409c02a79a76fa339c27 Mon Sep 17 00:00:00 2001 From: JJ Ding Date: Tue, 29 Nov 2011 11:08:41 -0800 Subject: Input: mouse - use macro module_platform_driver() Commit 940ab88962bc1aff3273a8356d64577a6e386736 introduced a new macro to save some platform_driver boilerplate code. Use it. Signed-off-by: JJ Ding Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/amimouse.c | 16 ++-------------- drivers/input/mouse/gpio_mouse.c | 13 +------------ drivers/input/mouse/pxa930_trkball.c | 14 +------------- 3 files changed, 4 insertions(+), 39 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index ff5f61a0fd3a..39be7b82c046 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -140,25 +140,13 @@ static int __exit amimouse_remove(struct platform_device *pdev) } static struct platform_driver amimouse_driver = { + .probe = amimouse_probe, .remove = __exit_p(amimouse_remove), .driver = { .name = "amiga-mouse", .owner = THIS_MODULE, }, }; - -static int __init amimouse_init(void) -{ - return platform_driver_probe(&amimouse_driver, amimouse_probe); -} - -module_init(amimouse_init); - -static void __exit amimouse_exit(void) -{ - platform_driver_unregister(&amimouse_driver); -} - -module_exit(amimouse_exit); +module_platform_driver(amimouse_driver); MODULE_ALIAS("platform:amiga-mouse"); diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 58902fbb9896..a9ad8e1402be 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -178,18 +178,7 @@ static struct platform_driver gpio_mouse_device_driver = { .owner = THIS_MODULE, } }; - -static int __init gpio_mouse_init(void) -{ - return platform_driver_register(&gpio_mouse_device_driver); -} -module_init(gpio_mouse_init); - -static void __exit gpio_mouse_exit(void) -{ - platform_driver_unregister(&gpio_mouse_device_driver); -} -module_exit(gpio_mouse_exit); +module_platform_driver(gpio_mouse_device_driver); MODULE_AUTHOR("Hans-Christian Egtvedt "); MODULE_DESCRIPTION("GPIO mouse driver"); diff --git a/drivers/input/mouse/pxa930_trkball.c b/drivers/input/mouse/pxa930_trkball.c index ee3b0ca9d592..a9e4bfdf31f4 100644 --- a/drivers/input/mouse/pxa930_trkball.c +++ b/drivers/input/mouse/pxa930_trkball.c @@ -250,19 +250,7 @@ static struct platform_driver pxa930_trkball_driver = { .probe = pxa930_trkball_probe, .remove = __devexit_p(pxa930_trkball_remove), }; - -static int __init pxa930_trkball_init(void) -{ - return platform_driver_register(&pxa930_trkball_driver); -} - -static void __exit pxa930_trkball_exit(void) -{ - platform_driver_unregister(&pxa930_trkball_driver); -} - -module_init(pxa930_trkball_init); -module_exit(pxa930_trkball_exit); +module_platform_driver(pxa930_trkball_driver); MODULE_AUTHOR("Yong Yao "); MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver"); -- cgit v1.2.3 From ee9dfd7a1d1256b0f51a0bf54bed0a8927c8e2ea Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 30 Dec 2011 15:16:45 -0800 Subject: Input: psmouse - make sure we do not use stale methods Several protocol initialization routines can fail after they set up psmouse methods, such as reconnect and disconnect. This may lead to these stale methods used with different protocol that they were intended to be used for and may cause unpredictavle behavior and/or crashes. Make sure we start with a clean slate before executing each and every protocol detection and/or initialization routine. Reported-by: Paul Fox Acked-by: Tai-hwa Liang Acked-by: Paul Fox Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 193 ++++++++++++++++++++++++------------- 1 file changed, 124 insertions(+), 69 deletions(-) (limited to 'drivers/input/mouse') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 200be9c9dbc7..de7e8bc17b1f 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -418,6 +418,49 @@ int psmouse_reset(struct psmouse *psmouse) return 0; } +/* + * Here we set the mouse resolution. + */ + +void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + static const unsigned char params[] = { 0, 1, 2, 2, 3 }; + unsigned char p; + + if (resolution == 0 || resolution > 200) + resolution = 200; + + p = params[resolution / 50]; + ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); + psmouse->resolution = 25 << p; +} + +/* + * Here we set the mouse report rate. + */ + +static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; + unsigned char r; + int i = 0; + + while (rates[i] > rate) i++; + r = rates[i]; + ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); + psmouse->rate = r; +} + +/* + * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. + */ + +static int psmouse_poll(struct psmouse *psmouse) +{ + return ps2_command(&psmouse->ps2dev, psmouse->packet, + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); +} + /* * Genius NetMouse magic init. @@ -602,6 +645,56 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) return 0; } +/* + * Apply default settings to the psmouse structure. Most of them will + * be overridden by individual protocol initialization routines. + */ + +static void psmouse_apply_defaults(struct psmouse *psmouse) +{ + struct input_dev *input_dev = psmouse->dev; + + memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); + memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); + memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); + memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); + memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_REL, input_dev->evbit); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + + psmouse->set_rate = psmouse_set_rate; + psmouse->set_resolution = psmouse_set_resolution; + psmouse->poll = psmouse_poll; + psmouse->protocol_handler = psmouse_process_byte; + psmouse->pktsize = 3; + psmouse->reconnect = NULL; + psmouse->disconnect = NULL; + psmouse->cleanup = NULL; + psmouse->pt_activate = NULL; + psmouse->pt_deactivate = NULL; +} + +/* + * Apply default settings to the psmouse structure and call specified + * protocol detection or initialization routine. + */ +static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse, + bool set_properties), + struct psmouse *psmouse, bool set_properties) +{ + if (set_properties) + psmouse_apply_defaults(psmouse); + + return detect(psmouse, set_properties); +} + /* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have. @@ -616,7 +709,7 @@ static int psmouse_extensions(struct psmouse *psmouse, * We always check for lifebook because it does not disturb mouse * (it only checks DMI information). */ - if (lifebook_detect(psmouse, set_properties) == 0) { + if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) { if (max_proto > PSMOUSE_IMEX) { if (!set_properties || lifebook_init(psmouse) == 0) return PSMOUSE_LIFEBOOK; @@ -628,15 +721,18 @@ static int psmouse_extensions(struct psmouse *psmouse, * upsets the thinkingmouse). */ - if (max_proto > PSMOUSE_IMEX && thinking_detect(psmouse, set_properties) == 0) + if (max_proto > PSMOUSE_IMEX && + psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) { return PSMOUSE_THINKPS; + } /* * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol * support is disabled in config - we need to know if it is synaptics so we * can reset it properly after probing for intellimouse. */ - if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) { + if (max_proto > PSMOUSE_PS2 && + psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) { synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { @@ -667,7 +763,8 @@ static int psmouse_extensions(struct psmouse *psmouse, */ if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); - if (alps_detect(psmouse, set_properties) == 0) { + if (psmouse_do_detect(alps_detect, + psmouse, set_properties) == 0) { if (!set_properties || alps_init(psmouse) == 0) return PSMOUSE_ALPS; /* @@ -681,7 +778,7 @@ static int psmouse_extensions(struct psmouse *psmouse, * Try OLPC HGPK touchpad. */ if (max_proto > PSMOUSE_IMEX && - hgpk_detect(psmouse, set_properties) == 0) { + psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) { if (!set_properties || hgpk_init(psmouse) == 0) return PSMOUSE_HGPK; /* @@ -694,7 +791,7 @@ static int psmouse_extensions(struct psmouse *psmouse, * Try Elantech touchpad. */ if (max_proto > PSMOUSE_IMEX && - elantech_detect(psmouse, set_properties) == 0) { + psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) { if (!set_properties || elantech_init(psmouse) == 0) return PSMOUSE_ELANTECH; /* @@ -703,18 +800,21 @@ static int psmouse_extensions(struct psmouse *psmouse, max_proto = PSMOUSE_IMEX; } - if (max_proto > PSMOUSE_IMEX) { - if (genius_detect(psmouse, set_properties) == 0) + if (psmouse_do_detect(genius_detect, + psmouse, set_properties) == 0) return PSMOUSE_GENPS; - if (ps2pp_init(psmouse, set_properties) == 0) + if (psmouse_do_detect(ps2pp_init, + psmouse, set_properties) == 0) return PSMOUSE_PS2PP; - if (trackpoint_detect(psmouse, set_properties) == 0) + if (psmouse_do_detect(trackpoint_detect, + psmouse, set_properties) == 0) return PSMOUSE_TRACKPOINT; - if (touchkit_ps2_detect(psmouse, set_properties) == 0) + if (psmouse_do_detect(touchkit_ps2_detect, + psmouse, set_properties) == 0) return PSMOUSE_TOUCHKIT_PS2; } @@ -723,7 +823,8 @@ static int psmouse_extensions(struct psmouse *psmouse, * Trackpoint devices (causing TP_READ_ID command to time out). */ if (max_proto > PSMOUSE_IMEX) { - if (fsp_detect(psmouse, set_properties) == 0) { + if (psmouse_do_detect(fsp_detect, + psmouse, set_properties) == 0) { if (!set_properties || fsp_init(psmouse) == 0) return PSMOUSE_FSP; /* @@ -741,17 +842,23 @@ static int psmouse_extensions(struct psmouse *psmouse, ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); psmouse_reset(psmouse); - if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0) + if (max_proto >= PSMOUSE_IMEX && + psmouse_do_detect(im_explorer_detect, + psmouse, set_properties) == 0) { return PSMOUSE_IMEX; + } - if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0) + if (max_proto >= PSMOUSE_IMPS && + psmouse_do_detect(intellimouse_detect, + psmouse, set_properties) == 0) { return PSMOUSE_IMPS; + } /* * Okay, all failed, we have a standard mouse here. The number of the buttons * is still a question, though. We assume 3. */ - ps2bare_detect(psmouse, set_properties); + psmouse_do_detect(ps2bare_detect, psmouse, set_properties); if (synaptics_hardware) { /* @@ -964,39 +1071,6 @@ static int psmouse_probe(struct psmouse *psmouse) return 0; } -/* - * Here we set the mouse resolution. - */ - -void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) -{ - static const unsigned char params[] = { 0, 1, 2, 2, 3 }; - unsigned char p; - - if (resolution == 0 || resolution > 200) - resolution = 200; - - p = params[resolution / 50]; - ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); - psmouse->resolution = 25 << p; -} - -/* - * Here we set the mouse report rate. - */ - -static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) -{ - static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; - unsigned char r; - int i = 0; - - while (rates[i] > rate) i++; - r = rates[i]; - ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); - psmouse->rate = r; -} - /* * psmouse_initialize() initializes the mouse to a sane state. */ @@ -1042,16 +1116,6 @@ static void psmouse_deactivate(struct psmouse *psmouse) psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); } -/* - * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. - */ - -static int psmouse_poll(struct psmouse *psmouse) -{ - return ps2_command(&psmouse->ps2dev, psmouse->packet, - PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); -} - /* * psmouse_resync() attempts to re-validate current protocol. @@ -1252,18 +1316,9 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, input_dev->dev.parent = &psmouse->ps2dev.serio->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - input_dev->keybit[BIT_WORD(BTN_MOUSE)] = - BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); - input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - - psmouse->set_rate = psmouse_set_rate; - psmouse->set_resolution = psmouse_set_resolution; - psmouse->poll = psmouse_poll; - psmouse->protocol_handler = psmouse_process_byte; - psmouse->pktsize = 3; - if (proto && (proto->detect || proto->init)) { + psmouse_apply_defaults(psmouse); + if (proto->detect && proto->detect(psmouse, true) < 0) return -1; -- cgit v1.2.3