ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / input / mouse / logips2pp.c
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 <linux/input.h>
13 #include <linux/serio.h>
14 #include "psmouse.h"
15 #include "logips2pp.h"
16
17 /*
18  * Process a PS2++ or PS2T++ packet.
19  */
20
21 void ps2pp_process_packet(struct psmouse *psmouse)
22 {
23         struct input_dev *dev = &psmouse->dev;
24         unsigned char *packet = psmouse->packet;
25
26         if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
27
28                 switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
29
30                         case 0x0d: /* Mouse extra info */
31
32                                 input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
33                                         (int) (packet[2] & 8) - (int) (packet[2] & 7));
34                                 input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
35                                 input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
36
37                                 break;
38
39                         case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
40
41                                 input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
42                                 input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
43                                 input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
44                                 input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
45                                 input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
46
47                                 break;
48
49                         case 0x0f: /* TouchPad extra info */
50
51                                 input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
52                                         (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
53                                 packet[0] = packet[2] | 0x08;
54                                 break;
55
56 #ifdef DEBUG
57                         default:
58                                 printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
59                                         (packet[1] >> 4) | (packet[0] & 0x30));
60 #endif
61                 }
62
63                 packet[0] &= 0x0f;
64                 packet[1] = 0;
65                 packet[2] = 0;
66
67         }
68 }
69
70 /*
71  * ps2pp_cmd() sends a PS2++ command, sliced into two bit
72  * pieces through the SETRES command. This is needed to send extended
73  * commands to mice on notebooks that try to understand the PS/2 protocol
74  * Ugly.
75  */
76
77 static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
78 {
79         unsigned char d;
80         int i;
81
82         if (psmouse_command(psmouse,  NULL, PSMOUSE_CMD_SETSCALE11))
83                 return -1;
84
85         for (i = 6; i >= 0; i -= 2) {
86                 d = (command >> i) & 3;
87                 if(psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES))
88                         return -1;
89         }
90
91         if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL))
92                 return -1;
93
94         return 0;
95 }
96
97 /*
98  * SmartScroll / CruiseControl for some newer Logitech mice Defaults to
99  * enabled if we do nothing to it. Of course I put this in because I want it
100  * disabled :P
101  * 1 - enabled (if previously disabled, also default)
102  * 0/2 - disabled 
103  */
104
105 static void ps2pp_set_smartscroll(struct psmouse *psmouse)
106 {
107         unsigned char param[4];
108
109         ps2pp_cmd(psmouse, param, 0x32);
110
111         param[0] = 0;
112         psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
113         psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
114         psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
115
116         if (psmouse_smartscroll == 1) 
117                 param[0] = 1;
118         else
119         if (psmouse_smartscroll > 2)
120                 return;
121
122         /* else leave param[0] == 0 to disable */
123         psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
124 }
125
126 /*
127  * Support 800 dpi resolution _only_ if the user wants it (there are good
128  * reasons to not use it even if the mouse supports it, and of course there are
129  * also good reasons to use it, let the user decide).
130  */
131
132 void ps2pp_set_800dpi(struct psmouse *psmouse)
133 {
134         unsigned char param = 3;
135         psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
136         psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
137         psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
138         psmouse_command(psmouse, &param, PSMOUSE_CMD_SETRES);
139 }
140
141 /*
142  * Detect the exact model and features of a PS2++ or PS2T++ Logitech mouse or
143  * touchpad.
144  */
145
146 static int ps2pp_detect_model(struct psmouse *psmouse, unsigned char *param)
147 {
148         int i;
149         static struct _logips2_list {
150                 const int model;
151                 unsigned const int features;
152         } logips2pp_list [] = {
153                 { 12,   PS2PP_4BTN},
154                 { 13,   0 },
155                 { 40,   PS2PP_4BTN },
156                 { 41,   PS2PP_4BTN },
157                 { 42,   PS2PP_4BTN },
158                 { 43,   PS2PP_4BTN },
159                 { 50,   0 },
160                 { 51,   0 },
161                 { 52,   PS2PP_4BTN | PS2PP_WHEEL },
162                 { 53,   PS2PP_WHEEL },
163                 { 61,   PS2PP_WHEEL | PS2PP_MX },       /* MX700 */
164                 { 73,   PS2PP_4BTN },
165                 { 75,   PS2PP_WHEEL },
166                 { 76,   PS2PP_WHEEL },
167                 { 80,   PS2PP_4BTN | PS2PP_WHEEL },
168                 { 81,   PS2PP_WHEEL },
169                 { 83,   PS2PP_WHEEL },
170                 { 88,   PS2PP_WHEEL },
171                 { 96,   0 },
172                 { 97,   0 },
173                 { 100 , PS2PP_WHEEL | PS2PP_MX },       /* MX510 */
174                 { 112 , PS2PP_WHEEL | PS2PP_MX },       /* MX500 */
175                 { 114 , PS2PP_WHEEL | PS2PP_MX | PS2PP_MX310 }, /* MX310 */
176                 { }
177         };
178
179         psmouse->vendor = "Logitech";
180         psmouse->model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
181
182         if (param[1] < 3)
183                 clear_bit(BTN_MIDDLE, psmouse->dev.keybit);
184         if (param[1] < 2)
185                 clear_bit(BTN_RIGHT, psmouse->dev.keybit);
186
187         psmouse->type = PSMOUSE_PS2;
188
189         for (i = 0; logips2pp_list[i].model; i++){
190                 if (logips2pp_list[i].model == psmouse->model){
191                         psmouse->type = PSMOUSE_PS2PP;
192                         if (logips2pp_list[i].features & PS2PP_4BTN)
193                                 set_bit(BTN_SIDE, psmouse->dev.keybit);
194
195                         if (logips2pp_list[i].features & PS2PP_WHEEL){
196                                 set_bit(REL_WHEEL, psmouse->dev.relbit);
197                                 psmouse->name = "Wheel Mouse";
198                         }
199                         if (logips2pp_list[i].features & PS2PP_MX) {
200                                 set_bit(BTN_SIDE, psmouse->dev.keybit);
201                                 set_bit(BTN_EXTRA, psmouse->dev.keybit);
202                                 set_bit(BTN_TASK, psmouse->dev.keybit);
203                                 if (!(logips2pp_list[i].features & PS2PP_MX310)){
204                                         set_bit(BTN_BACK, psmouse->dev.keybit);
205                                         set_bit(BTN_FORWARD, psmouse->dev.keybit);
206                                 }
207                                 psmouse->name = "MX Mouse";
208                         }
209                         break;
210                 }
211         }
212 /*
213  * Do Logitech PS2++ / PS2T++ magic init.
214  */
215         if (psmouse->type == PSMOUSE_PS2PP) {
216
217                 if (psmouse->model == 97) { /* TouchPad 3 */
218
219                         set_bit(REL_WHEEL, psmouse->dev.relbit);
220                         set_bit(REL_HWHEEL, psmouse->dev.relbit);
221
222                         param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; /* Unprotect RAM */
223                         psmouse_command(psmouse, param, 0x30d1);
224                         param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; /* Enable features */
225                         psmouse_command(psmouse, param, 0x30d1);
226                         param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; /* Enable PS2++ */
227                         psmouse_command(psmouse, param, 0x30d1);
228
229                         param[0] = 0;
230                         if (!psmouse_command(psmouse, param, 0x13d1) &&
231                                 param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
232                                 psmouse->name = "TouchPad 3";
233                                 return PSMOUSE_PS2TPP;
234                         }
235
236                 } else {
237
238                         param[0] = param[1] = param[2] = 0;
239                         ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
240                         ps2pp_cmd(psmouse, param, 0xDB);
241
242                         if ((param[0] & 0x78) == 0x48 && (param[1] & 0xf3) == 0xc2 &&
243                                 (param[2] & 3) == ((param[1] >> 2) & 3)) {
244                                         ps2pp_set_smartscroll(psmouse);
245                                         return PSMOUSE_PS2PP;
246                         }
247                 }
248         }
249
250         return 0;
251 }
252
253 /*
254  * Logitech magic init.
255  */
256 int ps2pp_detect(struct psmouse *psmouse)
257 {
258         unsigned char param[4];
259
260         param[0] = 0;
261         psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
262         psmouse_command(psmouse,  NULL, PSMOUSE_CMD_SETSCALE11);
263         psmouse_command(psmouse,  NULL, PSMOUSE_CMD_SETSCALE11);
264         psmouse_command(psmouse,  NULL, PSMOUSE_CMD_SETSCALE11);
265         param[1] = 0;
266         psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
267
268         return param[1] != 0 ? ps2pp_detect_model(psmouse, param) : 0;
269 }
270