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