aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMathias Gottschlag <mgottschlag@gmail.com>2014-12-29 12:26:35 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2014-12-29 12:51:37 -0500
commit05be1d079ec0b3691783e4384b1ada82149ff7d2 (patch)
treebd305e4dba7394c5eba6992f0f03e02b4812a438 /drivers
parent5fafed3e5612e9f308d20dc94adf5fc3d4a1a2a8 (diff)
Input: psmouse - support for the FocalTech PS/2 protocol extensions
Most of the protocol for these touchpads has been reverse engineered. This commit adds a basic multitouch-capable driver. A lot of the protocol is still unknown. Especially, we don't know how to identify the device yet apart from the PNP ID. The previous workaround for these devices has been left in place in case the driver is not compiled into the kernel or in case some other device with the same PNP ID is not recognized by the driver yet still has the same problems with the device probing code. Signed-off-by: Mathias Gottschlag <mgottschlag@gmail.com> Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/mouse/Kconfig10
-rw-r--r--drivers/input/mouse/focaltech.c408
-rw-r--r--drivers/input/mouse/focaltech.h2
-rw-r--r--drivers/input/mouse/psmouse-base.c32
-rw-r--r--drivers/input/mouse/psmouse.h1
5 files changed, 436 insertions, 17 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index d8b46b0f2dbe..2541bfa1181a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
146 146
147 If unsure, say N. 147 If unsure, say N.
148 148
149config MOUSE_PS2_FOCALTECH
150 bool "FocalTech PS/2 mouse protocol extension" if EXPERT
151 default y
152 depends on MOUSE_PS2
153 help
154 Say Y here if you have a FocalTech PS/2 TouchPad connected to
155 your system.
156
157 If unsure, say Y.
158
149config MOUSE_SERIAL 159config MOUSE_SERIAL
150 tristate "Serial mouse" 160 tristate "Serial mouse"
151 select SERIO 161 select SERIO
diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c
index f4d657ee1cc0..fca38ba63bbe 100644
--- a/drivers/input/mouse/focaltech.c
+++ b/drivers/input/mouse/focaltech.c
@@ -2,6 +2,7 @@
2 * Focaltech TouchPad PS/2 mouse driver 2 * Focaltech TouchPad PS/2 mouse driver
3 * 3 *
4 * Copyright (c) 2014 Red Hat Inc. 4 * Copyright (c) 2014 Red Hat Inc.
5 * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
@@ -13,15 +14,14 @@
13 * Hans de Goede <hdegoede@redhat.com> 14 * Hans de Goede <hdegoede@redhat.com>
14 */ 15 */
15 16
16/*
17 * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
18 * detection only, to avoid further detection attempts confusing the touchpad
19 * this way it at least works in PS/2 mouse compatibility mode.
20 */
21 17
22#include <linux/device.h> 18#include <linux/device.h>
23#include <linux/libps2.h> 19#include <linux/libps2.h>
20#include <linux/input/mt.h>
21#include <linux/serio.h>
22#include <linux/slab.h>
24#include "psmouse.h" 23#include "psmouse.h"
24#include "focaltech.h"
25 25
26static const char * const focaltech_pnp_ids[] = { 26static const char * const focaltech_pnp_ids[] = {
27 "FLT0101", 27 "FLT0101",
@@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = {
30 NULL 30 NULL
31}; 31};
32 32
33/*
34 * Even if the kernel is built without support for Focaltech PS/2 touchpads (or
35 * when the real driver fails to recognize the device), we still have to detect
36 * them in order to avoid further detection attempts confusing the touchpad.
37 * This way it at least works in PS/2 mouse compatibility mode.
38 */
33int focaltech_detect(struct psmouse *psmouse, bool set_properties) 39int focaltech_detect(struct psmouse *psmouse, bool set_properties)
34{ 40{
35 if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids)) 41 if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
@@ -37,16 +43,404 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
37 43
38 if (set_properties) { 44 if (set_properties) {
39 psmouse->vendor = "FocalTech"; 45 psmouse->vendor = "FocalTech";
40 psmouse->name = "FocalTech Touchpad in mouse emulation mode"; 46 psmouse->name = "FocalTech Touchpad";
41 } 47 }
42 48
43 return 0; 49 return 0;
44} 50}
45 51
46int focaltech_init(struct psmouse *psmouse) 52static void focaltech_reset(struct psmouse *psmouse)
47{ 53{
48 ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); 54 ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
49 psmouse_reset(psmouse); 55 psmouse_reset(psmouse);
56}
57
58#ifdef CONFIG_MOUSE_PS2_FOCALTECH
59
60/*
61 * Packet types - the numbers are not consecutive, so we might be missing
62 * something here.
63 */
64#define FOC_TOUCH 0x3 /* bitmap of active fingers */
65#define FOC_ABS 0x6 /* absolute position of one finger */
66#define FOC_REL 0x9 /* relative position of 1-2 fingers */
67
68#define FOC_MAX_FINGERS 5
69
70#define FOC_MAX_X 2431
71#define FOC_MAX_Y 1663
72
73/*
74 * Current state of a single finger on the touchpad.
75 */
76struct focaltech_finger_state {
77 /* The touchpad has generated a touch event for the finger */
78 bool active;
79
80 /*
81 * The touchpad has sent position data for the finger. The
82 * flag is 0 when the finger is not active, and there is a
83 * time between the first touch event for the finger and the
84 * following absolute position packet for the finger where the
85 * touchpad has declared the finger to be valid, but we do not
86 * have any valid position yet.
87 */
88 bool valid;
89
90 /*
91 * Absolute position (from the bottom left corner) of the
92 * finger.
93 */
94 unsigned int x;
95 unsigned int y;
96};
97
98/*
99 * Description of the current state of the touchpad hardware.
100 */
101struct focaltech_hw_state {
102 /*
103 * The touchpad tracks the positions of the fingers for us,
104 * the array indices correspond to the finger indices returned
105 * in the report packages.
106 */
107 struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
108
109 /* True if the clickpad has been pressed. */
110 bool pressed;
111};
112
113struct focaltech_data {
114 unsigned int x_max, y_max;
115 struct focaltech_hw_state state;
116};
117
118static void focaltech_report_state(struct psmouse *psmouse)
119{
120 struct focaltech_data *priv = psmouse->private;
121 struct focaltech_hw_state *state = &priv->state;
122 struct input_dev *dev = psmouse->dev;
123 int i;
124
125 for (i = 0; i < FOC_MAX_FINGERS; i++) {
126 struct focaltech_finger_state *finger = &state->fingers[i];
127 bool active = finger->active && finger->valid;
128
129 input_mt_slot(dev, i);
130 input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
131 if (active) {
132 input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
133 input_report_abs(dev, ABS_MT_POSITION_Y,
134 FOC_MAX_Y - finger->y);
135 }
136 }
137 input_mt_report_pointer_emulation(dev, true);
138
139 input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
140 input_sync(psmouse->dev);
141}
142
143static void focaltech_process_touch_packet(struct psmouse *psmouse,
144 unsigned char *packet)
145{
146 struct focaltech_data *priv = psmouse->private;
147 struct focaltech_hw_state *state = &priv->state;
148 unsigned char fingers = packet[1];
149 int i;
150
151 state->pressed = (packet[0] >> 4) & 1;
152
153 /* the second byte contains a bitmap of all fingers touching the pad */
154 for (i = 0; i < FOC_MAX_FINGERS; i++) {
155 state->fingers[i].active = fingers & 0x1;
156 if (!state->fingers[i].active) {
157 /*
158 * Even when the finger becomes active again, we still
159 * will have to wait for the first valid position.
160 */
161 state->fingers[i].valid = false;
162 }
163 fingers >>= 1;
164 }
165}
166
167static void focaltech_process_abs_packet(struct psmouse *psmouse,
168 unsigned char *packet)
169{
170 struct focaltech_data *priv = psmouse->private;
171 struct focaltech_hw_state *state = &priv->state;
172 unsigned int finger;
173
174 finger = (packet[1] >> 4) - 1;
175 if (finger >= FOC_MAX_FINGERS) {
176 psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
177 finger);
178 return;
179 }
180
181 state->pressed = (packet[0] >> 4) & 1;
182
183 /*
184 * packet[5] contains some kind of tool size in the most
185 * significant nibble. 0xff is a special value (latching) that
186 * signals a large contact area.
187 */
188 if (packet[5] == 0xff) {
189 state->fingers[finger].valid = false;
190 return;
191 }
192
193 state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
194 state->fingers[finger].y = (packet[3] << 8) | packet[4];
195 state->fingers[finger].valid = true;
196}
197
198static void focaltech_process_rel_packet(struct psmouse *psmouse,
199 unsigned char *packet)
200{
201 struct focaltech_data *priv = psmouse->private;
202 struct focaltech_hw_state *state = &priv->state;
203 int finger1, finger2;
204
205 state->pressed = packet[0] >> 7;
206 finger1 = ((packet[0] >> 4) & 0x7) - 1;
207 if (finger1 < FOC_MAX_FINGERS) {
208 state->fingers[finger1].x += (char)packet[1];
209 state->fingers[finger1].y += (char)packet[2];
210 } else {
211 psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
212 finger1);
213 }
214
215 /*
216 * If there is an odd number of fingers, the last relative
217 * packet only contains one finger. In this case, the second
218 * finger index in the packet is 0 (we subtract 1 in the lines
219 * above to create array indices, so the finger will overflow
220 * and be above FOC_MAX_FINGERS).
221 */
222 finger2 = ((packet[3] >> 4) & 0x7) - 1;
223 if (finger2 < FOC_MAX_FINGERS) {
224 state->fingers[finger2].x += (char)packet[4];
225 state->fingers[finger2].y += (char)packet[5];
226 }
227}
228
229static void focaltech_process_packet(struct psmouse *psmouse)
230{
231 unsigned char *packet = psmouse->packet;
232
233 switch (packet[0] & 0xf) {
234 case FOC_TOUCH:
235 focaltech_process_touch_packet(psmouse, packet);
236 break;
237
238 case FOC_ABS:
239 focaltech_process_abs_packet(psmouse, packet);
240 break;
241
242 case FOC_REL:
243 focaltech_process_rel_packet(psmouse, packet);
244 break;
245
246 default:
247 psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
248 break;
249 }
250
251 focaltech_report_state(psmouse);
252}
253
254static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
255{
256 if (psmouse->pktcnt >= 6) { /* Full packet received */
257 focaltech_process_packet(psmouse);
258 return PSMOUSE_FULL_PACKET;
259 }
260
261 /*
262 * We might want to do some validation of the data here, but
263 * we do not know the protocol well enough
264 */
265 return PSMOUSE_GOOD_DATA;
266}
267
268static int focaltech_switch_protocol(struct psmouse *psmouse)
269{
270 struct ps2dev *ps2dev = &psmouse->ps2dev;
271 unsigned char param[3];
272
273 param[0] = 0;
274 if (ps2_command(ps2dev, param, 0x10f8))
275 return -EIO;
276
277 if (ps2_command(ps2dev, param, 0x10f8))
278 return -EIO;
279
280 if (ps2_command(ps2dev, param, 0x10f8))
281 return -EIO;
282
283 param[0] = 1;
284 if (ps2_command(ps2dev, param, 0x10f8))
285 return -EIO;
286
287 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
288 return -EIO;
289
290 if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
291 return -EIO;
292
293 return 0;
294}
295
296static void focaltech_disconnect(struct psmouse *psmouse)
297{
298 focaltech_reset(psmouse);
299 kfree(psmouse->private);
300 psmouse->private = NULL;
301}
302
303static int focaltech_reconnect(struct psmouse *psmouse)
304{
305 int error;
306
307 focaltech_reset(psmouse);
308
309 error = focaltech_switch_protocol(psmouse);
310 if (error) {
311 psmouse_err(psmouse, "Unable to initialize the device\n");
312 return error;
313 }
314
315 return 0;
316}
317
318static void focaltech_set_input_params(struct psmouse *psmouse)
319{
320 struct input_dev *dev = psmouse->dev;
321 struct focaltech_data *priv = psmouse->private;
322
323 /*
324 * Undo part of setup done for us by psmouse core since touchpad
325 * is not a relative device.
326 */
327 __clear_bit(EV_REL, dev->evbit);
328 __clear_bit(REL_X, dev->relbit);
329 __clear_bit(REL_Y, dev->relbit);
330 __clear_bit(BTN_RIGHT, dev->keybit);
331 __clear_bit(BTN_MIDDLE, dev->keybit);
332
333 /*
334 * Now set up our capabilities.
335 */
336 __set_bit(EV_ABS, dev->evbit);
337 input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
338 input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
339 input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
340 __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
341}
342
343static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
344 unsigned char *param)
345{
346 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
347 return -EIO;
348
349 param[0] = 0;
350 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
351 return -EIO;
352
353 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
354 return -EIO;
355
356 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
357 return -EIO;
358
359 param[0] = reg;
360 if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
361 return -EIO;
362
363 if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
364 return -EIO;
365
366 return 0;
367}
368
369static int focaltech_read_size(struct psmouse *psmouse)
370{
371 struct ps2dev *ps2dev = &psmouse->ps2dev;
372 struct focaltech_data *priv = psmouse->private;
373 char param[3];
374
375 if (focaltech_read_register(ps2dev, 2, param))
376 return -EIO;
377
378 /* not sure whether this is 100% correct */
379 priv->x_max = (unsigned char)param[1] * 128;
380 priv->y_max = (unsigned char)param[2] * 128;
381
382 return 0;
383}
384int focaltech_init(struct psmouse *psmouse)
385{
386 struct focaltech_data *priv;
387 int error;
388
389 psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
390 GFP_KERNEL);
391 if (!priv)
392 return -ENOMEM;
393
394 focaltech_reset(psmouse);
395
396 error = focaltech_read_size(psmouse);
397 if (error) {
398 psmouse_err(psmouse,
399 "Unable to read the size of the touchpad\n");
400 goto fail;
401 }
402
403 error = focaltech_switch_protocol(psmouse);
404 if (error) {
405 psmouse_err(psmouse, "Unable to initialize the device\n");
406 goto fail;
407 }
408
409 focaltech_set_input_params(psmouse);
410
411 psmouse->protocol_handler = focaltech_process_byte;
412 psmouse->pktsize = 6;
413 psmouse->disconnect = focaltech_disconnect;
414 psmouse->reconnect = focaltech_reconnect;
415 psmouse->cleanup = focaltech_reset;
416 /* resync is not supported yet */
417 psmouse->resync_time = 0;
50 418
51 return 0; 419 return 0;
420
421fail:
422 focaltech_reset(psmouse);
423 kfree(priv);
424 return error;
52} 425}
426
427bool focaltech_supported(void)
428{
429 return true;
430}
431
432#else /* CONFIG_MOUSE_PS2_FOCALTECH */
433
434int focaltech_init(struct psmouse *psmouse)
435{
436 focaltech_reset(psmouse);
437
438 return 0;
439}
440
441bool focaltech_supported(void)
442{
443 return false;
444}
445
446#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h
index 498650c61e28..71870a9b548a 100644
--- a/drivers/input/mouse/focaltech.h
+++ b/drivers/input/mouse/focaltech.h
@@ -2,6 +2,7 @@
2 * Focaltech TouchPad PS/2 mouse driver 2 * Focaltech TouchPad PS/2 mouse driver
3 * 3 *
4 * Copyright (c) 2014 Red Hat Inc. 4 * Copyright (c) 2014 Red Hat Inc.
5 * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
@@ -18,5 +19,6 @@
18 19
19int focaltech_detect(struct psmouse *psmouse, bool set_properties); 20int focaltech_detect(struct psmouse *psmouse, bool set_properties);
20int focaltech_init(struct psmouse *psmouse); 21int focaltech_init(struct psmouse *psmouse);
22bool focaltech_supported(void);
21 23
22#endif 24#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 26994f6a2b2a..4a9de33a9afd 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -725,16 +725,19 @@ static int psmouse_extensions(struct psmouse *psmouse,
725 725
726/* Always check for focaltech, this is safe as it uses pnp-id matching */ 726/* Always check for focaltech, this is safe as it uses pnp-id matching */
727 if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { 727 if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
728 if (!set_properties || focaltech_init(psmouse) == 0) { 728 if (max_proto > PSMOUSE_IMEX) {
729 /* 729 if (!set_properties || focaltech_init(psmouse) == 0) {
730 * Not supported yet, use bare protocol. 730 if (focaltech_supported())
731 * Note that we need to also restrict 731 return PSMOUSE_FOCALTECH;
732 * psmouse_max_proto so that psmouse_initialize() 732 /*
733 * does not try to reset rate and resolution, 733 * Note that we need to also restrict
734 * because even that upsets the device. 734 * psmouse_max_proto so that psmouse_initialize()
735 */ 735 * does not try to reset rate and resolution,
736 psmouse_max_proto = PSMOUSE_PS2; 736 * because even that upsets the device.
737 return PSMOUSE_PS2; 737 */
738 psmouse_max_proto = PSMOUSE_PS2;
739 return PSMOUSE_PS2;
740 }
738 } 741 }
739 } 742 }
740 743
@@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
1063 .alias = "cortps", 1066 .alias = "cortps",
1064 .detect = cortron_detect, 1067 .detect = cortron_detect,
1065 }, 1068 },
1069#ifdef CONFIG_MOUSE_PS2_FOCALTECH
1070 {
1071 .type = PSMOUSE_FOCALTECH,
1072 .name = "FocalTechPS/2",
1073 .alias = "focaltech",
1074 .detect = focaltech_detect,
1075 .init = focaltech_init,
1076 },
1077#endif
1066 { 1078 {
1067 .type = PSMOUSE_AUTO, 1079 .type = PSMOUSE_AUTO,
1068 .name = "auto", 1080 .name = "auto",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index f4cf664c7db3..c2ff137ecbdb 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -96,6 +96,7 @@ enum psmouse_type {
96 PSMOUSE_FSP, 96 PSMOUSE_FSP,
97 PSMOUSE_SYNAPTICS_RELATIVE, 97 PSMOUSE_SYNAPTICS_RELATIVE,
98 PSMOUSE_CYPRESS, 98 PSMOUSE_CYPRESS,
99 PSMOUSE_FOCALTECH,
99 PSMOUSE_AUTO /* This one should always be last */ 100 PSMOUSE_AUTO /* This one should always be last */
100}; 101};
101 102