Rev 494 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
494 | giacomo | 1 | /* |
2 | * Logitech PS/2++ mouse driver |
||
3 | * |
||
4 | * Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz> |
||
5 | * Copyright (c) 2003 Eric Wong <eric@yhbt.net> |
||
6 | * |
||
7 | * This program is free software; you can redistribute it and/or modify it |
||
8 | * under the terms of the GNU General Public License version 2 as published by |
||
9 | * the Free Software Foundation. |
||
10 | */ |
||
11 | |||
12 | #include <linuxcomp.h> |
||
13 | |||
14 | #include <linux/input.h> |
||
15 | #include "psmouse.h" |
||
16 | #include "logips2pp.h" |
||
17 | |||
18 | #define DUBUG |
||
19 | |||
20 | /* |
||
21 | * Process a PS2++ or PS2T++ packet. |
||
22 | */ |
||
23 | |||
24 | void ps2pp_process_packet(struct psmouse *psmouse) |
||
25 | { |
||
26 | struct input_dev *dev = &psmouse->dev; |
||
27 | unsigned char *packet = psmouse->packet; |
||
28 | |||
29 | if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) { |
||
30 | |||
31 | switch ((packet[1] >> 4) | (packet[0] & 0x30)) { |
||
32 | |||
33 | case 0x0d: /* Mouse extra info */ |
||
34 | |||
35 | input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, |
||
36 | (int) (packet[2] & 8) - (int) (packet[2] & 7)); |
||
37 | input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); |
||
38 | input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); |
||
39 | |||
40 | break; |
||
41 | |||
42 | case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ |
||
43 | |||
44 | input_report_key(dev, BTN_SIDE, (packet[2]) & 1); |
||
45 | input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); |
||
46 | input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); |
||
47 | input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); |
||
48 | input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); |
||
49 | |||
50 | break; |
||
51 | |||
52 | case 0x0f: /* TouchPad extra info */ |
||
53 | |||
54 | input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, |
||
55 | (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); |
||
56 | packet[0] = packet[2] | 0x08; |
||
57 | break; |
||
58 | |||
59 | #ifdef DEBUG |
||
60 | default: |
||
61 | printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", |
||
62 | (packet[1] >> 4) | (packet[0] & 0x30)); |
||
63 | #endif |
||
64 | } |
||
65 | |||
66 | packet[0] &= 0x0f; |
||
67 | packet[1] = 0; |
||
68 | packet[2] = 0; |
||
69 | |||
70 | } |
||
71 | } |
||
72 | |||
73 | /* |
||
74 | * ps2pp_cmd() sends a PS2++ command, sliced into two bit |
||
75 | * pieces through the SETRES command. This is needed to send extended |
||
76 | * commands to mice on notebooks that try to understand the PS/2 protocol |
||
77 | * Ugly. |
||
78 | */ |
||
79 | |||
80 | static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) |
||
81 | { |
||
82 | unsigned char d; |
||
83 | int i; |
||
84 | |||
85 | if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) |
||
86 | return -1; |
||
87 | |||
88 | for (i = 6; i >= 0; i -= 2) { |
||
89 | d = (command >> i) & 3; |
||
90 | if(psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES)) |
||
91 | return -1; |
||
92 | } |
||
93 | |||
94 | if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL)) |
||
95 | return -1; |
||
96 | |||
97 | return 0; |
||
98 | } |
||
99 | |||
100 | /* |
||
101 | * SmartScroll / CruiseControl for some newer Logitech mice Defaults to |
||
102 | * enabled if we do nothing to it. Of course I put this in because I want it |
||
103 | * disabled :P |
||
104 | * 1 - enabled (if previously disabled, also default) |
||
105 | * 0/2 - disabled |
||
106 | */ |
||
107 | |||
108 | static void ps2pp_set_smartscroll(struct psmouse *psmouse) |
||
109 | { |
||
110 | unsigned char param[4]; |
||
111 | |||
112 | ps2pp_cmd(psmouse, param, 0x32); |
||
113 | |||
114 | param[0] = 0; |
||
115 | psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
||
116 | psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
||
117 | psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
||
118 | |||
119 | if (psmouse_smartscroll == 1) |
||
120 | param[0] = 1; |
||
121 | else |
||
122 | if (psmouse_smartscroll > 2) |
||
123 | return; |
||
124 | |||
125 | /* else leave param[0] == 0 to disable */ |
||
126 | psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); |
||
127 | } |
||
128 | |||
129 | /* |
||
130 | * Support 800 dpi resolution _only_ if the user wants it (there are good |
||
131 | * reasons to not use it even if the mouse supports it, and of course there are |
||
132 | * also good reasons to use it, let the user decide). |
||
133 | */ |
||
134 | |||
135 | void ps2pp_set_800dpi(struct psmouse *psmouse) |
||
136 | { |
||
137 | unsigned char param = 3; |
||
138 | psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
||
139 | psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
||
140 | psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); |
||
141 | psmouse_command(psmouse, ¶m, PSMOUSE_CMD_SETRES); |
||
142 | } |
||
143 | |||
144 | /* |
||
145 | * Detect the exact model and features of a PS2++ or PS2T++ Logitech mouse or |
||
146 | * touchpad. |
||
147 | */ |
||
148 | |||
149 | int ps2pp_detect_model(struct psmouse *psmouse, unsigned char *param) |
||
150 | { |
||
151 | int i; |
||
152 | static int logitech_4btn[] = { 12, 40, 41, 42, 43, 52, 73, 80, -1 }; |
||
153 | static int logitech_wheel[] = { 52, 53, 75, 76, 80, 81, 83, 88, 112, -1 }; |
||
154 | static int logitech_ps2pp[] = { 12, 13, 40, 41, 42, 43, 50, 51, 52, 53, 73, 75, |
||
155 | 76, 80, 81, 83, 88, 96, 97, 112, -1 }; |
||
156 | static int logitech_mx[] = { 112, -1 }; |
||
157 | |||
158 | psmouse->vendor = "Logitech"; |
||
159 | psmouse->model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); |
||
160 | |||
161 | if (param[1] < 3) |
||
162 | clear_bit(BTN_MIDDLE, psmouse->dev.keybit); |
||
163 | if (param[1] < 2) |
||
164 | clear_bit(BTN_RIGHT, psmouse->dev.keybit); |
||
165 | |||
166 | psmouse->type = PSMOUSE_PS2; |
||
167 | |||
168 | for (i = 0; logitech_ps2pp[i] != -1; i++) |
||
169 | if (logitech_ps2pp[i] == psmouse->model) |
||
170 | psmouse->type = PSMOUSE_PS2PP; |
||
171 | |||
172 | if (psmouse->type == PSMOUSE_PS2PP) { |
||
173 | |||
174 | for (i = 0; logitech_4btn[i] != -1; i++) |
||
175 | if (logitech_4btn[i] == psmouse->model) |
||
176 | set_bit(BTN_SIDE, psmouse->dev.keybit); |
||
177 | |||
178 | for (i = 0; logitech_wheel[i] != -1; i++) |
||
179 | if (logitech_wheel[i] == psmouse->model) { |
||
180 | set_bit(REL_WHEEL, psmouse->dev.relbit); |
||
181 | psmouse->name = "Wheel Mouse"; |
||
182 | } |
||
183 | |||
184 | for (i = 0; logitech_mx[i] != -1; i++) |
||
185 | if (logitech_mx[i] == psmouse->model) { |
||
186 | set_bit(BTN_SIDE, psmouse->dev.keybit); |
||
187 | set_bit(BTN_EXTRA, psmouse->dev.keybit); |
||
188 | set_bit(BTN_BACK, psmouse->dev.keybit); |
||
189 | set_bit(BTN_FORWARD, psmouse->dev.keybit); |
||
190 | set_bit(BTN_TASK, psmouse->dev.keybit); |
||
191 | psmouse->name = "MX Mouse"; |
||
192 | } |
||
193 | |||
194 | /* |
||
195 | * Do Logitech PS2++ / PS2T++ magic init. |
||
196 | */ |
||
197 | |||
198 | if (psmouse->model == 97) { /* TouchPad 3 */ |
||
199 | |||
200 | set_bit(REL_WHEEL, psmouse->dev.relbit); |
||
201 | set_bit(REL_HWHEEL, psmouse->dev.relbit); |
||
202 | |||
203 | param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; /* Unprotect RAM */ |
||
204 | psmouse_command(psmouse, param, 0x30d1); |
||
205 | param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; /* Enable features */ |
||
206 | psmouse_command(psmouse, param, 0x30d1); |
||
207 | param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; /* Enable PS2++ */ |
||
208 | psmouse_command(psmouse, param, 0x30d1); |
||
209 | |||
210 | param[0] = 0; |
||
211 | if (!psmouse_command(psmouse, param, 0x13d1) && |
||
212 | param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { |
||
213 | psmouse->name = "TouchPad 3"; |
||
214 | return PSMOUSE_PS2TPP; |
||
215 | } |
||
216 | |||
217 | } else { |
||
218 | |||
219 | param[0] = param[1] = param[2] = 0; |
||
220 | ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ |
||
221 | ps2pp_cmd(psmouse, param, 0xDB); |
||
222 | |||
223 | if ((param[0] & 0x78) == 0x48 && (param[1] & 0xf3) == 0xc2 && |
||
224 | (param[2] & 3) == ((param[1] >> 2) & 3)) { |
||
225 | ps2pp_set_smartscroll(psmouse); |
||
226 | return PSMOUSE_PS2PP; |
||
227 | } |
||
228 | } |
||
229 | } |
||
230 | |||
231 | return 0; |
||
232 | } |