This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / input / mouse / alps.c
1 /*
2  * ALPS touchpad PS/2 mouse driver
3  *
4  * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5  * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
6  * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
7  *
8  * ALPS detection, tap switching and status querying info is taken from
9  * tpconfig utility (by C. Scott Ananian and Bruce Kall).
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License version 2 as published by
13  * the Free Software Foundation.
14  */
15
16 #include <linux/input.h>
17 #include <linux/serio.h>
18 #include <linux/libps2.h>
19
20 #include "psmouse.h"
21 #include "alps.h"
22
23 #undef DEBUG
24 #ifdef DEBUG
25 #define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
26 #else
27 #define dbg(format, arg...) do {} while (0)
28 #endif
29
30 #define ALPS_MODEL_GLIDEPOINT   1
31 #define ALPS_MODEL_DUALPOINT    2
32
33 struct alps_model_info {
34         unsigned char signature[3];
35         unsigned char model;
36 } alps_model_data[] = {
37 /*      { { 0x33, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },        */
38         { { 0x53, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
39         { { 0x53, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
40         { { 0x63, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
41         { { 0x63, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
42         { { 0x73, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
43         { { 0x73, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
44         { { 0x63, 0x02, 0x28 }, ALPS_MODEL_GLIDEPOINT },
45 /*      { { 0x63, 0x02, 0x3c }, ALPS_MODEL_GLIDEPOINT },        */
46 /*      { { 0x63, 0x02, 0x50 }, ALPS_MODEL_GLIDEPOINT },        */
47         { { 0x63, 0x02, 0x64 }, ALPS_MODEL_GLIDEPOINT },
48         { { 0x20, 0x02, 0x0e }, ALPS_MODEL_DUALPOINT },
49         { { 0x22, 0x02, 0x0a }, ALPS_MODEL_DUALPOINT },
50         { { 0x22, 0x02, 0x14 }, ALPS_MODEL_DUALPOINT },
51         { { 0x63, 0x03, 0xc8 }, ALPS_MODEL_DUALPOINT },
52 };
53
54 /*
55  * ALPS abolute Mode
56  * byte 0:  1    1    1    1    1  mid0 rig0 lef0
57  * byte 1:  0   x6   x5   x4   x3   x2   x1   x0
58  * byte 2:  0   x10  x9   x8   x7  up1  fin  ges
59  * byte 3:  0   y9   y8   y7    1  mid1 rig1 lef1
60  * byte 4:  0   y6   y5   y4   y3   y2   y1   y0
61  * byte 5:  0   z6   z5   z4   z3   z2   z1   z0
62  *
63  * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad.
64  * We just 'or' them together for now.
65  *
66  * We used to send 'ges'tures as BTN_TOUCH but this made it impossible
67  * to disable tap events in the synaptics driver since the driver
68  * was unable to distinguish a gesture tap from an actual button click.
69  * A tap gesture now creates an emulated touch that the synaptics
70  * driver can interpret as a tap event, if MaxTapTime=0 and
71  * MaxTapMove=0 then the driver will ignore taps.
72  *
73  * The touchpad on an 'Acer Aspire' has 4 buttons:
74  *   left,right,up,down.
75  * This device always sets {mid,rig,lef}0 to 1 and
76  * reflects left,right,down,up in lef1,rig1,mid1,up1.
77  */
78
79 static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs)
80 {
81         unsigned char *packet = psmouse->packet;
82         struct input_dev *dev = &psmouse->dev;
83         int x, y, z;
84         int left = 0, right = 0, middle = 0;
85
86         input_regs(dev, regs);
87
88         if ((packet[0] & 0xc8) == 0x08) {   /* 3-byte PS/2 packet */
89                 x = packet[1];
90                 if (packet[0] & 0x10)
91                         x = x - 256;
92                 y = packet[2];
93                 if (packet[0] & 0x20)
94                         y = y - 256;
95                 left  = (packet[0]     ) & 1;
96                 right = (packet[0] >> 1) & 1;
97
98                 input_report_rel(dev, REL_X, x);
99                 input_report_rel(dev, REL_Y, -y);
100                 input_report_key(dev, BTN_A, left);
101                 input_report_key(dev, BTN_B, right);
102                 input_sync(dev);
103                 return;
104         }
105
106         x = (packet[1] & 0x7f) | ((packet[2] & 0x78)<<(7-3));
107         y = (packet[4] & 0x7f) | ((packet[3] & 0x70)<<(7-4));
108         z = packet[5];
109
110         if (z == 127) { /* DualPoint stick is relative, not absolute */
111                 if (x > 383)
112                         x = x - 768;
113                 if (y > 255)
114                         y = y - 512;
115                 left  = packet[3] & 1;
116                 right = (packet[3] >> 1) & 1;
117
118                 input_report_rel(dev, REL_X, x);
119                 input_report_rel(dev, REL_Y, -y);
120                 input_report_key(dev, BTN_LEFT, left);
121                 input_report_key(dev, BTN_RIGHT, right);
122                 input_sync(dev);
123                 return;
124         }
125
126         if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
127         if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
128
129         if (z > 0) {
130                 input_report_abs(dev, ABS_X, x);
131                 input_report_abs(dev, ABS_Y, y);
132         }
133         input_report_abs(dev, ABS_PRESSURE, z);
134         input_report_key(dev, BTN_TOOL_FINGER, z > 0);
135
136         left  |= (packet[2]     ) & 1;
137         left  |= (packet[3]     ) & 1;
138         right |= (packet[3] >> 1) & 1;
139         if (packet[0] == 0xff) {
140                 int back    = (packet[3] >> 2) & 1;
141                 int forward = (packet[2] >> 2) & 1;
142                 if (back && forward) {
143                         middle = 1;
144                         back = 0;
145                         forward = 0;
146                 }
147                 input_report_key(dev, BTN_BACK,    back);
148                 input_report_key(dev, BTN_FORWARD, forward);
149         } else {
150                 left   |= (packet[0]     ) & 1;
151                 right  |= (packet[0] >> 1) & 1;
152                 middle |= (packet[0] >> 2) & 1;
153                 middle |= (packet[3] >> 2) & 1;
154         }
155
156         input_report_key(dev, BTN_LEFT, left);
157         input_report_key(dev, BTN_RIGHT, right);
158         input_report_key(dev, BTN_MIDDLE, middle);
159
160         input_sync(dev);
161 }
162
163 static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
164 {
165         if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
166                 if (psmouse->pktcnt == 3) {
167                         alps_process_packet(psmouse, regs);
168                         return PSMOUSE_FULL_PACKET;
169                 }
170                 return PSMOUSE_GOOD_DATA;
171         }
172
173         /* ALPS absolute mode packets start with 0b11111mrl */
174         if ((psmouse->packet[0] & 0xf8) != 0xf8)
175                 return PSMOUSE_BAD_DATA;
176
177         /* Bytes 2 - 6 should have 0 in the highest bit */
178         if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
179             (psmouse->packet[psmouse->pktcnt-1] & 0x80))
180                 return PSMOUSE_BAD_DATA;
181
182         if (psmouse->pktcnt == 6) {
183                 alps_process_packet(psmouse, regs);
184                 return PSMOUSE_FULL_PACKET;
185         }
186
187         return PSMOUSE_GOOD_DATA;
188 }
189
190 int alps_get_model(struct psmouse *psmouse)
191 {
192         struct ps2dev *ps2dev = &psmouse->ps2dev;
193         unsigned char param[4];
194         int i;
195
196         /*
197          * First try "E6 report".
198          * ALPS should return 0x00,0x00,0x0a or 0x00,0x00,0x64
199          */
200         param[0] = 0;
201         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
202             ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
203             ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
204             ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11))
205                 return -1;
206
207         param[0] = param[1] = param[2] = 0xff;
208         if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
209                 return -1;
210
211         dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
212
213         if (param[0] != 0x00 || param[1] != 0x00 || (param[2] != 0x0a && param[2] != 0x64))
214                 return -1;
215
216         /* Now try "E7 report". ALPS should return 0x33 in byte 1 */
217         param[0] = 0;
218         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
219             ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
220             ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
221             ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21))
222                 return -1;
223
224         param[0] = param[1] = param[2] = 0xff;
225         if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
226                 return -1;
227
228         dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
229
230         for (i = 0; i < ARRAY_SIZE(alps_model_data); i++)
231                 if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature)))
232                         return alps_model_data[i].model;
233
234         return -1;
235 }
236
237 /*
238  * For DualPoint devices select the device that should respond to
239  * subsequent commands. It looks like glidepad is behind stickpointer,
240  * I'd thought it would be other way around...
241  */
242 static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
243 {
244         struct ps2dev *ps2dev = &psmouse->ps2dev;
245         unsigned char param[3];
246         int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
247
248         if (ps2_command(ps2dev, NULL, cmd) ||
249             ps2_command(ps2dev, NULL, cmd) ||
250             ps2_command(ps2dev, NULL, cmd) ||
251             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
252                 return -1;
253
254         /* we may get 3 more bytes, just ignore them */
255         ps2_command(ps2dev, param, 0x0300);
256
257         return 0;
258 }
259
260 static int alps_absolute_mode(struct psmouse *psmouse)
261 {
262         struct ps2dev *ps2dev = &psmouse->ps2dev;
263
264         /* Try ALPS magic knock - 4 disable before enable */
265         if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
266             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
267             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
268             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
269             ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
270                 return -1;
271
272         /*
273          * Switch mouse to poll (remote) mode so motion data will not
274          * get in our way
275          */
276         return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
277 }
278
279 static int alps_get_status(struct psmouse *psmouse, char *param)
280 {
281         struct ps2dev *ps2dev = &psmouse->ps2dev;
282
283         /* Get status: 0xF5 0xF5 0xF5 0xE9 */
284         if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
285             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
286             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
287             ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
288                 return -1;
289
290         dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
291
292         return 0;
293 }
294
295 /*
296  * Turn touchpad tapping on or off. The sequences are:
297  * 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
298  * 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
299  * My guess that 0xE9 (GetInfo) is here as a sync point.
300  * For models that also have stickpointer (DualPoints) its tapping
301  * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
302  * we don't fiddle with it.
303  */
304 static int alps_tap_mode(struct psmouse *psmouse, int enable)
305 {
306         struct ps2dev *ps2dev = &psmouse->ps2dev;
307         int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
308         unsigned char tap_arg = enable ? 0x0A : 0x00;
309         unsigned char param[4];
310
311         if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
312             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
313             ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
314             ps2_command(ps2dev, &tap_arg, cmd))
315                 return -1;
316
317         if (alps_get_status(psmouse, param))
318                 return -1;
319
320         return 0;
321 }
322
323 static int alps_reconnect(struct psmouse *psmouse)
324 {
325         int model;
326         unsigned char param[4];
327
328         if ((model = alps_get_model(psmouse)) < 0)
329                 return -1;
330
331         if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
332                 return -1;
333
334         if (alps_get_status(psmouse, param))
335                 return -1;
336
337         if (param[0] & 0x04)
338                 alps_tap_mode(psmouse, 0);
339
340         if (alps_absolute_mode(psmouse)) {
341                 printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
342                 return -1;
343         }
344
345         if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
346                 return -1;
347
348         return 0;
349 }
350
351 static void alps_disconnect(struct psmouse *psmouse)
352 {
353         psmouse_reset(psmouse);
354 }
355
356 int alps_init(struct psmouse *psmouse)
357 {
358         unsigned char param[4];
359         int model;
360
361         if ((model = alps_get_model(psmouse)) < 0)
362                 return -1;
363
364         printk(KERN_INFO "ALPS Touchpad (%s) detected\n",
365                 model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint");
366
367         if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
368                 return -1;
369
370         if (alps_get_status(psmouse, param)) {
371                 printk(KERN_ERR "alps.c: touchpad status report request failed\n");
372                 return -1;
373         }
374
375         if (param[0] & 0x04) {
376                 printk(KERN_INFO "  Disabling hardware tapping\n");
377                 if (alps_tap_mode(psmouse, 0))
378                         printk(KERN_WARNING "alps.c: Failed to disable hardware tapping\n");
379         }
380
381         if (alps_absolute_mode(psmouse)) {
382                 printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
383                 return -1;
384         }
385
386         if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
387                 return -1;
388
389         psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL);
390         psmouse->dev.relbit[LONG(REL_X)] |= BIT(REL_X);
391         psmouse->dev.relbit[LONG(REL_Y)] |= BIT(REL_Y);
392         psmouse->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A);
393         psmouse->dev.keybit[LONG(BTN_B)] |= BIT(BTN_B);
394
395         psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
396         input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0);
397         input_set_abs_params(&psmouse->dev, ABS_Y, 0, 1023, 0, 0);
398         input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0);
399
400         psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
401         psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER);
402         psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD);
403         psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK);
404
405         psmouse->protocol_handler = alps_process_byte;
406         psmouse->disconnect = alps_disconnect;
407         psmouse->reconnect = alps_reconnect;
408         psmouse->pktsize = 6;
409
410         return 0;
411 }
412
413 int alps_detect(struct psmouse *psmouse, int set_properties)
414 {
415         if (alps_get_model(psmouse) < 0)
416                 return -1;
417
418         if (set_properties) {
419                 psmouse->vendor = "ALPS";
420                 psmouse->name = "TouchPad";
421         }
422         return 0;
423 }
424