aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSebastian Kapfer <sebastian_kapfer@gmx.net>2009-12-15 11:39:50 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-16 01:15:25 -0500
commit1d9f26262aef6d63ff65eba0fd5f1583f342b69b (patch)
treeb2ed8e81376d0650cc6637e7c68abc27f013f819 /drivers
parent18f7ad59b0ef341fb9390cb79b2a39707c48257d (diff)
Input: ALPS - add interleaved protocol support (Dell E6x00 series)
Properly handle version of the protocol where standard PS/2 packets from trackpoint are stuffed into middle (byte 3-6) of the standard ALPS packets when both the touchpad and trackpoint are used together. The patch is based on work done by Matthew Chapman and additional research done by David Kubicek and Erik Osterholm: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/296610 Many thanks to David Kubicek for his efforts in researching fine points of this new version of the protocol, especially interaction between pad and stick in these models. Signed-off-by: Sebastian Kapfer <sebastian_kapfer@gmx.net> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/mouse/alps.c254
-rw-r--r--drivers/input/mouse/alps.h1
2 files changed, 228 insertions, 27 deletions
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index b03e7e0b4099..f93c2c0daf1f 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -5,6 +5,7 @@
5 * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com> 5 * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
6 * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru> 6 * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
7 * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> 7 * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
8 * Copyright (c) 2009 Sebastian Kapfer <sebastian_kapfer@gmx.net>
8 * 9 *
9 * ALPS detection, tap switching and status querying info is taken from 10 * ALPS detection, tap switching and status querying info is taken from
10 * tpconfig utility (by C. Scott Ananian and Bruce Kall). 11 * tpconfig utility (by C. Scott Ananian and Bruce Kall).
@@ -28,7 +29,6 @@
28#define dbg(format, arg...) do {} while (0) 29#define dbg(format, arg...) do {} while (0)
29#endif 30#endif
30 31
31
32#define ALPS_OLDPROTO 0x01 /* old style input */ 32#define ALPS_OLDPROTO 0x01 /* old style input */
33#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ 33#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */
34#define ALPS_PASS 0x04 /* device has a pass-through port */ 34#define ALPS_PASS 0x04 /* device has a pass-through port */
@@ -37,7 +37,8 @@
37#define ALPS_FW_BK_1 0x10 /* front & back buttons present */ 37#define ALPS_FW_BK_1 0x10 /* front & back buttons present */
38#define ALPS_FW_BK_2 0x20 /* front & back buttons present */ 38#define ALPS_FW_BK_2 0x20 /* front & back buttons present */
39#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ 39#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */
40 40#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with
41 6-byte ALPS packet */
41 42
42static const struct alps_model_info alps_model_data[] = { 43static const struct alps_model_info alps_model_data[] = {
43 { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ 44 { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
@@ -58,7 +59,9 @@ static const struct alps_model_info alps_model_data[] = {
58 { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ 59 { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
59 { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, 60 { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
60 { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ 61 { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
61 { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */ 62 /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
63 { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf,
64 ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
62 { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ 65 { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */
63}; 66};
64 67
@@ -69,20 +72,88 @@ static const struct alps_model_info alps_model_data[] = {
69 */ 72 */
70 73
71/* 74/*
72 * ALPS abolute Mode - new format 75 * PS/2 packet format
76 *
77 * byte 0: 0 0 YSGN XSGN 1 M R L
78 * byte 1: X7 X6 X5 X4 X3 X2 X1 X0
79 * byte 2: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
80 *
81 * Note that the device never signals overflow condition.
82 *
83 * ALPS absolute Mode - new format
73 * 84 *
74 * byte 0: 1 ? ? ? 1 ? ? ? 85 * byte 0: 1 ? ? ? 1 ? ? ?
75 * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 86 * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
76 * byte 2: 0 x10 x9 x8 x7 ? fin ges 87 * byte 2: 0 x10 x9 x8 x7 ? fin ges
77 * byte 3: 0 y9 y8 y7 1 M R L 88 * byte 3: 0 y9 y8 y7 1 M R L
78 * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 89 * byte 4: 0 y6 y5 y4 y3 y2 y1 y0
79 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 90 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0
80 * 91 *
92 * Dualpoint device -- interleaved packet format
93 *
94 * byte 0: 1 1 0 0 1 1 1 1
95 * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
96 * byte 2: 0 x10 x9 x8 x7 0 fin ges
97 * byte 3: 0 0 YSGN XSGN 1 1 1 1
98 * byte 4: X7 X6 X5 X4 X3 X2 X1 X0
99 * byte 5: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
100 * byte 6: 0 y9 y8 y7 1 m r l
101 * byte 7: 0 y6 y5 y4 y3 y2 y1 y0
102 * byte 8: 0 z6 z5 z4 z3 z2 z1 z0
103 *
104 * CAPITALS = stick, miniscules = touchpad
105 *
81 * ?'s can have different meanings on different models, 106 * ?'s can have different meanings on different models,
82 * such as wheel rotation, extra buttons, stick buttons 107 * such as wheel rotation, extra buttons, stick buttons
83 * on a dualpoint, etc. 108 * on a dualpoint, etc.
84 */ 109 */
85 110
111static bool alps_is_valid_first_byte(const struct alps_model_info *model,
112 unsigned char data)
113{
114 return (data & model->mask0) == model->byte0;
115}
116
117static void alps_report_buttons(struct psmouse *psmouse,
118 struct input_dev *dev1, struct input_dev *dev2,
119 int left, int right, int middle)
120{
121 struct alps_data *priv = psmouse->private;
122 const struct alps_model_info *model = priv->i;
123
124 if (model->flags & ALPS_PS2_INTERLEAVED) {
125 struct input_dev *dev;
126
127 /*
128 * If shared button has already been reported on the
129 * other device (dev2) then this event should be also
130 * sent through that device.
131 */
132 dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1;
133 input_report_key(dev, BTN_LEFT, left);
134
135 dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1;
136 input_report_key(dev, BTN_RIGHT, right);
137
138 dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1;
139 input_report_key(dev, BTN_MIDDLE, middle);
140
141 /*
142 * Sync the _other_ device now, we'll do the first
143 * device later once we report the rest of the events.
144 */
145 input_sync(dev2);
146 } else {
147 /*
148 * For devices with non-interleaved packets we know what
149 * device buttons belong to so we can simply report them.
150 */
151 input_report_key(dev1, BTN_LEFT, left);
152 input_report_key(dev1, BTN_RIGHT, right);
153 input_report_key(dev1, BTN_MIDDLE, middle);
154 }
155}
156
86static void alps_process_packet(struct psmouse *psmouse) 157static void alps_process_packet(struct psmouse *psmouse)
87{ 158{
88 struct alps_data *priv = psmouse->private; 159 struct alps_data *priv = psmouse->private;
@@ -93,18 +164,6 @@ static void alps_process_packet(struct psmouse *psmouse)
93 int x, y, z, ges, fin, left, right, middle; 164 int x, y, z, ges, fin, left, right, middle;
94 int back = 0, forward = 0; 165 int back = 0, forward = 0;
95 166
96 if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */
97 input_report_key(dev2, BTN_LEFT, packet[0] & 1);
98 input_report_key(dev2, BTN_RIGHT, packet[0] & 2);
99 input_report_key(dev2, BTN_MIDDLE, packet[0] & 4);
100 input_report_rel(dev2, REL_X,
101 packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
102 input_report_rel(dev2, REL_Y,
103 packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
104 input_sync(dev2);
105 return;
106 }
107
108 if (model->flags & ALPS_OLDPROTO) { 167 if (model->flags & ALPS_OLDPROTO) {
109 left = packet[2] & 0x10; 168 left = packet[2] & 0x10;
110 right = packet[2] & 0x08; 169 right = packet[2] & 0x08;
@@ -140,18 +199,13 @@ static void alps_process_packet(struct psmouse *psmouse)
140 input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); 199 input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
141 input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); 200 input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
142 201
143 input_report_key(dev2, BTN_LEFT, left); 202 alps_report_buttons(psmouse, dev2, dev, left, right, middle);
144 input_report_key(dev2, BTN_RIGHT, right);
145 input_report_key(dev2, BTN_MIDDLE, middle);
146 203
147 input_sync(dev);
148 input_sync(dev2); 204 input_sync(dev2);
149 return; 205 return;
150 } 206 }
151 207
152 input_report_key(dev, BTN_LEFT, left); 208 alps_report_buttons(psmouse, dev, dev2, left, right, middle);
153 input_report_key(dev, BTN_RIGHT, right);
154 input_report_key(dev, BTN_MIDDLE, middle);
155 209
156 /* Convert hardware tap to a reasonable Z value */ 210 /* Convert hardware tap to a reasonable Z value */
157 if (ges && !fin) 211 if (ges && !fin)
@@ -202,25 +256,168 @@ static void alps_process_packet(struct psmouse *psmouse)
202 input_sync(dev); 256 input_sync(dev);
203} 257}
204 258
259static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
260 unsigned char packet[],
261 bool report_buttons)
262{
263 struct alps_data *priv = psmouse->private;
264 struct input_dev *dev2 = priv->dev2;
265
266 if (report_buttons)
267 alps_report_buttons(psmouse, dev2, psmouse->dev,
268 packet[0] & 1, packet[0] & 2, packet[0] & 4);
269
270 input_report_rel(dev2, REL_X,
271 packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
272 input_report_rel(dev2, REL_Y,
273 packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
274
275 input_sync(dev2);
276}
277
278static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
279{
280 struct alps_data *priv = psmouse->private;
281
282 if (psmouse->pktcnt < 6)
283 return PSMOUSE_GOOD_DATA;
284
285 if (psmouse->pktcnt == 6) {
286 /*
287 * Start a timer to flush the packet if it ends up last
288 * 6-byte packet in the stream. Timer needs to fire
289 * psmouse core times out itself. 20 ms should be enough
290 * to decide if we are getting more data or not.
291 */
292 mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20));
293 return PSMOUSE_GOOD_DATA;
294 }
295
296 del_timer(&priv->timer);
297
298 if (psmouse->packet[6] & 0x80) {
299
300 /*
301 * Highest bit is set - that means we either had
302 * complete ALPS packet and this is start of the
303 * next packet or we got garbage.
304 */
305
306 if (((psmouse->packet[3] |
307 psmouse->packet[4] |
308 psmouse->packet[5]) & 0x80) ||
309 (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) {
310 dbg("refusing packet %x %x %x %x "
311 "(suspected interleaved ps/2)\n",
312 psmouse->packet[3], psmouse->packet[4],
313 psmouse->packet[5], psmouse->packet[6]);
314 return PSMOUSE_BAD_DATA;
315 }
316
317 alps_process_packet(psmouse);
318
319 /* Continue with the next packet */
320 psmouse->packet[0] = psmouse->packet[6];
321 psmouse->pktcnt = 1;
322
323 } else {
324
325 /*
326 * High bit is 0 - that means that we indeed got a PS/2
327 * packet in the middle of ALPS packet.
328 *
329 * There is also possibility that we got 6-byte ALPS
330 * packet followed by 3-byte packet from trackpoint. We
331 * can not distinguish between these 2 scenarios but
332 * becase the latter is unlikely to happen in course of
333 * normal operation (user would need to press all
334 * buttons on the pad and start moving trackpoint
335 * without touching the pad surface) we assume former.
336 * Even if we are wrong the wost thing that would happen
337 * the cursor would jump but we should not get protocol
338 * desynchronization.
339 */
340
341 alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
342 false);
343
344 /*
345 * Continue with the standard ALPS protocol handling,
346 * but make sure we won't process it as an interleaved
347 * packet again, which may happen if all buttons are
348 * pressed. To avoid this let's reset the 4th bit which
349 * is normally 1.
350 */
351 psmouse->packet[3] = psmouse->packet[6] & 0xf7;
352 psmouse->pktcnt = 4;
353 }
354
355 return PSMOUSE_GOOD_DATA;
356}
357
358static void alps_flush_packet(unsigned long data)
359{
360 struct psmouse *psmouse = (struct psmouse *)data;
361
362 serio_pause_rx(psmouse->ps2dev.serio);
363
364 if (psmouse->pktcnt == 6) {
365
366 /*
367 * We did not any more data in reasonable amount of time.
368 * Validate the last 3 bytes and process as a standard
369 * ALPS packet.
370 */
371 if ((psmouse->packet[3] |
372 psmouse->packet[4] |
373 psmouse->packet[5]) & 0x80) {
374 dbg("refusing packet %x %x %x "
375 "(suspected interleaved ps/2)\n",
376 psmouse->packet[3], psmouse->packet[4],
377 psmouse->packet[5]);
378 } else {
379 alps_process_packet(psmouse);
380 }
381 psmouse->pktcnt = 0;
382 }
383
384 serio_continue_rx(psmouse->ps2dev.serio);
385}
386
205static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) 387static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
206{ 388{
207 struct alps_data *priv = psmouse->private; 389 struct alps_data *priv = psmouse->private;
390 const struct alps_model_info *model = priv->i;
208 391
209 if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ 392 if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
210 if (psmouse->pktcnt == 3) { 393 if (psmouse->pktcnt == 3) {
211 alps_process_packet(psmouse); 394 alps_report_bare_ps2_packet(psmouse, psmouse->packet,
395 true);
212 return PSMOUSE_FULL_PACKET; 396 return PSMOUSE_FULL_PACKET;
213 } 397 }
214 return PSMOUSE_GOOD_DATA; 398 return PSMOUSE_GOOD_DATA;
215 } 399 }
216 400
217 if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0) 401 /* Check for PS/2 packet stuffed in the middle of ALPS packet. */
402
403 if ((model->flags & ALPS_PS2_INTERLEAVED) &&
404 psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {
405 return alps_handle_interleaved_ps2(psmouse);
406 }
407
408 if (!alps_is_valid_first_byte(model, psmouse->packet[0])) {
409 dbg("refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
410 psmouse->packet[0], model->mask0, model->byte0);
218 return PSMOUSE_BAD_DATA; 411 return PSMOUSE_BAD_DATA;
412 }
219 413
220 /* Bytes 2 - 6 should have 0 in the highest bit */ 414 /* Bytes 2 - 6 should have 0 in the highest bit */
221 if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 && 415 if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
222 (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) 416 (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
417 dbg("refusing packet[%i] = %x\n",
418 psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]);
223 return PSMOUSE_BAD_DATA; 419 return PSMOUSE_BAD_DATA;
420 }
224 421
225 if (psmouse->pktcnt == 6) { 422 if (psmouse->pktcnt == 6) {
226 alps_process_packet(psmouse); 423 alps_process_packet(psmouse);
@@ -459,6 +656,7 @@ static void alps_disconnect(struct psmouse *psmouse)
459 struct alps_data *priv = psmouse->private; 656 struct alps_data *priv = psmouse->private;
460 657
461 psmouse_reset(psmouse); 658 psmouse_reset(psmouse);
659 del_timer_sync(&priv->timer);
462 input_unregister_device(priv->dev2); 660 input_unregister_device(priv->dev2);
463 kfree(priv); 661 kfree(priv);
464} 662}
@@ -476,6 +674,8 @@ int alps_init(struct psmouse *psmouse)
476 goto init_fail; 674 goto init_fail;
477 675
478 priv->dev2 = dev2; 676 priv->dev2 = dev2;
677 setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);
678
479 psmouse->private = priv; 679 psmouse->private = priv;
480 680
481 model = alps_get_model(psmouse, &version); 681 model = alps_get_model(psmouse, &version);
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index bc87936fee1a..904ed8b3c8be 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -23,6 +23,7 @@ struct alps_data {
23 char phys[32]; /* Phys */ 23 char phys[32]; /* Phys */
24 const struct alps_model_info *i;/* Info */ 24 const struct alps_model_info *i;/* Info */
25 int prev_fin; /* Finger bit from previous packet */ 25 int prev_fin; /* Finger bit from previous packet */
26 struct timer_list timer;
26}; 27};
27 28
28#ifdef CONFIG_MOUSE_PS2_ALPS 29#ifdef CONFIG_MOUSE_PS2_ALPS