diff options
author | Seth Forshee <seth.forshee@canonical.com> | 2011-11-07 22:53:36 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-11-08 01:22:11 -0500 |
commit | 25bded7cd60fa460e520e9f819bd06f4c5cb53f0 (patch) | |
tree | 6aa3e07df800e00576ee268a185235d70b0ac09d /drivers/input/mouse | |
parent | b46615fe9215214ac00e26d35fc54dbe1c510803 (diff) |
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 <seth.forshee@canonical.com>
Acked-by: Chase Douglas <chase.douglas@canonical.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/alps.c | 791 | ||||
-rw-r--r-- | drivers/input/mouse/alps.h | 14 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse.h | 1 |
3 files changed, 768 insertions, 38 deletions
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 @@ | |||
23 | #include "psmouse.h" | 23 | #include "psmouse.h" |
24 | #include "alps.h" | 24 | #include "alps.h" |
25 | 25 | ||
26 | /* | ||
27 | * Definitions for ALPS version 3 and 4 command mode protocol | ||
28 | */ | ||
29 | #define ALPS_CMD_NIBBLE_10 0x01f2 | ||
30 | |||
31 | static const struct alps_nibble_commands alps_v3_nibble_commands[] = { | ||
32 | { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ | ||
33 | { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ | ||
34 | { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ | ||
35 | { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ | ||
36 | { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ | ||
37 | { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ | ||
38 | { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ | ||
39 | { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ | ||
40 | { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ | ||
41 | { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ | ||
42 | { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ | ||
43 | { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ | ||
44 | { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ | ||
45 | { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ | ||
46 | { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ | ||
47 | { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ | ||
48 | }; | ||
49 | |||
50 | static const struct alps_nibble_commands alps_v4_nibble_commands[] = { | ||
51 | { PSMOUSE_CMD_ENABLE, 0x00 }, /* 0 */ | ||
52 | { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ | ||
53 | { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ | ||
54 | { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ | ||
55 | { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ | ||
56 | { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ | ||
57 | { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ | ||
58 | { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ | ||
59 | { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ | ||
60 | { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ | ||
61 | { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ | ||
62 | { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ | ||
63 | { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ | ||
64 | { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ | ||
65 | { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ | ||
66 | { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ | ||
67 | }; | ||
68 | |||
69 | |||
26 | #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ | 70 | #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ |
27 | #define ALPS_PASS 0x04 /* device has a pass-through port */ | 71 | #define ALPS_PASS 0x04 /* device has a pass-through port */ |
28 | 72 | ||
@@ -34,30 +78,33 @@ | |||
34 | 6-byte ALPS packet */ | 78 | 6-byte ALPS packet */ |
35 | 79 | ||
36 | static const struct alps_model_info alps_model_data[] = { | 80 | static const struct alps_model_info alps_model_data[] = { |
37 | { { 0x32, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ | 81 | { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ |
38 | { { 0x33, 0x02, 0x0a }, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ | 82 | { { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ |
39 | { { 0x53, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, | 83 | { { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, |
40 | { { 0x53, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, | 84 | { { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, |
41 | { { 0x60, 0x03, 0xc8 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ | 85 | { { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ |
42 | { { 0x63, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, | 86 | { { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, |
43 | { { 0x63, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, | 87 | { { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, |
44 | { { 0x63, 0x02, 0x28 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ | 88 | { { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ |
45 | { { 0x63, 0x02, 0x3c }, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ | 89 | { { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ |
46 | { { 0x63, 0x02, 0x50 }, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ | 90 | { { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ |
47 | { { 0x63, 0x02, 0x64 }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, | 91 | { { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, |
48 | { { 0x63, 0x03, 0xc8 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ | 92 | { { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ |
49 | { { 0x73, 0x00, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ | 93 | { { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ |
50 | { { 0x73, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, | 94 | { { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, |
51 | { { 0x73, 0x02, 0x14 }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ | 95 | { { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ |
52 | { { 0x20, 0x02, 0x0e }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ | 96 | { { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ |
53 | { { 0x22, 0x02, 0x0a }, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, | 97 | { { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, |
54 | { { 0x22, 0x02, 0x14 }, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ | 98 | { { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ |
55 | /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ | 99 | /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ |
56 | { { 0x62, 0x02, 0x14 }, ALPS_PROTO_V2, 0xcf, 0xcf, | 100 | { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, |
57 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, | 101 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, |
58 | { { 0x73, 0x02, 0x50 }, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ | 102 | { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ |
59 | { { 0x52, 0x01, 0x14 }, ALPS_PROTO_V2, 0xff, 0xff, | 103 | { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, |
60 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ | 104 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ |
105 | { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, | ||
106 | { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, | ||
107 | { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, | ||
61 | }; | 108 | }; |
62 | 109 | ||
63 | /* | 110 | /* |
@@ -101,7 +148,7 @@ static void alps_report_buttons(struct psmouse *psmouse, | |||
101 | input_sync(dev2); | 148 | input_sync(dev2); |
102 | } | 149 | } |
103 | 150 | ||
104 | static void alps_process_packet(struct psmouse *psmouse) | 151 | static void alps_process_packet_v1_v2(struct psmouse *psmouse) |
105 | { | 152 | { |
106 | struct alps_data *priv = psmouse->private; | 153 | struct alps_data *priv = psmouse->private; |
107 | const struct alps_model_info *model = priv->i; | 154 | const struct alps_model_info *model = priv->i; |
@@ -203,6 +250,224 @@ static void alps_process_packet(struct psmouse *psmouse) | |||
203 | input_sync(dev); | 250 | input_sync(dev); |
204 | } | 251 | } |
205 | 252 | ||
253 | static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) | ||
254 | { | ||
255 | struct alps_data *priv = psmouse->private; | ||
256 | unsigned char *packet = psmouse->packet; | ||
257 | struct input_dev *dev = priv->dev2; | ||
258 | int x, y, z, left, right, middle; | ||
259 | |||
260 | /* Sanity check packet */ | ||
261 | if (!(packet[0] & 0x40)) { | ||
262 | psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * There's a special packet that seems to indicate the end | ||
268 | * of a stream of trackstick data. Filter these out. | ||
269 | */ | ||
270 | if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) | ||
271 | return; | ||
272 | |||
273 | x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); | ||
274 | y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); | ||
275 | z = (packet[4] & 0x7c) >> 2; | ||
276 | |||
277 | /* | ||
278 | * The x and y values tend to be quite large, and when used | ||
279 | * alone the trackstick is difficult to use. Scale them down | ||
280 | * to compensate. | ||
281 | */ | ||
282 | x /= 8; | ||
283 | y /= 8; | ||
284 | |||
285 | input_report_rel(dev, REL_X, x); | ||
286 | input_report_rel(dev, REL_Y, -y); | ||
287 | |||
288 | /* | ||
289 | * Most ALPS models report the trackstick buttons in the touchpad | ||
290 | * packets, but a few report them here. No reliable way has been | ||
291 | * found to differentiate between the models upfront, so we enable | ||
292 | * the quirk in response to seeing a button press in the trackstick | ||
293 | * packet. | ||
294 | */ | ||
295 | left = packet[3] & 0x01; | ||
296 | right = packet[3] & 0x02; | ||
297 | middle = packet[3] & 0x04; | ||
298 | |||
299 | if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && | ||
300 | (left || right || middle)) | ||
301 | priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; | ||
302 | |||
303 | if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { | ||
304 | input_report_key(dev, BTN_LEFT, left); | ||
305 | input_report_key(dev, BTN_RIGHT, right); | ||
306 | input_report_key(dev, BTN_MIDDLE, middle); | ||
307 | } | ||
308 | |||
309 | input_sync(dev); | ||
310 | return; | ||
311 | } | ||
312 | |||
313 | static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) | ||
314 | { | ||
315 | struct alps_data *priv = psmouse->private; | ||
316 | unsigned char *packet = psmouse->packet; | ||
317 | struct input_dev *dev = psmouse->dev; | ||
318 | struct input_dev *dev2 = priv->dev2; | ||
319 | int x, y, z; | ||
320 | int left, right, middle; | ||
321 | |||
322 | /* | ||
323 | * There's no single feature of touchpad position and bitmap | ||
324 | * packets that can be used to distinguish between them. We | ||
325 | * rely on the fact that a bitmap packet should always follow | ||
326 | * a position packet with bit 6 of packet[4] set. | ||
327 | */ | ||
328 | if (priv->multi_packet) { | ||
329 | priv->multi_packet = 0; | ||
330 | |||
331 | /* | ||
332 | * Sometimes a position packet will indicate a multi-packet | ||
333 | * sequence, but then what follows is another position | ||
334 | * packet. Check for this, and when it happens process the | ||
335 | * position packet as usual. | ||
336 | */ | ||
337 | if (packet[0] & 0x40) { | ||
338 | /* | ||
339 | * Bitmap packets are not yet supported, so for now | ||
340 | * just ignore them. | ||
341 | */ | ||
342 | return; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | if (!priv->multi_packet && (packet[4] & 0x40)) | ||
347 | priv->multi_packet = 1; | ||
348 | else | ||
349 | priv->multi_packet = 0; | ||
350 | |||
351 | left = packet[3] & 0x01; | ||
352 | right = packet[3] & 0x02; | ||
353 | middle = packet[3] & 0x04; | ||
354 | |||
355 | x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) | | ||
356 | ((packet[0] & 0x30) >> 4); | ||
357 | y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f); | ||
358 | z = packet[5] & 0x7f; | ||
359 | |||
360 | /* | ||
361 | * Sometimes the hardware sends a single packet with z = 0 | ||
362 | * in the middle of a stream. Real releases generate packets | ||
363 | * with x, y, and z all zero, so these seem to be flukes. | ||
364 | * Ignore them. | ||
365 | */ | ||
366 | if (x && y && !z) | ||
367 | return; | ||
368 | |||
369 | if (z >= 64) | ||
370 | input_report_key(dev, BTN_TOUCH, 1); | ||
371 | else | ||
372 | input_report_key(dev, BTN_TOUCH, 0); | ||
373 | |||
374 | if (z > 0) { | ||
375 | input_report_abs(dev, ABS_X, x); | ||
376 | input_report_abs(dev, ABS_Y, y); | ||
377 | } | ||
378 | input_report_abs(dev, ABS_PRESSURE, z); | ||
379 | |||
380 | input_report_key(dev, BTN_TOOL_FINGER, z > 0); | ||
381 | input_report_key(dev, BTN_LEFT, left); | ||
382 | input_report_key(dev, BTN_RIGHT, right); | ||
383 | input_report_key(dev, BTN_MIDDLE, middle); | ||
384 | |||
385 | input_sync(dev); | ||
386 | |||
387 | if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { | ||
388 | left = packet[3] & 0x10; | ||
389 | right = packet[3] & 0x20; | ||
390 | middle = packet[3] & 0x40; | ||
391 | |||
392 | input_report_key(dev2, BTN_LEFT, left); | ||
393 | input_report_key(dev2, BTN_RIGHT, right); | ||
394 | input_report_key(dev2, BTN_MIDDLE, middle); | ||
395 | input_sync(dev2); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | static void alps_process_packet_v3(struct psmouse *psmouse) | ||
400 | { | ||
401 | unsigned char *packet = psmouse->packet; | ||
402 | |||
403 | /* | ||
404 | * v3 protocol packets come in three types, two representing | ||
405 | * touchpad data and one representing trackstick data. | ||
406 | * Trackstick packets seem to be distinguished by always | ||
407 | * having 0x3f in the last byte. This value has never been | ||
408 | * observed in the last byte of either of the other types | ||
409 | * of packets. | ||
410 | */ | ||
411 | if (packet[5] == 0x3f) { | ||
412 | alps_process_trackstick_packet_v3(psmouse); | ||
413 | return; | ||
414 | } | ||
415 | |||
416 | alps_process_touchpad_packet_v3(psmouse); | ||
417 | } | ||
418 | |||
419 | static void alps_process_packet_v4(struct psmouse *psmouse) | ||
420 | { | ||
421 | unsigned char *packet = psmouse->packet; | ||
422 | struct input_dev *dev = psmouse->dev; | ||
423 | int x, y, z; | ||
424 | int left, right; | ||
425 | |||
426 | left = packet[4] & 0x01; | ||
427 | right = packet[4] & 0x02; | ||
428 | |||
429 | x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | | ||
430 | ((packet[0] & 0x30) >> 4); | ||
431 | y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); | ||
432 | z = packet[5] & 0x7f; | ||
433 | |||
434 | if (z >= 64) | ||
435 | input_report_key(dev, BTN_TOUCH, 1); | ||
436 | else | ||
437 | input_report_key(dev, BTN_TOUCH, 0); | ||
438 | |||
439 | if (z > 0) { | ||
440 | input_report_abs(dev, ABS_X, x); | ||
441 | input_report_abs(dev, ABS_Y, y); | ||
442 | } | ||
443 | input_report_abs(dev, ABS_PRESSURE, z); | ||
444 | |||
445 | input_report_key(dev, BTN_TOOL_FINGER, z > 0); | ||
446 | input_report_key(dev, BTN_LEFT, left); | ||
447 | input_report_key(dev, BTN_RIGHT, right); | ||
448 | |||
449 | input_sync(dev); | ||
450 | } | ||
451 | |||
452 | static void alps_process_packet(struct psmouse *psmouse) | ||
453 | { | ||
454 | struct alps_data *priv = psmouse->private; | ||
455 | const struct alps_model_info *model = priv->i; | ||
456 | |||
457 | switch (model->proto_version) { | ||
458 | case ALPS_PROTO_V1: | ||
459 | case ALPS_PROTO_V2: | ||
460 | alps_process_packet_v1_v2(psmouse); | ||
461 | break; | ||
462 | case ALPS_PROTO_V3: | ||
463 | alps_process_packet_v3(psmouse); | ||
464 | break; | ||
465 | case ALPS_PROTO_V4: | ||
466 | alps_process_packet_v4(psmouse); | ||
467 | break; | ||
468 | } | ||
469 | } | ||
470 | |||
206 | static void alps_report_bare_ps2_packet(struct psmouse *psmouse, | 471 | static void alps_report_bare_ps2_packet(struct psmouse *psmouse, |
207 | unsigned char packet[], | 472 | unsigned char packet[], |
208 | bool report_buttons) | 473 | bool report_buttons) |
@@ -376,11 +641,127 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) | |||
376 | return PSMOUSE_GOOD_DATA; | 641 | return PSMOUSE_GOOD_DATA; |
377 | } | 642 | } |
378 | 643 | ||
644 | static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble) | ||
645 | { | ||
646 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
647 | struct alps_data *priv = psmouse->private; | ||
648 | int command; | ||
649 | unsigned char *param; | ||
650 | unsigned char dummy[4]; | ||
651 | |||
652 | BUG_ON(nibble > 0xf); | ||
653 | |||
654 | command = priv->nibble_commands[nibble].command; | ||
655 | param = (command & 0x0f00) ? | ||
656 | dummy : (unsigned char *)&priv->nibble_commands[nibble].data; | ||
657 | |||
658 | if (ps2_command(ps2dev, param, command)) | ||
659 | return -1; | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr) | ||
665 | { | ||
666 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
667 | struct alps_data *priv = psmouse->private; | ||
668 | int i, nibble; | ||
669 | |||
670 | if (ps2_command(ps2dev, NULL, priv->addr_command)) | ||
671 | return -1; | ||
672 | |||
673 | for (i = 12; i >= 0; i -= 4) { | ||
674 | nibble = (addr >> i) & 0xf; | ||
675 | if (alps_command_mode_send_nibble(psmouse, nibble)) | ||
676 | return -1; | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr) | ||
683 | { | ||
684 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
685 | unsigned char param[4]; | ||
686 | |||
687 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
688 | return -1; | ||
689 | |||
690 | /* | ||
691 | * The address being read is returned in the first two bytes | ||
692 | * of the result. Check that this address matches the expected | ||
693 | * address. | ||
694 | */ | ||
695 | if (addr != ((param[0] << 8) | param[1])) | ||
696 | return -1; | ||
697 | |||
698 | return param[2]; | ||
699 | } | ||
700 | |||
701 | static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr) | ||
702 | { | ||
703 | if (alps_command_mode_set_addr(psmouse, addr)) | ||
704 | return -1; | ||
705 | return __alps_command_mode_read_reg(psmouse, addr); | ||
706 | } | ||
707 | |||
708 | static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value) | ||
709 | { | ||
710 | if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) | ||
711 | return -1; | ||
712 | if (alps_command_mode_send_nibble(psmouse, value & 0xf)) | ||
713 | return -1; | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, | ||
718 | u8 value) | ||
719 | { | ||
720 | if (alps_command_mode_set_addr(psmouse, addr)) | ||
721 | return -1; | ||
722 | return __alps_command_mode_write_reg(psmouse, value); | ||
723 | } | ||
724 | |||
725 | static int alps_enter_command_mode(struct psmouse *psmouse, | ||
726 | unsigned char *resp) | ||
727 | { | ||
728 | unsigned char param[4]; | ||
729 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
730 | |||
731 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || | ||
732 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || | ||
733 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || | ||
734 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { | ||
735 | psmouse_err(psmouse, "failed to enter command mode\n"); | ||
736 | return -1; | ||
737 | } | ||
738 | |||
739 | if (param[0] != 0x88 && param[1] != 0x07) { | ||
740 | psmouse_dbg(psmouse, | ||
741 | "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", | ||
742 | param[0], param[1], param[2]); | ||
743 | return -1; | ||
744 | } | ||
745 | |||
746 | if (resp) | ||
747 | *resp = param[2]; | ||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | static inline int alps_exit_command_mode(struct psmouse *psmouse) | ||
752 | { | ||
753 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
754 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) | ||
755 | return -1; | ||
756 | return 0; | ||
757 | } | ||
758 | |||
379 | static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) | 759 | static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) |
380 | { | 760 | { |
381 | struct ps2dev *ps2dev = &psmouse->ps2dev; | 761 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
382 | static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; | 762 | static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; |
383 | unsigned char param[4]; | 763 | unsigned char param[4]; |
764 | const struct alps_model_info *model = NULL; | ||
384 | int i; | 765 | int i; |
385 | 766 | ||
386 | /* | 767 | /* |
@@ -428,12 +809,41 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int | |||
428 | *version = (param[0] << 8) | (param[1] << 4) | i; | 809 | *version = (param[0] << 8) | (param[1] << 4) | i; |
429 | } | 810 | } |
430 | 811 | ||
431 | for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) | 812 | for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { |
432 | if (!memcmp(param, alps_model_data[i].signature, | 813 | if (!memcmp(param, alps_model_data[i].signature, |
433 | sizeof(alps_model_data[i].signature))) | 814 | sizeof(alps_model_data[i].signature))) { |
434 | return alps_model_data + i; | 815 | model = alps_model_data + i; |
816 | break; | ||
817 | } | ||
818 | } | ||
435 | 819 | ||
436 | return NULL; | 820 | if (model && model->proto_version > ALPS_PROTO_V2) { |
821 | /* | ||
822 | * Need to check command mode response to identify | ||
823 | * model | ||
824 | */ | ||
825 | model = NULL; | ||
826 | if (alps_enter_command_mode(psmouse, param)) { | ||
827 | psmouse_warn(psmouse, | ||
828 | "touchpad failed to enter command mode\n"); | ||
829 | } else { | ||
830 | for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { | ||
831 | if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && | ||
832 | alps_model_data[i].command_mode_resp == param[0]) { | ||
833 | model = alps_model_data + i; | ||
834 | break; | ||
835 | } | ||
836 | } | ||
837 | alps_exit_command_mode(psmouse); | ||
838 | |||
839 | if (!model) | ||
840 | psmouse_dbg(psmouse, | ||
841 | "Unknown command mode response %2.2x\n", | ||
842 | param[0]); | ||
843 | } | ||
844 | } | ||
845 | |||
846 | return model; | ||
437 | } | 847 | } |
438 | 848 | ||
439 | /* | 849 | /* |
@@ -441,7 +851,7 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int | |||
441 | * subsequent commands. It looks like glidepad is behind stickpointer, | 851 | * subsequent commands. It looks like glidepad is behind stickpointer, |
442 | * I'd thought it would be other way around... | 852 | * I'd thought it would be other way around... |
443 | */ | 853 | */ |
444 | static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) | 854 | static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable) |
445 | { | 855 | { |
446 | struct ps2dev *ps2dev = &psmouse->ps2dev; | 856 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
447 | int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; | 857 | int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; |
@@ -458,7 +868,7 @@ static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) | |||
458 | return 0; | 868 | return 0; |
459 | } | 869 | } |
460 | 870 | ||
461 | static int alps_absolute_mode(struct psmouse *psmouse) | 871 | static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) |
462 | { | 872 | { |
463 | struct ps2dev *ps2dev = &psmouse->ps2dev; | 873 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
464 | 874 | ||
@@ -533,13 +943,13 @@ static int alps_poll(struct psmouse *psmouse) | |||
533 | bool poll_failed; | 943 | bool poll_failed; |
534 | 944 | ||
535 | if (priv->i->flags & ALPS_PASS) | 945 | if (priv->i->flags & ALPS_PASS) |
536 | alps_passthrough_mode(psmouse, true); | 946 | alps_passthrough_mode_v2(psmouse, true); |
537 | 947 | ||
538 | poll_failed = ps2_command(&psmouse->ps2dev, buf, | 948 | poll_failed = ps2_command(&psmouse->ps2dev, buf, |
539 | PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; | 949 | PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; |
540 | 950 | ||
541 | if (priv->i->flags & ALPS_PASS) | 951 | if (priv->i->flags & ALPS_PASS) |
542 | alps_passthrough_mode(psmouse, false); | 952 | alps_passthrough_mode_v2(psmouse, false); |
543 | 953 | ||
544 | if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) | 954 | if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) |
545 | return -1; | 955 | return -1; |
@@ -556,13 +966,13 @@ static int alps_poll(struct psmouse *psmouse) | |||
556 | return 0; | 966 | return 0; |
557 | } | 967 | } |
558 | 968 | ||
559 | static int alps_hw_init(struct psmouse *psmouse) | 969 | static int alps_hw_init_v1_v2(struct psmouse *psmouse) |
560 | { | 970 | { |
561 | struct alps_data *priv = psmouse->private; | 971 | struct alps_data *priv = psmouse->private; |
562 | const struct alps_model_info *model = priv->i; | 972 | const struct alps_model_info *model = priv->i; |
563 | 973 | ||
564 | if ((model->flags & ALPS_PASS) && | 974 | if ((model->flags & ALPS_PASS) && |
565 | alps_passthrough_mode(psmouse, true)) { | 975 | alps_passthrough_mode_v2(psmouse, true)) { |
566 | return -1; | 976 | return -1; |
567 | } | 977 | } |
568 | 978 | ||
@@ -571,13 +981,13 @@ static int alps_hw_init(struct psmouse *psmouse) | |||
571 | return -1; | 981 | return -1; |
572 | } | 982 | } |
573 | 983 | ||
574 | if (alps_absolute_mode(psmouse)) { | 984 | if (alps_absolute_mode_v1_v2(psmouse)) { |
575 | psmouse_err(psmouse, "Failed to enable absolute mode\n"); | 985 | psmouse_err(psmouse, "Failed to enable absolute mode\n"); |
576 | return -1; | 986 | return -1; |
577 | } | 987 | } |
578 | 988 | ||
579 | if ((model->flags & ALPS_PASS) && | 989 | if ((model->flags & ALPS_PASS) && |
580 | alps_passthrough_mode(psmouse, false)) { | 990 | alps_passthrough_mode_v2(psmouse, false)) { |
581 | return -1; | 991 | return -1; |
582 | } | 992 | } |
583 | 993 | ||
@@ -590,6 +1000,297 @@ static int alps_hw_init(struct psmouse *psmouse) | |||
590 | return 0; | 1000 | return 0; |
591 | } | 1001 | } |
592 | 1002 | ||
1003 | /* | ||
1004 | * Enable or disable passthrough mode to the trackstick. Must be in | ||
1005 | * command mode when calling this function. | ||
1006 | */ | ||
1007 | static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) | ||
1008 | { | ||
1009 | int reg_val; | ||
1010 | |||
1011 | reg_val = alps_command_mode_read_reg(psmouse, 0x0008); | ||
1012 | if (reg_val == -1) | ||
1013 | return -1; | ||
1014 | |||
1015 | if (enable) | ||
1016 | reg_val |= 0x01; | ||
1017 | else | ||
1018 | reg_val &= ~0x01; | ||
1019 | |||
1020 | if (__alps_command_mode_write_reg(psmouse, reg_val)) | ||
1021 | return -1; | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | /* Must be in command mode when calling this function */ | ||
1027 | static int alps_absolute_mode_v3(struct psmouse *psmouse) | ||
1028 | { | ||
1029 | int reg_val; | ||
1030 | |||
1031 | reg_val = alps_command_mode_read_reg(psmouse, 0x0004); | ||
1032 | if (reg_val == -1) | ||
1033 | return -1; | ||
1034 | |||
1035 | reg_val |= 0x06; | ||
1036 | if (__alps_command_mode_write_reg(psmouse, reg_val)) | ||
1037 | return -1; | ||
1038 | |||
1039 | return 0; | ||
1040 | } | ||
1041 | |||
1042 | static int alps_hw_init_v3(struct psmouse *psmouse) | ||
1043 | { | ||
1044 | struct alps_data *priv = psmouse->private; | ||
1045 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
1046 | int reg_val; | ||
1047 | unsigned char param[4]; | ||
1048 | |||
1049 | priv->nibble_commands = alps_v3_nibble_commands; | ||
1050 | priv->addr_command = PSMOUSE_CMD_RESET_WRAP; | ||
1051 | |||
1052 | if (alps_enter_command_mode(psmouse, NULL)) | ||
1053 | goto error; | ||
1054 | |||
1055 | /* Check for trackstick */ | ||
1056 | reg_val = alps_command_mode_read_reg(psmouse, 0x0008); | ||
1057 | if (reg_val == -1) | ||
1058 | goto error; | ||
1059 | if (reg_val & 0x80) { | ||
1060 | if (alps_passthrough_mode_v3(psmouse, true)) | ||
1061 | goto error; | ||
1062 | if (alps_exit_command_mode(psmouse)) | ||
1063 | goto error; | ||
1064 | |||
1065 | /* | ||
1066 | * E7 report for the trackstick | ||
1067 | * | ||
1068 | * There have been reports of failures to seem to trace back | ||
1069 | * to the above trackstick check failing. When these occur | ||
1070 | * this E7 report fails, so when that happens we continue | ||
1071 | * with the assumption that there isn't a trackstick after | ||
1072 | * all. | ||
1073 | */ | ||
1074 | param[0] = 0x64; | ||
1075 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
1076 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
1077 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
1078 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { | ||
1079 | psmouse_warn(psmouse, "trackstick E7 report failed\n"); | ||
1080 | } else { | ||
1081 | psmouse_dbg(psmouse, | ||
1082 | "trackstick E7 report: %2.2x %2.2x %2.2x\n", | ||
1083 | param[0], param[1], param[2]); | ||
1084 | |||
1085 | /* | ||
1086 | * Not sure what this does, but it is absolutely | ||
1087 | * essential. Without it, the touchpad does not | ||
1088 | * work at all and the trackstick just emits normal | ||
1089 | * PS/2 packets. | ||
1090 | */ | ||
1091 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
1092 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
1093 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
1094 | alps_command_mode_send_nibble(psmouse, 0x9) || | ||
1095 | alps_command_mode_send_nibble(psmouse, 0x4)) { | ||
1096 | psmouse_err(psmouse, | ||
1097 | "Error sending magic E6 sequence\n"); | ||
1098 | goto error_passthrough; | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | if (alps_enter_command_mode(psmouse, NULL)) | ||
1103 | goto error_passthrough; | ||
1104 | if (alps_passthrough_mode_v3(psmouse, false)) | ||
1105 | goto error; | ||
1106 | } | ||
1107 | |||
1108 | if (alps_absolute_mode_v3(psmouse)) { | ||
1109 | psmouse_err(psmouse, "Failed to enter absolute mode\n"); | ||
1110 | goto error; | ||
1111 | } | ||
1112 | |||
1113 | reg_val = alps_command_mode_read_reg(psmouse, 0x0006); | ||
1114 | if (reg_val == -1) | ||
1115 | goto error; | ||
1116 | if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) | ||
1117 | goto error; | ||
1118 | |||
1119 | reg_val = alps_command_mode_read_reg(psmouse, 0x0007); | ||
1120 | if (reg_val == -1) | ||
1121 | goto error; | ||
1122 | if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) | ||
1123 | goto error; | ||
1124 | |||
1125 | if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) | ||
1126 | goto error; | ||
1127 | if (__alps_command_mode_write_reg(psmouse, 0x04)) | ||
1128 | goto error; | ||
1129 | |||
1130 | if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) | ||
1131 | goto error; | ||
1132 | if (__alps_command_mode_write_reg(psmouse, 0x03)) | ||
1133 | goto error; | ||
1134 | |||
1135 | if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) | ||
1136 | goto error; | ||
1137 | if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) | ||
1138 | goto error; | ||
1139 | |||
1140 | if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) | ||
1141 | goto error; | ||
1142 | if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) | ||
1143 | goto error; | ||
1144 | |||
1145 | /* | ||
1146 | * This ensures the trackstick packets are in the format | ||
1147 | * supported by this driver. If bit 1 isn't set the packet | ||
1148 | * format is different. | ||
1149 | */ | ||
1150 | if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) | ||
1151 | goto error; | ||
1152 | |||
1153 | alps_exit_command_mode(psmouse); | ||
1154 | |||
1155 | /* Set rate and enable data reporting */ | ||
1156 | param[0] = 0x64; | ||
1157 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || | ||
1158 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { | ||
1159 | psmouse_err(psmouse, "Failed to enable data reporting\n"); | ||
1160 | return -1; | ||
1161 | } | ||
1162 | |||
1163 | return 0; | ||
1164 | |||
1165 | error_passthrough: | ||
1166 | /* Something failed while in passthrough mode, so try to get out */ | ||
1167 | if (!alps_enter_command_mode(psmouse, NULL)) | ||
1168 | alps_passthrough_mode_v3(psmouse, false); | ||
1169 | error: | ||
1170 | /* | ||
1171 | * Leaving the touchpad in command mode will essentially render | ||
1172 | * it unusable until the machine reboots, so exit it here just | ||
1173 | * to be safe | ||
1174 | */ | ||
1175 | alps_exit_command_mode(psmouse); | ||
1176 | return -1; | ||
1177 | } | ||
1178 | |||
1179 | /* Must be in command mode when calling this function */ | ||
1180 | static int alps_absolute_mode_v4(struct psmouse *psmouse) | ||
1181 | { | ||
1182 | int reg_val; | ||
1183 | |||
1184 | reg_val = alps_command_mode_read_reg(psmouse, 0x0004); | ||
1185 | if (reg_val == -1) | ||
1186 | return -1; | ||
1187 | |||
1188 | reg_val |= 0x02; | ||
1189 | if (__alps_command_mode_write_reg(psmouse, reg_val)) | ||
1190 | return -1; | ||
1191 | |||
1192 | return 0; | ||
1193 | } | ||
1194 | |||
1195 | static int alps_hw_init_v4(struct psmouse *psmouse) | ||
1196 | { | ||
1197 | struct alps_data *priv = psmouse->private; | ||
1198 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
1199 | unsigned char param[4]; | ||
1200 | |||
1201 | priv->nibble_commands = alps_v4_nibble_commands; | ||
1202 | priv->addr_command = PSMOUSE_CMD_DISABLE; | ||
1203 | |||
1204 | if (alps_enter_command_mode(psmouse, NULL)) | ||
1205 | goto error; | ||
1206 | |||
1207 | if (alps_absolute_mode_v4(psmouse)) { | ||
1208 | psmouse_err(psmouse, "Failed to enter absolute mode\n"); | ||
1209 | goto error; | ||
1210 | } | ||
1211 | |||
1212 | if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c)) | ||
1213 | goto error; | ||
1214 | |||
1215 | if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03)) | ||
1216 | goto error; | ||
1217 | |||
1218 | if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03)) | ||
1219 | goto error; | ||
1220 | |||
1221 | if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15)) | ||
1222 | goto error; | ||
1223 | |||
1224 | if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01)) | ||
1225 | goto error; | ||
1226 | |||
1227 | if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03)) | ||
1228 | goto error; | ||
1229 | |||
1230 | if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03)) | ||
1231 | goto error; | ||
1232 | |||
1233 | if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03)) | ||
1234 | goto error; | ||
1235 | |||
1236 | alps_exit_command_mode(psmouse); | ||
1237 | |||
1238 | /* | ||
1239 | * This sequence changes the output from a 9-byte to an | ||
1240 | * 8-byte format. All the same data seems to be present, | ||
1241 | * just in a more compact format. | ||
1242 | */ | ||
1243 | param[0] = 0xc8; | ||
1244 | param[1] = 0x64; | ||
1245 | param[2] = 0x50; | ||
1246 | if (ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || | ||
1247 | ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE) || | ||
1248 | ps2_command(ps2dev, ¶m[2], PSMOUSE_CMD_SETRATE) || | ||
1249 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) | ||
1250 | return -1; | ||
1251 | |||
1252 | /* Set rate and enable data reporting */ | ||
1253 | param[0] = 0x64; | ||
1254 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || | ||
1255 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { | ||
1256 | psmouse_err(psmouse, "Failed to enable data reporting\n"); | ||
1257 | return -1; | ||
1258 | } | ||
1259 | |||
1260 | return 0; | ||
1261 | |||
1262 | error: | ||
1263 | /* | ||
1264 | * Leaving the touchpad in command mode will essentially render | ||
1265 | * it unusable until the machine reboots, so exit it here just | ||
1266 | * to be safe | ||
1267 | */ | ||
1268 | alps_exit_command_mode(psmouse); | ||
1269 | return -1; | ||
1270 | } | ||
1271 | |||
1272 | static int alps_hw_init(struct psmouse *psmouse) | ||
1273 | { | ||
1274 | struct alps_data *priv = psmouse->private; | ||
1275 | const struct alps_model_info *model = priv->i; | ||
1276 | int ret = -1; | ||
1277 | |||
1278 | switch (model->proto_version) { | ||
1279 | case ALPS_PROTO_V1: | ||
1280 | case ALPS_PROTO_V2: | ||
1281 | ret = alps_hw_init_v1_v2(psmouse); | ||
1282 | break; | ||
1283 | case ALPS_PROTO_V3: | ||
1284 | ret = alps_hw_init_v3(psmouse); | ||
1285 | break; | ||
1286 | case ALPS_PROTO_V4: | ||
1287 | ret = alps_hw_init_v4(psmouse); | ||
1288 | break; | ||
1289 | } | ||
1290 | |||
1291 | return ret; | ||
1292 | } | ||
1293 | |||
593 | static int alps_reconnect(struct psmouse *psmouse) | 1294 | static int alps_reconnect(struct psmouse *psmouse) |
594 | { | 1295 | { |
595 | const struct alps_model_info *model; | 1296 | const struct alps_model_info *model; |
@@ -630,6 +1331,8 @@ int alps_init(struct psmouse *psmouse) | |||
630 | 1331 | ||
631 | psmouse->private = priv; | 1332 | psmouse->private = priv; |
632 | 1333 | ||
1334 | psmouse_reset(psmouse); | ||
1335 | |||
633 | model = alps_get_model(psmouse, &version); | 1336 | model = alps_get_model(psmouse, &version); |
634 | if (!model) | 1337 | if (!model) |
635 | goto init_fail; | 1338 | goto init_fail; |
@@ -657,8 +1360,20 @@ int alps_init(struct psmouse *psmouse) | |||
657 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); | 1360 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); |
658 | 1361 | ||
659 | dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); | 1362 | dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); |
660 | input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); | 1363 | |
661 | input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); | 1364 | switch (model->proto_version) { |
1365 | case ALPS_PROTO_V1: | ||
1366 | case ALPS_PROTO_V2: | ||
1367 | input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); | ||
1368 | input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); | ||
1369 | break; | ||
1370 | case ALPS_PROTO_V3: | ||
1371 | case ALPS_PROTO_V4: | ||
1372 | input_set_abs_params(dev1, ABS_X, 0, 2000, 0, 0); | ||
1373 | input_set_abs_params(dev1, ABS_Y, 0, 1400, 0, 0); | ||
1374 | break; | ||
1375 | } | ||
1376 | |||
662 | input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); | 1377 | input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); |
663 | 1378 | ||
664 | if (model->flags & ALPS_WHEEL) { | 1379 | if (model->flags & ALPS_WHEEL) { |
@@ -701,7 +1416,7 @@ int alps_init(struct psmouse *psmouse) | |||
701 | psmouse->poll = alps_poll; | 1416 | psmouse->poll = alps_poll; |
702 | psmouse->disconnect = alps_disconnect; | 1417 | psmouse->disconnect = alps_disconnect; |
703 | psmouse->reconnect = alps_reconnect; | 1418 | psmouse->reconnect = alps_reconnect; |
704 | psmouse->pktsize = 6; | 1419 | psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; |
705 | 1420 | ||
706 | /* We are having trouble resyncing ALPS touchpads so disable it for now */ | 1421 | /* We are having trouble resyncing ALPS touchpads so disable it for now */ |
707 | psmouse->resync_time = 0; | 1422 | 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 @@ | |||
14 | 14 | ||
15 | #define ALPS_PROTO_V1 0 | 15 | #define ALPS_PROTO_V1 0 |
16 | #define ALPS_PROTO_V2 1 | 16 | #define ALPS_PROTO_V2 1 |
17 | #define ALPS_PROTO_V3 2 | ||
18 | #define ALPS_PROTO_V4 3 | ||
17 | 19 | ||
18 | struct alps_model_info { | 20 | struct alps_model_info { |
19 | unsigned char signature[3]; | 21 | unsigned char signature[3]; |
22 | unsigned char command_mode_resp; /* v3/v4 only */ | ||
20 | unsigned char proto_version; | 23 | unsigned char proto_version; |
21 | unsigned char byte0, mask0; | 24 | unsigned char byte0, mask0; |
22 | unsigned char flags; | 25 | unsigned char flags; |
23 | }; | 26 | }; |
24 | 27 | ||
28 | struct alps_nibble_commands { | ||
29 | int command; | ||
30 | unsigned char data; | ||
31 | }; | ||
32 | |||
25 | struct alps_data { | 33 | struct alps_data { |
26 | struct input_dev *dev2; /* Relative device */ | 34 | struct input_dev *dev2; /* Relative device */ |
27 | char phys[32]; /* Phys */ | 35 | char phys[32]; /* Phys */ |
28 | const struct alps_model_info *i;/* Info */ | 36 | const struct alps_model_info *i;/* Info */ |
37 | const struct alps_nibble_commands *nibble_commands; | ||
38 | int addr_command; /* Command to set register address */ | ||
29 | int prev_fin; /* Finger bit from previous packet */ | 39 | int prev_fin; /* Finger bit from previous packet */ |
40 | int multi_packet; /* Multi-packet data in progress */ | ||
41 | u8 quirks; | ||
30 | struct timer_list timer; | 42 | struct timer_list timer; |
31 | }; | 43 | }; |
32 | 44 | ||
45 | #define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */ | ||
46 | |||
33 | #ifdef CONFIG_MOUSE_PS2_ALPS | 47 | #ifdef CONFIG_MOUSE_PS2_ALPS |
34 | int alps_detect(struct psmouse *psmouse, bool set_properties); | 48 | int alps_detect(struct psmouse *psmouse, bool set_properties); |
35 | int alps_init(struct psmouse *psmouse); | 49 | 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 @@ | |||
8 | #define PSMOUSE_CMD_SETSTREAM 0x00ea | 8 | #define PSMOUSE_CMD_SETSTREAM 0x00ea |
9 | #define PSMOUSE_CMD_SETPOLL 0x00f0 | 9 | #define PSMOUSE_CMD_SETPOLL 0x00f0 |
10 | #define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ | 10 | #define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ |
11 | #define PSMOUSE_CMD_RESET_WRAP 0x00ec | ||
11 | #define PSMOUSE_CMD_GETID 0x02f2 | 12 | #define PSMOUSE_CMD_GETID 0x02f2 |
12 | #define PSMOUSE_CMD_SETRATE 0x10f3 | 13 | #define PSMOUSE_CMD_SETRATE 0x10f3 |
13 | #define PSMOUSE_CMD_ENABLE 0x00f4 | 14 | #define PSMOUSE_CMD_ENABLE 0x00f4 |