diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/mouse |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/Kconfig | 138 | ||||
-rw-r--r-- | drivers/input/mouse/Makefile | 18 | ||||
-rw-r--r-- | drivers/input/mouse/alps.c | 477 | ||||
-rw-r--r-- | drivers/input/mouse/alps.h | 32 | ||||
-rw-r--r-- | drivers/input/mouse/amimouse.c | 137 | ||||
-rw-r--r-- | drivers/input/mouse/hil_ptr.c | 414 | ||||
-rw-r--r-- | drivers/input/mouse/inport.c | 196 | ||||
-rw-r--r-- | drivers/input/mouse/logibm.c | 183 | ||||
-rw-r--r-- | drivers/input/mouse/logips2pp.c | 397 | ||||
-rw-r--r-- | drivers/input/mouse/logips2pp.h | 16 | ||||
-rw-r--r-- | drivers/input/mouse/maplemouse.c | 134 | ||||
-rw-r--r-- | drivers/input/mouse/pc110pad.c | 178 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 1011 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse.h | 106 | ||||
-rw-r--r-- | drivers/input/mouse/rpcmouse.c | 107 | ||||
-rw-r--r-- | drivers/input/mouse/sermouse.c | 370 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.c | 700 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.h | 110 | ||||
-rw-r--r-- | drivers/input/mouse/vsxxxaa.c | 591 |
19 files changed, 5315 insertions, 0 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig new file mode 100644 index 000000000000..537154dd7a87 --- /dev/null +++ b/drivers/input/mouse/Kconfig | |||
@@ -0,0 +1,138 @@ | |||
1 | # | ||
2 | # Mouse driver configuration | ||
3 | # | ||
4 | menuconfig INPUT_MOUSE | ||
5 | bool "Mouse" | ||
6 | default y | ||
7 | help | ||
8 | Say Y here, and a list of supported mice will be displayed. | ||
9 | This option doesn't affect the kernel. | ||
10 | |||
11 | If unsure, say Y. | ||
12 | |||
13 | if INPUT_MOUSE | ||
14 | |||
15 | config MOUSE_PS2 | ||
16 | tristate "PS/2 mouse" | ||
17 | default y | ||
18 | select SERIO | ||
19 | select SERIO_LIBPS2 | ||
20 | select SERIO_I8042 if PC | ||
21 | select SERIO_GSCPS2 if GSC | ||
22 | ---help--- | ||
23 | Say Y here if you have a PS/2 mouse connected to your system. This | ||
24 | includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 | ||
25 | mice with wheels and extra buttons, Microsoft, Logitech or Genius | ||
26 | compatible. | ||
27 | |||
28 | Synaptics TouchPad users might be interested in a specialized | ||
29 | XFree86 driver at: | ||
30 | <http://w1.894.telia.com/~u89404340/touchpad/index.html> | ||
31 | and a new version of GPM at: | ||
32 | <http://www.geocities.com/dt_or/gpm/gpm.html> | ||
33 | to take advantage of the advanced features of the touchpad. | ||
34 | |||
35 | If unsure, say Y. | ||
36 | |||
37 | To compile this driver as a module, choose M here: the | ||
38 | module will be called psmouse. | ||
39 | |||
40 | config MOUSE_SERIAL | ||
41 | tristate "Serial mouse" | ||
42 | select SERIO | ||
43 | ---help--- | ||
44 | Say Y here if you have a serial (RS-232, COM port) mouse connected | ||
45 | to your system. This includes Sun, MouseSystems, Microsoft, | ||
46 | Logitech and all other compatible serial mice. | ||
47 | |||
48 | If unsure, say N. | ||
49 | |||
50 | To compile this driver as a module, choose M here: the | ||
51 | module will be called sermouse. | ||
52 | |||
53 | config MOUSE_INPORT | ||
54 | tristate "InPort/MS/ATIXL busmouse" | ||
55 | depends on ISA | ||
56 | help | ||
57 | Say Y here if you have an InPort, Microsoft or ATI XL busmouse. | ||
58 | They are rather rare these days. | ||
59 | |||
60 | To compile this driver as a module, choose M here: the | ||
61 | module will be called inport. | ||
62 | |||
63 | config MOUSE_ATIXL | ||
64 | bool "ATI XL variant" | ||
65 | depends on MOUSE_INPORT | ||
66 | help | ||
67 | Say Y here if your mouse is of the ATI XL variety. | ||
68 | |||
69 | config MOUSE_LOGIBM | ||
70 | tristate "Logitech busmouse" | ||
71 | depends on ISA | ||
72 | help | ||
73 | Say Y here if you have a Logitech busmouse. | ||
74 | They are rather rare these days. | ||
75 | |||
76 | To compile this driver as a module, choose M here: the | ||
77 | module will be called logibm. | ||
78 | |||
79 | config MOUSE_PC110PAD | ||
80 | tristate "IBM PC110 touchpad" | ||
81 | depends on ISA | ||
82 | help | ||
83 | Say Y if you have the IBM PC-110 micro-notebook and want its | ||
84 | touchpad supported. | ||
85 | |||
86 | To compile this driver as a module, choose M here: the | ||
87 | module will be called pc110pad. | ||
88 | |||
89 | config MOUSE_MAPLE | ||
90 | tristate "Maple bus mouse" | ||
91 | depends on SH_DREAMCAST && MAPLE | ||
92 | help | ||
93 | Say Y if you have a DreamCast console and a mouse attached to | ||
94 | its Maple bus. | ||
95 | |||
96 | To compile this driver as a module, choose M here: the | ||
97 | module will be called maplemouse. | ||
98 | |||
99 | config MOUSE_AMIGA | ||
100 | tristate "Amiga mouse" | ||
101 | depends on AMIGA | ||
102 | help | ||
103 | Say Y here if you have an Amiga and want its native mouse | ||
104 | supported by the kernel. | ||
105 | |||
106 | To compile this driver as a module, choose M here: the | ||
107 | module will be called amimouse. | ||
108 | |||
109 | config MOUSE_RISCPC | ||
110 | tristate "Acorn RiscPC mouse" | ||
111 | depends on ARCH_ACORN | ||
112 | help | ||
113 | Say Y here if you have the Acorn RiscPC computer and want its | ||
114 | native mouse supported. | ||
115 | |||
116 | To compile this driver as a module, choose M here: the | ||
117 | module will be called rpcmouse. | ||
118 | |||
119 | config MOUSE_VSXXXAA | ||
120 | tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet" | ||
121 | select SERIO | ||
122 | help | ||
123 | Say Y (or M) if you want to use a DEC VSXXX-AA (hockey | ||
124 | puck) or a VSXXX-GA (rectangular) mouse. Theses mice are | ||
125 | typically used on DECstations or VAXstations, but can also | ||
126 | be used on any box capable of RS232 (with some adaptor | ||
127 | described in the source file). This driver also works with the | ||
128 | digitizer (VSXXX-AB) DEC produced. | ||
129 | |||
130 | config MOUSE_HIL | ||
131 | tristate "HIL pointers (mice etc)." | ||
132 | depends on GSC | ||
133 | select HP_SDC | ||
134 | select HIL_MLC | ||
135 | help | ||
136 | Say Y here to support HIL pointers. | ||
137 | |||
138 | endif | ||
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile new file mode 100644 index 000000000000..a7864195806a --- /dev/null +++ b/drivers/input/mouse/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | # | ||
2 | # Makefile for the mouse drivers. | ||
3 | # | ||
4 | |||
5 | # Each configuration option enables a list of files. | ||
6 | |||
7 | obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o | ||
8 | obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o | ||
9 | obj-$(CONFIG_MOUSE_INPORT) += inport.o | ||
10 | obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o | ||
11 | obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o | ||
12 | obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o | ||
13 | obj-$(CONFIG_MOUSE_PS2) += psmouse.o | ||
14 | obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o | ||
15 | obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o | ||
16 | obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o | ||
17 | |||
18 | psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o | ||
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c new file mode 100644 index 000000000000..1f85a9718c89 --- /dev/null +++ b/drivers/input/mouse/alps.c | |||
@@ -0,0 +1,477 @@ | |||
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 | * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> | ||
8 | * | ||
9 | * ALPS detection, tap switching and status querying info is taken from | ||
10 | * tpconfig utility (by C. Scott Ananian and Bruce Kall). | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License version 2 as published by | ||
14 | * the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/input.h> | ||
18 | #include <linux/serio.h> | ||
19 | #include <linux/libps2.h> | ||
20 | |||
21 | #include "psmouse.h" | ||
22 | #include "alps.h" | ||
23 | |||
24 | #undef DEBUG | ||
25 | #ifdef DEBUG | ||
26 | #define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg) | ||
27 | #else | ||
28 | #define dbg(format, arg...) do {} while (0) | ||
29 | #endif | ||
30 | |||
31 | #define ALPS_DUALPOINT 0x01 | ||
32 | #define ALPS_WHEEL 0x02 | ||
33 | #define ALPS_FW_BK 0x04 | ||
34 | #define ALPS_4BTN 0x08 | ||
35 | #define ALPS_OLDPROTO 0x10 | ||
36 | #define ALPS_PASS 0x20 | ||
37 | |||
38 | static struct alps_model_info alps_model_data[] = { | ||
39 | { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ | ||
40 | { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | ||
41 | { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | ||
42 | { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | ||
43 | { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | ||
44 | { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, 0 }, | ||
45 | { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ | ||
46 | { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK }, /* NEC Versa L320 */ | ||
47 | { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, | ||
48 | { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */ | ||
49 | { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | ||
50 | { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | ||
51 | { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ | ||
52 | { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, | ||
53 | { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * XXX - this entry is suspicious. First byte has zero lower nibble, | ||
58 | * which is what a normal mouse would report. Also, the value 0x0e | ||
59 | * isn't valid per PS/2 spec. | ||
60 | */ | ||
61 | |||
62 | /* | ||
63 | * ALPS abolute Mode - new format | ||
64 | * | ||
65 | * byte 0: 1 ? ? ? 1 ? ? ? | ||
66 | * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 | ||
67 | * byte 2: 0 x10 x9 x8 x7 ? fin ges | ||
68 | * byte 3: 0 y9 y8 y7 1 M R L | ||
69 | * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 | ||
70 | * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 | ||
71 | * | ||
72 | * ?'s can have different meanings on different models, | ||
73 | * such as wheel rotation, extra buttons, stick buttons | ||
74 | * on a dualpoint, etc. | ||
75 | */ | ||
76 | |||
77 | static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs) | ||
78 | { | ||
79 | struct alps_data *priv = psmouse->private; | ||
80 | unsigned char *packet = psmouse->packet; | ||
81 | struct input_dev *dev = &psmouse->dev; | ||
82 | struct input_dev *dev2 = &priv->dev2; | ||
83 | int x, y, z, ges, fin, left, right, middle; | ||
84 | |||
85 | input_regs(dev, regs); | ||
86 | |||
87 | if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */ | ||
88 | input_report_key(dev2, BTN_LEFT, packet[0] & 1); | ||
89 | input_report_key(dev2, BTN_RIGHT, packet[0] & 2); | ||
90 | input_report_key(dev2, BTN_MIDDLE, packet[0] & 4); | ||
91 | input_report_rel(dev2, REL_X, | ||
92 | packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0); | ||
93 | input_report_rel(dev2, REL_Y, | ||
94 | packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0); | ||
95 | input_sync(dev2); | ||
96 | return; | ||
97 | } | ||
98 | |||
99 | if (priv->i->flags & ALPS_OLDPROTO) { | ||
100 | left = packet[2] & 0x08; | ||
101 | right = packet[2] & 0x10; | ||
102 | middle = 0; | ||
103 | x = packet[1] | ((packet[0] & 0x07) << 7); | ||
104 | y = packet[4] | ((packet[3] & 0x07) << 7); | ||
105 | z = packet[5]; | ||
106 | } else { | ||
107 | left = packet[3] & 1; | ||
108 | right = packet[3] & 2; | ||
109 | middle = packet[3] & 4; | ||
110 | x = packet[1] | ((packet[2] & 0x78) << (7 - 3)); | ||
111 | y = packet[4] | ((packet[3] & 0x70) << (7 - 4)); | ||
112 | z = packet[5]; | ||
113 | } | ||
114 | |||
115 | ges = packet[2] & 1; | ||
116 | fin = packet[2] & 2; | ||
117 | |||
118 | input_report_key(dev, BTN_LEFT, left); | ||
119 | input_report_key(dev, BTN_RIGHT, right); | ||
120 | input_report_key(dev, BTN_MIDDLE, middle); | ||
121 | |||
122 | if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) { | ||
123 | input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); | ||
124 | input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); | ||
125 | input_sync(dev); | ||
126 | input_sync(dev2); | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | /* Convert hardware tap to a reasonable Z value */ | ||
131 | if (ges && !fin) z = 40; | ||
132 | |||
133 | /* | ||
134 | * A "tap and drag" operation is reported by the hardware as a transition | ||
135 | * from (!fin && ges) to (fin && ges). This should be translated to the | ||
136 | * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. | ||
137 | */ | ||
138 | if (ges && fin && !priv->prev_fin) { | ||
139 | input_report_abs(dev, ABS_X, x); | ||
140 | input_report_abs(dev, ABS_Y, y); | ||
141 | input_report_abs(dev, ABS_PRESSURE, 0); | ||
142 | input_report_key(dev, BTN_TOOL_FINGER, 0); | ||
143 | input_sync(dev); | ||
144 | } | ||
145 | priv->prev_fin = fin; | ||
146 | |||
147 | if (z > 30) input_report_key(dev, BTN_TOUCH, 1); | ||
148 | if (z < 25) input_report_key(dev, BTN_TOUCH, 0); | ||
149 | |||
150 | if (z > 0) { | ||
151 | input_report_abs(dev, ABS_X, x); | ||
152 | input_report_abs(dev, ABS_Y, y); | ||
153 | } | ||
154 | |||
155 | input_report_abs(dev, ABS_PRESSURE, z); | ||
156 | input_report_key(dev, BTN_TOOL_FINGER, z > 0); | ||
157 | |||
158 | |||
159 | if (priv->i->flags & ALPS_WHEEL) | ||
160 | input_report_rel(dev, REL_WHEEL, ((packet[0] >> 4) & 0x07) | ((packet[2] >> 2) & 0x08)); | ||
161 | |||
162 | if (priv->i->flags & ALPS_FW_BK) { | ||
163 | input_report_key(dev, BTN_FORWARD, packet[0] & 0x10); | ||
164 | input_report_key(dev, BTN_BACK, packet[2] & 0x04); | ||
165 | } | ||
166 | |||
167 | input_sync(dev); | ||
168 | } | ||
169 | |||
170 | static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *regs) | ||
171 | { | ||
172 | struct alps_data *priv = psmouse->private; | ||
173 | |||
174 | if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ | ||
175 | if (psmouse->pktcnt == 3) { | ||
176 | alps_process_packet(psmouse, regs); | ||
177 | return PSMOUSE_FULL_PACKET; | ||
178 | } | ||
179 | return PSMOUSE_GOOD_DATA; | ||
180 | } | ||
181 | |||
182 | if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0) | ||
183 | return PSMOUSE_BAD_DATA; | ||
184 | |||
185 | /* Bytes 2 - 6 should have 0 in the highest bit */ | ||
186 | if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 && | ||
187 | (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) | ||
188 | return PSMOUSE_BAD_DATA; | ||
189 | |||
190 | if (psmouse->pktcnt == 6) { | ||
191 | alps_process_packet(psmouse, regs); | ||
192 | return PSMOUSE_FULL_PACKET; | ||
193 | } | ||
194 | |||
195 | return PSMOUSE_GOOD_DATA; | ||
196 | } | ||
197 | |||
198 | static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) | ||
199 | { | ||
200 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
201 | unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; | ||
202 | unsigned char param[4]; | ||
203 | int i; | ||
204 | |||
205 | /* | ||
206 | * First try "E6 report". | ||
207 | * ALPS should return 0,0,10 or 0,0,100 | ||
208 | */ | ||
209 | param[0] = 0; | ||
210 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || | ||
211 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
212 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
213 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) | ||
214 | return NULL; | ||
215 | |||
216 | param[0] = param[1] = param[2] = 0xff; | ||
217 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
218 | return NULL; | ||
219 | |||
220 | dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); | ||
221 | |||
222 | if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) | ||
223 | return NULL; | ||
224 | |||
225 | /* | ||
226 | * Now try "E7 report". Allowed responses are in | ||
227 | * alps_model_data[].signature | ||
228 | */ | ||
229 | param[0] = 0; | ||
230 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || | ||
231 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
232 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
233 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) | ||
234 | return NULL; | ||
235 | |||
236 | param[0] = param[1] = param[2] = 0xff; | ||
237 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
238 | return NULL; | ||
239 | |||
240 | dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); | ||
241 | |||
242 | for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++); | ||
243 | *version = (param[0] << 8) | (param[1] << 4) | i; | ||
244 | |||
245 | for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) | ||
246 | if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature))) | ||
247 | return alps_model_data + i; | ||
248 | |||
249 | return NULL; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * For DualPoint devices select the device that should respond to | ||
254 | * subsequent commands. It looks like glidepad is behind stickpointer, | ||
255 | * I'd thought it would be other way around... | ||
256 | */ | ||
257 | static int alps_passthrough_mode(struct psmouse *psmouse, int enable) | ||
258 | { | ||
259 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
260 | unsigned char param[3]; | ||
261 | int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; | ||
262 | |||
263 | if (ps2_command(ps2dev, NULL, cmd) || | ||
264 | ps2_command(ps2dev, NULL, cmd) || | ||
265 | ps2_command(ps2dev, NULL, cmd) || | ||
266 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) | ||
267 | return -1; | ||
268 | |||
269 | /* we may get 3 more bytes, just ignore them */ | ||
270 | ps2_command(ps2dev, param, 0x0300); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int alps_absolute_mode(struct psmouse *psmouse) | ||
276 | { | ||
277 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
278 | |||
279 | /* Try ALPS magic knock - 4 disable before enable */ | ||
280 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
281 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
282 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
283 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
284 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) | ||
285 | return -1; | ||
286 | |||
287 | /* | ||
288 | * Switch mouse to poll (remote) mode so motion data will not | ||
289 | * get in our way | ||
290 | */ | ||
291 | return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); | ||
292 | } | ||
293 | |||
294 | static int alps_get_status(struct psmouse *psmouse, char *param) | ||
295 | { | ||
296 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
297 | |||
298 | /* Get status: 0xF5 0xF5 0xF5 0xE9 */ | ||
299 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
300 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
301 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
302 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
303 | return -1; | ||
304 | |||
305 | dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Turn touchpad tapping on or off. The sequences are: | ||
312 | * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, | ||
313 | * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. | ||
314 | * My guess that 0xE9 (GetInfo) is here as a sync point. | ||
315 | * For models that also have stickpointer (DualPoints) its tapping | ||
316 | * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but | ||
317 | * we don't fiddle with it. | ||
318 | */ | ||
319 | static int alps_tap_mode(struct psmouse *psmouse, int enable) | ||
320 | { | ||
321 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
322 | int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES; | ||
323 | unsigned char tap_arg = enable ? 0x0A : 0x00; | ||
324 | unsigned char param[4]; | ||
325 | |||
326 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) || | ||
327 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
328 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
329 | ps2_command(ps2dev, &tap_arg, cmd)) | ||
330 | return -1; | ||
331 | |||
332 | if (alps_get_status(psmouse, param)) | ||
333 | return -1; | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int alps_reconnect(struct psmouse *psmouse) | ||
339 | { | ||
340 | struct alps_data *priv = psmouse->private; | ||
341 | unsigned char param[4]; | ||
342 | int version; | ||
343 | |||
344 | if (!(priv->i = alps_get_model(psmouse, &version))) | ||
345 | return -1; | ||
346 | |||
347 | if (priv->i->flags & ALPS_PASS && alps_passthrough_mode(psmouse, 1)) | ||
348 | return -1; | ||
349 | |||
350 | if (alps_get_status(psmouse, param)) | ||
351 | return -1; | ||
352 | |||
353 | if (param[0] & 0x04) | ||
354 | alps_tap_mode(psmouse, 1); | ||
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 | if (priv->i->flags == ALPS_PASS && alps_passthrough_mode(psmouse, 0)) | ||
362 | return -1; | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static void alps_disconnect(struct psmouse *psmouse) | ||
368 | { | ||
369 | struct alps_data *priv = psmouse->private; | ||
370 | psmouse_reset(psmouse); | ||
371 | input_unregister_device(&priv->dev2); | ||
372 | kfree(priv); | ||
373 | } | ||
374 | |||
375 | int alps_init(struct psmouse *psmouse) | ||
376 | { | ||
377 | struct alps_data *priv; | ||
378 | unsigned char param[4]; | ||
379 | int version; | ||
380 | |||
381 | psmouse->private = priv = kmalloc(sizeof(struct alps_data), GFP_KERNEL); | ||
382 | if (!priv) | ||
383 | goto init_fail; | ||
384 | memset(priv, 0, sizeof(struct alps_data)); | ||
385 | |||
386 | if (!(priv->i = alps_get_model(psmouse, &version))) | ||
387 | goto init_fail; | ||
388 | |||
389 | if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1)) | ||
390 | goto init_fail; | ||
391 | |||
392 | if (alps_get_status(psmouse, param)) { | ||
393 | printk(KERN_ERR "alps.c: touchpad status report request failed\n"); | ||
394 | goto init_fail; | ||
395 | } | ||
396 | |||
397 | if (param[0] & 0x04) { | ||
398 | printk(KERN_INFO " Enabling hardware tapping\n"); | ||
399 | if (alps_tap_mode(psmouse, 1)) | ||
400 | printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); | ||
401 | } | ||
402 | |||
403 | if (alps_absolute_mode(psmouse)) { | ||
404 | printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); | ||
405 | goto init_fail; | ||
406 | } | ||
407 | |||
408 | if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0)) | ||
409 | goto init_fail; | ||
410 | |||
411 | psmouse->dev.evbit[LONG(EV_KEY)] |= BIT(EV_KEY); | ||
412 | psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH); | ||
413 | psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER); | ||
414 | psmouse->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); | ||
415 | |||
416 | psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS); | ||
417 | input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0); | ||
418 | input_set_abs_params(&psmouse->dev, ABS_Y, 0, 767, 0, 0); | ||
419 | input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0); | ||
420 | |||
421 | if (priv->i->flags & ALPS_WHEEL) { | ||
422 | psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL); | ||
423 | psmouse->dev.relbit[LONG(REL_WHEEL)] |= BIT(REL_WHEEL); | ||
424 | } | ||
425 | |||
426 | if (priv->i->flags & ALPS_FW_BK) { | ||
427 | psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD); | ||
428 | psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK); | ||
429 | } | ||
430 | |||
431 | sprintf(priv->phys, "%s/input1", psmouse->ps2dev.serio->phys); | ||
432 | priv->dev2.phys = priv->phys; | ||
433 | priv->dev2.name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; | ||
434 | priv->dev2.id.bustype = BUS_I8042; | ||
435 | priv->dev2.id.vendor = 0x0002; | ||
436 | priv->dev2.id.product = PSMOUSE_ALPS; | ||
437 | priv->dev2.id.version = 0x0000; | ||
438 | |||
439 | priv->dev2.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
440 | priv->dev2.relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y); | ||
441 | priv->dev2.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); | ||
442 | |||
443 | input_register_device(&priv->dev2); | ||
444 | |||
445 | printk(KERN_INFO "input: %s on %s\n", priv->dev2.name, psmouse->ps2dev.serio->phys); | ||
446 | |||
447 | psmouse->protocol_handler = alps_process_byte; | ||
448 | psmouse->disconnect = alps_disconnect; | ||
449 | psmouse->reconnect = alps_reconnect; | ||
450 | psmouse->pktsize = 6; | ||
451 | |||
452 | return 0; | ||
453 | |||
454 | init_fail: | ||
455 | kfree(priv); | ||
456 | return -1; | ||
457 | } | ||
458 | |||
459 | int alps_detect(struct psmouse *psmouse, int set_properties) | ||
460 | { | ||
461 | int version; | ||
462 | struct alps_model_info *model; | ||
463 | |||
464 | if (!(model = alps_get_model(psmouse, &version))) | ||
465 | return -1; | ||
466 | |||
467 | if (set_properties) { | ||
468 | psmouse->vendor = "ALPS"; | ||
469 | if (model->flags & ALPS_DUALPOINT) | ||
470 | psmouse->name = "DualPoint TouchPad"; | ||
471 | else | ||
472 | psmouse->name = "GlidePoint"; | ||
473 | psmouse->model = version; | ||
474 | } | ||
475 | return 0; | ||
476 | } | ||
477 | |||
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h new file mode 100644 index 000000000000..aba103dd65b7 --- /dev/null +++ b/drivers/input/mouse/alps.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * ALPS touchpad PS/2 mouse driver | ||
3 | * | ||
4 | * Copyright (c) 2003 Peter Osterlund <petero2@telia.com> | ||
5 | * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> | ||
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 | #ifndef _ALPS_H | ||
13 | #define _ALPS_H | ||
14 | |||
15 | int alps_detect(struct psmouse *psmouse, int set_properties); | ||
16 | int alps_init(struct psmouse *psmouse); | ||
17 | |||
18 | struct alps_model_info { | ||
19 | unsigned char signature[3]; | ||
20 | unsigned char byte0, mask0; | ||
21 | unsigned char flags; | ||
22 | }; | ||
23 | |||
24 | struct alps_data { | ||
25 | struct input_dev dev2; /* Relative device */ | ||
26 | char name[32]; /* Name */ | ||
27 | char phys[32]; /* Phys */ | ||
28 | struct alps_model_info *i; /* Info */ | ||
29 | int prev_fin; /* Finger bit from previous packet */ | ||
30 | }; | ||
31 | |||
32 | #endif | ||
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c new file mode 100644 index 000000000000..7baa09cca7c5 --- /dev/null +++ b/drivers/input/mouse/amimouse.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Amiga mouse driver for Linux/m68k | ||
3 | * | ||
4 | * Copyright (c) 2000-2002 Vojtech Pavlik | ||
5 | * | ||
6 | * Based on the work of: | ||
7 | * Michael Rausch James Banks | ||
8 | * Matther Dillon David Giller | ||
9 | * Nathan Laredo Linus Torvalds | ||
10 | * Johan Myreen Jes Sorensen | ||
11 | * Russell King | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * This program is free software; you can redistribute it and/or modify it | ||
16 | * under the terms of the GNU General Public License version 2 as published by | ||
17 | * the Free Software Foundation | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/input.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | |||
25 | #include <asm/irq.h> | ||
26 | #include <asm/setup.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | #include <asm/amigahw.h> | ||
30 | #include <asm/amigaints.h> | ||
31 | |||
32 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
33 | MODULE_DESCRIPTION("Amiga mouse driver"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | static int amimouse_used = 0; | ||
37 | static int amimouse_lastx, amimouse_lasty; | ||
38 | static struct input_dev amimouse_dev; | ||
39 | |||
40 | static char *amimouse_name = "Amiga mouse"; | ||
41 | static char *amimouse_phys = "amimouse/input0"; | ||
42 | |||
43 | static irqreturn_t amimouse_interrupt(int irq, void *dummy, struct pt_regs *fp) | ||
44 | { | ||
45 | unsigned short joy0dat, potgor; | ||
46 | int nx, ny, dx, dy; | ||
47 | |||
48 | joy0dat = custom.joy0dat; | ||
49 | |||
50 | nx = joy0dat & 0xff; | ||
51 | ny = joy0dat >> 8; | ||
52 | |||
53 | dx = nx - amimouse_lastx; | ||
54 | dy = ny - amimouse_lasty; | ||
55 | |||
56 | if (dx < -127) dx = (256 + nx) - amimouse_lastx; | ||
57 | if (dx > 127) dx = (nx - 256) - amimouse_lastx; | ||
58 | if (dy < -127) dy = (256 + ny) - amimouse_lasty; | ||
59 | if (dy > 127) dy = (ny - 256) - amimouse_lasty; | ||
60 | |||
61 | amimouse_lastx = nx; | ||
62 | amimouse_lasty = ny; | ||
63 | |||
64 | potgor = custom.potgor; | ||
65 | |||
66 | input_regs(&amimouse_dev, fp); | ||
67 | |||
68 | input_report_rel(&amimouse_dev, REL_X, dx); | ||
69 | input_report_rel(&amimouse_dev, REL_Y, dy); | ||
70 | |||
71 | input_report_key(&amimouse_dev, BTN_LEFT, ciaa.pra & 0x40); | ||
72 | input_report_key(&amimouse_dev, BTN_MIDDLE, potgor & 0x0100); | ||
73 | input_report_key(&amimouse_dev, BTN_RIGHT, potgor & 0x0400); | ||
74 | |||
75 | input_sync(&amimouse_dev); | ||
76 | |||
77 | return IRQ_HANDLED; | ||
78 | } | ||
79 | |||
80 | static int amimouse_open(struct input_dev *dev) | ||
81 | { | ||
82 | unsigned short joy0dat; | ||
83 | |||
84 | if (amimouse_used++) | ||
85 | return 0; | ||
86 | |||
87 | joy0dat = custom.joy0dat; | ||
88 | |||
89 | amimouse_lastx = joy0dat & 0xff; | ||
90 | amimouse_lasty = joy0dat >> 8; | ||
91 | |||
92 | if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) { | ||
93 | amimouse_used--; | ||
94 | printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB); | ||
95 | return -EBUSY; | ||
96 | } | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static void amimouse_close(struct input_dev *dev) | ||
102 | { | ||
103 | if (!--amimouse_used) | ||
104 | free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt); | ||
105 | } | ||
106 | |||
107 | static int __init amimouse_init(void) | ||
108 | { | ||
109 | if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE)) | ||
110 | return -ENODEV; | ||
111 | |||
112 | amimouse_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
113 | amimouse_dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
114 | amimouse_dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); | ||
115 | amimouse_dev.open = amimouse_open; | ||
116 | amimouse_dev.close = amimouse_close; | ||
117 | |||
118 | amimouse_dev.name = amimouse_name; | ||
119 | amimouse_dev.phys = amimouse_phys; | ||
120 | amimouse_dev.id.bustype = BUS_AMIGA; | ||
121 | amimouse_dev.id.vendor = 0x0001; | ||
122 | amimouse_dev.id.product = 0x0002; | ||
123 | amimouse_dev.id.version = 0x0100; | ||
124 | |||
125 | input_register_device(&amimouse_dev); | ||
126 | |||
127 | printk(KERN_INFO "input: %s at joy0dat\n", amimouse_name); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void __exit amimouse_exit(void) | ||
132 | { | ||
133 | input_unregister_device(&amimouse_dev); | ||
134 | } | ||
135 | |||
136 | module_init(amimouse_init); | ||
137 | module_exit(amimouse_exit); | ||
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c new file mode 100644 index 000000000000..bc22849c6c79 --- /dev/null +++ b/drivers/input/mouse/hil_ptr.c | |||
@@ -0,0 +1,414 @@ | |||
1 | /* | ||
2 | * Generic linux-input device driver for axis-bearing devices | ||
3 | * | ||
4 | * Copyright (c) 2001 Brian S. Julin | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions, and the following disclaimer, | ||
12 | * without modification. | ||
13 | * 2. The name of the author may not be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * Alternatively, this software may be distributed under the terms of the | ||
17 | * GNU General Public License ("GPL"). | ||
18 | * | ||
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | ||
23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
28 | * | ||
29 | * References: | ||
30 | * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include <linux/hil.h> | ||
35 | #include <linux/input.h> | ||
36 | #include <linux/serio.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/pci_ids.h> | ||
42 | |||
43 | #define PREFIX "HIL PTR: " | ||
44 | #define HIL_GENERIC_NAME "HIL pointer device" | ||
45 | |||
46 | MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); | ||
47 | MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver"); | ||
48 | MODULE_LICENSE("Dual BSD/GPL"); | ||
49 | |||
50 | |||
51 | #define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */ | ||
52 | #undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */ | ||
53 | |||
54 | |||
55 | #define HIL_PTR_MAX_LENGTH 16 | ||
56 | |||
57 | struct hil_ptr { | ||
58 | struct input_dev dev; | ||
59 | struct serio *serio; | ||
60 | |||
61 | /* Input buffer and index for packets from HIL bus. */ | ||
62 | hil_packet data[HIL_PTR_MAX_LENGTH]; | ||
63 | int idx4; /* four counts per packet */ | ||
64 | |||
65 | /* Raw device info records from HIL bus, see hil.h for fields. */ | ||
66 | char idd[HIL_PTR_MAX_LENGTH]; /* DID byte and IDD record */ | ||
67 | char rsc[HIL_PTR_MAX_LENGTH]; /* RSC record */ | ||
68 | char exd[HIL_PTR_MAX_LENGTH]; /* EXD record */ | ||
69 | char rnm[HIL_PTR_MAX_LENGTH + 1]; /* RNM record + NULL term. */ | ||
70 | |||
71 | /* Extra device details not contained in struct input_dev. */ | ||
72 | unsigned int nbtn, naxes; | ||
73 | unsigned int btnmap[7]; | ||
74 | |||
75 | /* Something to sleep around with. */ | ||
76 | struct semaphore sem; | ||
77 | }; | ||
78 | |||
79 | /* Process a complete packet after transfer from the HIL */ | ||
80 | static void hil_ptr_process_record(struct hil_ptr *ptr) | ||
81 | { | ||
82 | struct input_dev *dev = &ptr->dev; | ||
83 | hil_packet *data = ptr->data; | ||
84 | hil_packet p; | ||
85 | int idx, i, cnt, laxis; | ||
86 | int ax16, absdev; | ||
87 | |||
88 | idx = ptr->idx4/4; | ||
89 | p = data[idx - 1]; | ||
90 | |||
91 | if ((p & ~HIL_CMDCT_POL) == | ||
92 | (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report; | ||
93 | if ((p & ~HIL_CMDCT_RPL) == | ||
94 | (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report; | ||
95 | |||
96 | /* Not a poll response. See if we are loading config records. */ | ||
97 | switch (p & HIL_PKT_DATA_MASK) { | ||
98 | case HIL_CMD_IDD: | ||
99 | for (i = 0; i < idx; i++) | ||
100 | ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | ||
101 | for (; i < HIL_PTR_MAX_LENGTH; i++) | ||
102 | ptr->idd[i] = 0; | ||
103 | break; | ||
104 | case HIL_CMD_RSC: | ||
105 | for (i = 0; i < idx; i++) | ||
106 | ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | ||
107 | for (; i < HIL_PTR_MAX_LENGTH; i++) | ||
108 | ptr->rsc[i] = 0; | ||
109 | break; | ||
110 | case HIL_CMD_EXD: | ||
111 | for (i = 0; i < idx; i++) | ||
112 | ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | ||
113 | for (; i < HIL_PTR_MAX_LENGTH; i++) | ||
114 | ptr->exd[i] = 0; | ||
115 | break; | ||
116 | case HIL_CMD_RNM: | ||
117 | for (i = 0; i < idx; i++) | ||
118 | ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | ||
119 | for (; i < HIL_PTR_MAX_LENGTH + 1; i++) | ||
120 | ptr->rnm[i] = '\0'; | ||
121 | break; | ||
122 | default: | ||
123 | /* These occur when device isn't present */ | ||
124 | if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break; | ||
125 | /* Anything else we'd like to know about. */ | ||
126 | printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); | ||
127 | break; | ||
128 | } | ||
129 | goto out; | ||
130 | |||
131 | report: | ||
132 | if ((p & HIL_CMDCT_POL) != idx - 1) { | ||
133 | printk(KERN_WARNING PREFIX "Malformed poll packet %x (idx = %i)\n", p, idx); | ||
134 | goto out; | ||
135 | } | ||
136 | |||
137 | i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0; | ||
138 | laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK; | ||
139 | laxis += i; | ||
140 | |||
141 | ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */ | ||
142 | absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; | ||
143 | |||
144 | for (cnt = 1; i < laxis; i++) { | ||
145 | unsigned int lo,hi,val; | ||
146 | lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK; | ||
147 | hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0; | ||
148 | if (absdev) { | ||
149 | val = lo + (hi<<8); | ||
150 | #ifdef TABLET_AUTOADJUST | ||
151 | if (val < ptr->dev.absmin[ABS_X + i]) | ||
152 | ptr->dev.absmin[ABS_X + i] = val; | ||
153 | if (val > ptr->dev.absmax[ABS_X + i]) | ||
154 | ptr->dev.absmax[ABS_X + i] = val; | ||
155 | #endif | ||
156 | if (i%3) val = ptr->dev.absmax[ABS_X + i] - val; | ||
157 | input_report_abs(dev, ABS_X + i, val); | ||
158 | } else { | ||
159 | val = (int) (((int8_t)lo) | ((int8_t)hi<<8)); | ||
160 | if (i%3) val *= -1; | ||
161 | input_report_rel(dev, REL_X + i, val); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | while (cnt < idx - 1) { | ||
166 | unsigned int btn; | ||
167 | int up; | ||
168 | btn = ptr->data[cnt++]; | ||
169 | up = btn & 1; | ||
170 | btn &= 0xfe; | ||
171 | if (btn == 0x8e) { | ||
172 | continue; /* TODO: proximity == touch? */ | ||
173 | } | ||
174 | else if ((btn > 0x8c) || (btn < 0x80)) continue; | ||
175 | btn = (btn - 0x80) >> 1; | ||
176 | btn = ptr->btnmap[btn]; | ||
177 | input_report_key(dev, btn, !up); | ||
178 | } | ||
179 | input_sync(dev); | ||
180 | out: | ||
181 | ptr->idx4 = 0; | ||
182 | up(&ptr->sem); | ||
183 | } | ||
184 | |||
185 | static void hil_ptr_process_err(struct hil_ptr *ptr) { | ||
186 | printk(KERN_WARNING PREFIX "errored HIL packet\n"); | ||
187 | ptr->idx4 = 0; | ||
188 | up(&ptr->sem); | ||
189 | return; | ||
190 | } | ||
191 | |||
192 | static irqreturn_t hil_ptr_interrupt(struct serio *serio, | ||
193 | unsigned char data, unsigned int flags, struct pt_regs *regs) | ||
194 | { | ||
195 | struct hil_ptr *ptr; | ||
196 | hil_packet packet; | ||
197 | int idx; | ||
198 | |||
199 | ptr = (struct hil_ptr *)serio->private; | ||
200 | if (ptr == NULL) { | ||
201 | BUG(); | ||
202 | return IRQ_HANDLED; | ||
203 | } | ||
204 | |||
205 | if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) { | ||
206 | hil_ptr_process_err(ptr); | ||
207 | return IRQ_HANDLED; | ||
208 | } | ||
209 | idx = ptr->idx4/4; | ||
210 | if (!(ptr->idx4 % 4)) ptr->data[idx] = 0; | ||
211 | packet = ptr->data[idx]; | ||
212 | packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8); | ||
213 | ptr->data[idx] = packet; | ||
214 | |||
215 | /* Records of N 4-byte hil_packets must terminate with a command. */ | ||
216 | if ((++(ptr->idx4)) % 4) return IRQ_HANDLED; | ||
217 | if ((packet & 0xffff0000) != HIL_ERR_INT) { | ||
218 | hil_ptr_process_err(ptr); | ||
219 | return IRQ_HANDLED; | ||
220 | } | ||
221 | if (packet & HIL_PKT_CMD) | ||
222 | hil_ptr_process_record(ptr); | ||
223 | return IRQ_HANDLED; | ||
224 | } | ||
225 | |||
226 | static void hil_ptr_disconnect(struct serio *serio) | ||
227 | { | ||
228 | struct hil_ptr *ptr; | ||
229 | |||
230 | ptr = (struct hil_ptr *)serio->private; | ||
231 | if (ptr == NULL) { | ||
232 | BUG(); | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | input_unregister_device(&ptr->dev); | ||
237 | serio_close(serio); | ||
238 | kfree(ptr); | ||
239 | } | ||
240 | |||
241 | static void hil_ptr_connect(struct serio *serio, struct serio_driver *driver) | ||
242 | { | ||
243 | struct hil_ptr *ptr; | ||
244 | char *txt; | ||
245 | unsigned int i, naxsets, btntype; | ||
246 | uint8_t did, *idd; | ||
247 | |||
248 | if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return; | ||
249 | |||
250 | if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return; | ||
251 | memset(ptr, 0, sizeof(struct hil_ptr)); | ||
252 | |||
253 | if (serio_open(serio, driver)) goto bail0; | ||
254 | |||
255 | serio->private = ptr; | ||
256 | ptr->serio = serio; | ||
257 | ptr->dev.private = ptr; | ||
258 | |||
259 | init_MUTEX_LOCKED(&(ptr->sem)); | ||
260 | |||
261 | /* Get device info. MLC driver supplies devid/status/etc. */ | ||
262 | serio->write(serio, 0); | ||
263 | serio->write(serio, 0); | ||
264 | serio->write(serio, HIL_PKT_CMD >> 8); | ||
265 | serio->write(serio, HIL_CMD_IDD); | ||
266 | down(&(ptr->sem)); | ||
267 | |||
268 | serio->write(serio, 0); | ||
269 | serio->write(serio, 0); | ||
270 | serio->write(serio, HIL_PKT_CMD >> 8); | ||
271 | serio->write(serio, HIL_CMD_RSC); | ||
272 | down(&(ptr->sem)); | ||
273 | |||
274 | serio->write(serio, 0); | ||
275 | serio->write(serio, 0); | ||
276 | serio->write(serio, HIL_PKT_CMD >> 8); | ||
277 | serio->write(serio, HIL_CMD_RNM); | ||
278 | down(&(ptr->sem)); | ||
279 | |||
280 | serio->write(serio, 0); | ||
281 | serio->write(serio, 0); | ||
282 | serio->write(serio, HIL_PKT_CMD >> 8); | ||
283 | serio->write(serio, HIL_CMD_EXD); | ||
284 | down(&(ptr->sem)); | ||
285 | |||
286 | up(&(ptr->sem)); | ||
287 | |||
288 | init_input_dev(&ptr->dev); | ||
289 | did = ptr->idd[0]; | ||
290 | idd = ptr->idd + 1; | ||
291 | txt = "unknown"; | ||
292 | if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { | ||
293 | ptr->dev.evbit[0] = BIT(EV_REL); | ||
294 | txt = "relative"; | ||
295 | } | ||
296 | |||
297 | if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) { | ||
298 | ptr->dev.evbit[0] = BIT(EV_ABS); | ||
299 | txt = "absolute"; | ||
300 | } | ||
301 | if (!ptr->dev.evbit[0]) { | ||
302 | goto bail1; | ||
303 | } | ||
304 | |||
305 | ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); | ||
306 | if (ptr->nbtn) ptr->dev.evbit[0] |= BIT(EV_KEY); | ||
307 | |||
308 | naxsets = HIL_IDD_NUM_AXSETS(*idd); | ||
309 | ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); | ||
310 | |||
311 | printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n", | ||
312 | did, txt); | ||
313 | printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n", | ||
314 | ptr->nbtn, naxsets, ptr->naxes); | ||
315 | |||
316 | btntype = BTN_MISC; | ||
317 | if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET) | ||
318 | #ifdef TABLET_SIMULATES_MOUSE | ||
319 | btntype = BTN_TOUCH; | ||
320 | #else | ||
321 | btntype = BTN_DIGI; | ||
322 | #endif | ||
323 | if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN) | ||
324 | btntype = BTN_TOUCH; | ||
325 | |||
326 | if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE) | ||
327 | btntype = BTN_MOUSE; | ||
328 | |||
329 | for (i = 0; i < ptr->nbtn; i++) { | ||
330 | set_bit(btntype | i, ptr->dev.keybit); | ||
331 | ptr->btnmap[i] = btntype | i; | ||
332 | } | ||
333 | |||
334 | if (btntype == BTN_MOUSE) { | ||
335 | /* Swap buttons 2 and 3 */ | ||
336 | ptr->btnmap[1] = BTN_MIDDLE; | ||
337 | ptr->btnmap[2] = BTN_RIGHT; | ||
338 | } | ||
339 | |||
340 | if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { | ||
341 | for (i = 0; i < ptr->naxes; i++) { | ||
342 | set_bit(REL_X + i, ptr->dev.relbit); | ||
343 | } | ||
344 | for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { | ||
345 | set_bit(REL_X + i, ptr->dev.relbit); | ||
346 | } | ||
347 | } else { | ||
348 | for (i = 0; i < ptr->naxes; i++) { | ||
349 | set_bit(ABS_X + i, ptr->dev.absbit); | ||
350 | ptr->dev.absmin[ABS_X + i] = 0; | ||
351 | ptr->dev.absmax[ABS_X + i] = | ||
352 | HIL_IDD_AXIS_MAX((ptr->idd + 1), i); | ||
353 | } | ||
354 | for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { | ||
355 | set_bit(ABS_X + i, ptr->dev.absbit); | ||
356 | ptr->dev.absmin[ABS_X + i] = 0; | ||
357 | ptr->dev.absmax[ABS_X + i] = | ||
358 | HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3)); | ||
359 | } | ||
360 | #ifdef TABLET_AUTOADJUST | ||
361 | for (i = 0; i < ABS_MAX; i++) { | ||
362 | int diff = ptr->dev.absmax[ABS_X + i] / 10; | ||
363 | ptr->dev.absmin[ABS_X + i] += diff; | ||
364 | ptr->dev.absmax[ABS_X + i] -= diff; | ||
365 | } | ||
366 | #endif | ||
367 | } | ||
368 | |||
369 | ptr->dev.name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME; | ||
370 | |||
371 | ptr->dev.id.bustype = BUS_HIL; | ||
372 | ptr->dev.id.vendor = PCI_VENDOR_ID_HP; | ||
373 | ptr->dev.id.product = 0x0001; /* TODO: get from ptr->rsc */ | ||
374 | ptr->dev.id.version = 0x0100; /* TODO: get from ptr->rsc */ | ||
375 | ptr->dev.dev = &serio->dev; | ||
376 | |||
377 | input_register_device(&ptr->dev); | ||
378 | printk(KERN_INFO "input: %s (%s), ID: %d\n", | ||
379 | ptr->dev.name, | ||
380 | (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad", | ||
381 | did); | ||
382 | |||
383 | return; | ||
384 | bail1: | ||
385 | serio_close(serio); | ||
386 | bail0: | ||
387 | kfree(ptr); | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | |||
392 | static struct serio_driver hil_ptr_serio_driver = { | ||
393 | .driver = { | ||
394 | .name = "hil_ptr", | ||
395 | }, | ||
396 | .description = "HP HIL mouse/tablet driver", | ||
397 | .connect = hil_ptr_connect, | ||
398 | .disconnect = hil_ptr_disconnect, | ||
399 | .interrupt = hil_ptr_interrupt | ||
400 | }; | ||
401 | |||
402 | static int __init hil_ptr_init(void) | ||
403 | { | ||
404 | serio_register_driver(&hil_ptr_serio_driver); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static void __exit hil_ptr_exit(void) | ||
409 | { | ||
410 | serio_unregister_driver(&hil_ptr_serio_driver); | ||
411 | } | ||
412 | |||
413 | module_init(hil_ptr_init); | ||
414 | module_exit(hil_ptr_exit); | ||
diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c new file mode 100644 index 000000000000..ca4e96886627 --- /dev/null +++ b/drivers/input/mouse/inport.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * $Id: inport.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ | ||
3 | * | ||
4 | * Copyright (c) 1999-2001 Vojtech Pavlik | ||
5 | * | ||
6 | * Based on the work of: | ||
7 | * Teemu Rantanen Derrick Cole | ||
8 | * Peter Cervasio Christoph Niemann | ||
9 | * Philip Blundell Russell King | ||
10 | * Bob Harris | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Inport (ATI XL and Microsoft) busmouse driver for Linux | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License as published by | ||
20 | * the Free Software Foundation; either version 2 of the License, or | ||
21 | * (at your option) any later version. | ||
22 | * | ||
23 | * This program is distributed in the hope that it will be useful, | ||
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
26 | * GNU General Public License for more details. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License | ||
29 | * along with this program; if not, write to the Free Software | ||
30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
31 | * | ||
32 | * Should you need to contact me, the author, you can do so either by | ||
33 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
34 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
35 | */ | ||
36 | |||
37 | #include <linux/module.h> | ||
38 | #include <linux/moduleparam.h> | ||
39 | #include <linux/config.h> | ||
40 | #include <linux/ioport.h> | ||
41 | #include <linux/init.h> | ||
42 | #include <linux/interrupt.h> | ||
43 | #include <linux/input.h> | ||
44 | |||
45 | #include <asm/io.h> | ||
46 | #include <asm/irq.h> | ||
47 | |||
48 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
49 | MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver"); | ||
50 | MODULE_LICENSE("GPL"); | ||
51 | |||
52 | #define INPORT_BASE 0x23c | ||
53 | #define INPORT_EXTENT 4 | ||
54 | |||
55 | #define INPORT_CONTROL_PORT INPORT_BASE + 0 | ||
56 | #define INPORT_DATA_PORT INPORT_BASE + 1 | ||
57 | #define INPORT_SIGNATURE_PORT INPORT_BASE + 2 | ||
58 | |||
59 | #define INPORT_REG_BTNS 0x00 | ||
60 | #define INPORT_REG_X 0x01 | ||
61 | #define INPORT_REG_Y 0x02 | ||
62 | #define INPORT_REG_MODE 0x07 | ||
63 | #define INPORT_RESET 0x80 | ||
64 | |||
65 | #ifdef CONFIG_INPUT_ATIXL | ||
66 | #define INPORT_NAME "ATI XL Mouse" | ||
67 | #define INPORT_VENDOR 0x0002 | ||
68 | #define INPORT_SPEED_30HZ 0x01 | ||
69 | #define INPORT_SPEED_50HZ 0x02 | ||
70 | #define INPORT_SPEED_100HZ 0x03 | ||
71 | #define INPORT_SPEED_200HZ 0x04 | ||
72 | #define INPORT_MODE_BASE INPORT_SPEED_100HZ | ||
73 | #define INPORT_MODE_IRQ 0x08 | ||
74 | #else | ||
75 | #define INPORT_NAME "Microsoft InPort Mouse" | ||
76 | #define INPORT_VENDOR 0x0001 | ||
77 | #define INPORT_MODE_BASE 0x10 | ||
78 | #define INPORT_MODE_IRQ 0x01 | ||
79 | #endif | ||
80 | #define INPORT_MODE_HOLD 0x20 | ||
81 | |||
82 | #define INPORT_IRQ 5 | ||
83 | |||
84 | static int inport_irq = INPORT_IRQ; | ||
85 | module_param_named(irq, inport_irq, uint, 0); | ||
86 | MODULE_PARM_DESC(irq, "IRQ number (5=default)"); | ||
87 | |||
88 | __obsolete_setup("inport_irq="); | ||
89 | |||
90 | static int inport_used; | ||
91 | |||
92 | static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
93 | |||
94 | static int inport_open(struct input_dev *dev) | ||
95 | { | ||
96 | if (!inport_used++) { | ||
97 | if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) | ||
98 | return -EBUSY; | ||
99 | outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); | ||
100 | outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); | ||
101 | } | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static void inport_close(struct input_dev *dev) | ||
107 | { | ||
108 | if (!--inport_used) { | ||
109 | outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); | ||
110 | outb(INPORT_MODE_BASE, INPORT_DATA_PORT); | ||
111 | free_irq(inport_irq, NULL); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | static struct input_dev inport_dev = { | ||
116 | .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, | ||
117 | .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) }, | ||
118 | .relbit = { BIT(REL_X) | BIT(REL_Y) }, | ||
119 | .open = inport_open, | ||
120 | .close = inport_close, | ||
121 | .name = INPORT_NAME, | ||
122 | .phys = "isa023c/input0", | ||
123 | .id = { | ||
124 | .bustype = BUS_ISA, | ||
125 | .vendor = INPORT_VENDOR, | ||
126 | .product = 0x0001, | ||
127 | .version = 0x0100, | ||
128 | }, | ||
129 | }; | ||
130 | |||
131 | static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
132 | { | ||
133 | unsigned char buttons; | ||
134 | |||
135 | outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); | ||
136 | outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); | ||
137 | |||
138 | input_regs(&inport_dev, regs); | ||
139 | |||
140 | outb(INPORT_REG_X, INPORT_CONTROL_PORT); | ||
141 | input_report_rel(&inport_dev, REL_X, inb(INPORT_DATA_PORT)); | ||
142 | |||
143 | outb(INPORT_REG_Y, INPORT_CONTROL_PORT); | ||
144 | input_report_rel(&inport_dev, REL_Y, inb(INPORT_DATA_PORT)); | ||
145 | |||
146 | outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT); | ||
147 | buttons = inb(INPORT_DATA_PORT); | ||
148 | |||
149 | input_report_key(&inport_dev, BTN_MIDDLE, buttons & 1); | ||
150 | input_report_key(&inport_dev, BTN_LEFT, buttons & 2); | ||
151 | input_report_key(&inport_dev, BTN_RIGHT, buttons & 4); | ||
152 | |||
153 | outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); | ||
154 | outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); | ||
155 | |||
156 | input_sync(&inport_dev); | ||
157 | return IRQ_HANDLED; | ||
158 | } | ||
159 | |||
160 | static int __init inport_init(void) | ||
161 | { | ||
162 | unsigned char a,b,c; | ||
163 | |||
164 | if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) { | ||
165 | printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE); | ||
166 | return -EBUSY; | ||
167 | } | ||
168 | |||
169 | a = inb(INPORT_SIGNATURE_PORT); | ||
170 | b = inb(INPORT_SIGNATURE_PORT); | ||
171 | c = inb(INPORT_SIGNATURE_PORT); | ||
172 | if (( a == b ) || ( a != c )) { | ||
173 | release_region(INPORT_BASE, INPORT_EXTENT); | ||
174 | printk(KERN_ERR "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE); | ||
175 | return -ENODEV; | ||
176 | } | ||
177 | |||
178 | outb(INPORT_RESET, INPORT_CONTROL_PORT); | ||
179 | outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); | ||
180 | outb(INPORT_MODE_BASE, INPORT_DATA_PORT); | ||
181 | |||
182 | input_register_device(&inport_dev); | ||
183 | |||
184 | printk(KERN_INFO "input: " INPORT_NAME " at %#x irq %d\n", INPORT_BASE, inport_irq); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static void __exit inport_exit(void) | ||
190 | { | ||
191 | input_unregister_device(&inport_dev); | ||
192 | release_region(INPORT_BASE, INPORT_EXTENT); | ||
193 | } | ||
194 | |||
195 | module_init(inport_init); | ||
196 | module_exit(inport_exit); | ||
diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c new file mode 100644 index 000000000000..77eb83e87f61 --- /dev/null +++ b/drivers/input/mouse/logibm.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * $Id: logibm.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ | ||
3 | * | ||
4 | * Copyright (c) 1999-2001 Vojtech Pavlik | ||
5 | * | ||
6 | * Based on the work of: | ||
7 | * James Banks Matthew Dillon | ||
8 | * David Giller Nathan Laredo | ||
9 | * Linus Torvalds Johan Myreen | ||
10 | * Cliff Matthews Philip Blundell | ||
11 | * Russell King | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * Logitech Bus Mouse Driver for Linux | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License as published by | ||
21 | * the Free Software Foundation; either version 2 of the License, or | ||
22 | * (at your option) any later version. | ||
23 | * | ||
24 | * This program is distributed in the hope that it will be useful, | ||
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
27 | * GNU General Public License for more details. | ||
28 | * | ||
29 | * You should have received a copy of the GNU General Public License | ||
30 | * along with this program; if not, write to the Free Software | ||
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
32 | * | ||
33 | * Should you need to contact me, the author, you can do so either by | ||
34 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
35 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
36 | */ | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/moduleparam.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include <linux/ioport.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/input.h> | ||
44 | #include <linux/interrupt.h> | ||
45 | |||
46 | #include <asm/io.h> | ||
47 | #include <asm/irq.h> | ||
48 | |||
49 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
50 | MODULE_DESCRIPTION("Logitech busmouse driver"); | ||
51 | MODULE_LICENSE("GPL"); | ||
52 | |||
53 | #define LOGIBM_BASE 0x23c | ||
54 | #define LOGIBM_EXTENT 4 | ||
55 | |||
56 | #define LOGIBM_DATA_PORT LOGIBM_BASE + 0 | ||
57 | #define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1 | ||
58 | #define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2 | ||
59 | #define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3 | ||
60 | |||
61 | #define LOGIBM_ENABLE_IRQ 0x00 | ||
62 | #define LOGIBM_DISABLE_IRQ 0x10 | ||
63 | #define LOGIBM_READ_X_LOW 0x80 | ||
64 | #define LOGIBM_READ_X_HIGH 0xa0 | ||
65 | #define LOGIBM_READ_Y_LOW 0xc0 | ||
66 | #define LOGIBM_READ_Y_HIGH 0xe0 | ||
67 | |||
68 | #define LOGIBM_DEFAULT_MODE 0x90 | ||
69 | #define LOGIBM_CONFIG_BYTE 0x91 | ||
70 | #define LOGIBM_SIGNATURE_BYTE 0xa5 | ||
71 | |||
72 | #define LOGIBM_IRQ 5 | ||
73 | |||
74 | static int logibm_irq = LOGIBM_IRQ; | ||
75 | module_param_named(irq, logibm_irq, uint, 0); | ||
76 | MODULE_PARM_DESC(irq, "IRQ number (5=default)"); | ||
77 | |||
78 | __obsolete_setup("logibm_irq="); | ||
79 | |||
80 | static int logibm_used = 0; | ||
81 | |||
82 | static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
83 | |||
84 | static int logibm_open(struct input_dev *dev) | ||
85 | { | ||
86 | if (logibm_used++) | ||
87 | return 0; | ||
88 | if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { | ||
89 | logibm_used--; | ||
90 | printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); | ||
91 | return -EBUSY; | ||
92 | } | ||
93 | outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static void logibm_close(struct input_dev *dev) | ||
98 | { | ||
99 | if (--logibm_used) | ||
100 | return; | ||
101 | outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); | ||
102 | free_irq(logibm_irq, NULL); | ||
103 | } | ||
104 | |||
105 | static struct input_dev logibm_dev = { | ||
106 | .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, | ||
107 | .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) }, | ||
108 | .relbit = { BIT(REL_X) | BIT(REL_Y) }, | ||
109 | .open = logibm_open, | ||
110 | .close = logibm_close, | ||
111 | .name = "Logitech bus mouse", | ||
112 | .phys = "isa023c/input0", | ||
113 | .id = { | ||
114 | .bustype = BUS_ISA, | ||
115 | .vendor = 0x0003, | ||
116 | .product = 0x0001, | ||
117 | .version = 0x0100, | ||
118 | }, | ||
119 | }; | ||
120 | |||
121 | static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
122 | { | ||
123 | char dx, dy; | ||
124 | unsigned char buttons; | ||
125 | |||
126 | outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT); | ||
127 | dx = (inb(LOGIBM_DATA_PORT) & 0xf); | ||
128 | outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT); | ||
129 | dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4; | ||
130 | outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT); | ||
131 | dy = (inb(LOGIBM_DATA_PORT) & 0xf); | ||
132 | outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT); | ||
133 | buttons = inb(LOGIBM_DATA_PORT); | ||
134 | dy |= (buttons & 0xf) << 4; | ||
135 | buttons = ~buttons >> 5; | ||
136 | |||
137 | input_regs(&logibm_dev, regs); | ||
138 | input_report_rel(&logibm_dev, REL_X, dx); | ||
139 | input_report_rel(&logibm_dev, REL_Y, dy); | ||
140 | input_report_key(&logibm_dev, BTN_RIGHT, buttons & 1); | ||
141 | input_report_key(&logibm_dev, BTN_MIDDLE, buttons & 2); | ||
142 | input_report_key(&logibm_dev, BTN_LEFT, buttons & 4); | ||
143 | input_sync(&logibm_dev); | ||
144 | |||
145 | outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); | ||
146 | return IRQ_HANDLED; | ||
147 | } | ||
148 | |||
149 | static int __init logibm_init(void) | ||
150 | { | ||
151 | if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) { | ||
152 | printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE); | ||
153 | return -EBUSY; | ||
154 | } | ||
155 | |||
156 | outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT); | ||
157 | outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT); | ||
158 | udelay(100); | ||
159 | |||
160 | if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) { | ||
161 | release_region(LOGIBM_BASE, LOGIBM_EXTENT); | ||
162 | printk(KERN_ERR "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE); | ||
163 | return -ENODEV; | ||
164 | } | ||
165 | |||
166 | outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT); | ||
167 | outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); | ||
168 | |||
169 | input_register_device(&logibm_dev); | ||
170 | |||
171 | printk(KERN_INFO "input: Logitech bus mouse at %#x irq %d\n", LOGIBM_BASE, logibm_irq); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static void __exit logibm_exit(void) | ||
177 | { | ||
178 | input_unregister_device(&logibm_dev); | ||
179 | release_region(LOGIBM_BASE, LOGIBM_EXTENT); | ||
180 | } | ||
181 | |||
182 | module_init(logibm_init); | ||
183 | module_exit(logibm_exit); | ||
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c new file mode 100644 index 000000000000..5ab1bd7d529d --- /dev/null +++ b/drivers/input/mouse/logips2pp.c | |||
@@ -0,0 +1,397 @@ | |||
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 <linux/libps2.h> | ||
15 | #include "psmouse.h" | ||
16 | #include "logips2pp.h" | ||
17 | |||
18 | /* Logitech mouse types */ | ||
19 | #define PS2PP_KIND_WHEEL 1 | ||
20 | #define PS2PP_KIND_MX 2 | ||
21 | #define PS2PP_KIND_TP3 3 | ||
22 | |||
23 | /* Logitech mouse features */ | ||
24 | #define PS2PP_WHEEL 0x01 | ||
25 | #define PS2PP_HWHEEL 0x02 | ||
26 | #define PS2PP_SIDE_BTN 0x04 | ||
27 | #define PS2PP_EXTRA_BTN 0x08 | ||
28 | #define PS2PP_TASK_BTN 0x10 | ||
29 | #define PS2PP_NAV_BTN 0x20 | ||
30 | |||
31 | struct ps2pp_info { | ||
32 | const int model; | ||
33 | unsigned const int kind; | ||
34 | unsigned const int features; | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * Process a PS2++ or PS2T++ packet. | ||
39 | */ | ||
40 | |||
41 | static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse, struct pt_regs *regs) | ||
42 | { | ||
43 | struct input_dev *dev = &psmouse->dev; | ||
44 | unsigned char *packet = psmouse->packet; | ||
45 | |||
46 | if (psmouse->pktcnt < 3) | ||
47 | return PSMOUSE_GOOD_DATA; | ||
48 | |||
49 | /* | ||
50 | * Full packet accumulated, process it | ||
51 | */ | ||
52 | |||
53 | input_regs(dev, regs); | ||
54 | |||
55 | if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) { | ||
56 | |||
57 | /* Logitech extended packet */ | ||
58 | switch ((packet[1] >> 4) | (packet[0] & 0x30)) { | ||
59 | |||
60 | case 0x0d: /* Mouse extra info */ | ||
61 | |||
62 | input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, | ||
63 | (int) (packet[2] & 8) - (int) (packet[2] & 7)); | ||
64 | input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); | ||
65 | input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); | ||
66 | |||
67 | break; | ||
68 | |||
69 | case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ | ||
70 | |||
71 | input_report_key(dev, BTN_SIDE, (packet[2]) & 1); | ||
72 | input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); | ||
73 | input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); | ||
74 | input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); | ||
75 | input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); | ||
76 | |||
77 | break; | ||
78 | |||
79 | case 0x0f: /* TouchPad extra info */ | ||
80 | |||
81 | input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, | ||
82 | (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); | ||
83 | packet[0] = packet[2] | 0x08; | ||
84 | break; | ||
85 | |||
86 | #ifdef DEBUG | ||
87 | default: | ||
88 | printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", | ||
89 | (packet[1] >> 4) | (packet[0] & 0x30)); | ||
90 | #endif | ||
91 | } | ||
92 | } else { | ||
93 | /* Standard PS/2 motion data */ | ||
94 | input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); | ||
95 | input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); | ||
96 | } | ||
97 | |||
98 | input_report_key(dev, BTN_LEFT, packet[0] & 1); | ||
99 | input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); | ||
100 | input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); | ||
101 | |||
102 | input_sync(dev); | ||
103 | |||
104 | return PSMOUSE_FULL_PACKET; | ||
105 | |||
106 | } | ||
107 | |||
108 | /* | ||
109 | * ps2pp_cmd() sends a PS2++ command, sliced into two bit | ||
110 | * pieces through the SETRES command. This is needed to send extended | ||
111 | * commands to mice on notebooks that try to understand the PS/2 protocol | ||
112 | * Ugly. | ||
113 | */ | ||
114 | |||
115 | static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) | ||
116 | { | ||
117 | if (psmouse_sliced_command(psmouse, command)) | ||
118 | return -1; | ||
119 | |||
120 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL)) | ||
121 | return -1; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * SmartScroll / CruiseControl for some newer Logitech mice Defaults to | ||
128 | * enabled if we do nothing to it. Of course I put this in because I want it | ||
129 | * disabled :P | ||
130 | * 1 - enabled (if previously disabled, also default) | ||
131 | * 0 - disabled | ||
132 | */ | ||
133 | |||
134 | static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll) | ||
135 | { | ||
136 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
137 | unsigned char param[4]; | ||
138 | |||
139 | if (smartscroll > 1) | ||
140 | smartscroll = 1; | ||
141 | |||
142 | ps2pp_cmd(psmouse, param, 0x32); | ||
143 | |||
144 | param[0] = 0; | ||
145 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
146 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
147 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
148 | |||
149 | param[0] = smartscroll; | ||
150 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
151 | } | ||
152 | |||
153 | static ssize_t psmouse_attr_show_smartscroll(struct psmouse *psmouse, char *buf) | ||
154 | { | ||
155 | return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0); | ||
156 | } | ||
157 | |||
158 | static ssize_t psmouse_attr_set_smartscroll(struct psmouse *psmouse, const char *buf, size_t count) | ||
159 | { | ||
160 | unsigned long value; | ||
161 | char *rest; | ||
162 | |||
163 | value = simple_strtoul(buf, &rest, 10); | ||
164 | if (*rest || value > 1) | ||
165 | return -EINVAL; | ||
166 | |||
167 | ps2pp_set_smartscroll(psmouse, value); | ||
168 | psmouse->smartscroll = value; | ||
169 | return count; | ||
170 | } | ||
171 | |||
172 | PSMOUSE_DEFINE_ATTR(smartscroll); | ||
173 | |||
174 | /* | ||
175 | * Support 800 dpi resolution _only_ if the user wants it (there are good | ||
176 | * reasons to not use it even if the mouse supports it, and of course there are | ||
177 | * also good reasons to use it, let the user decide). | ||
178 | */ | ||
179 | |||
180 | static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution) | ||
181 | { | ||
182 | if (resolution > 400) { | ||
183 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
184 | unsigned char param = 3; | ||
185 | |||
186 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
187 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
188 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
189 | ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); | ||
190 | psmouse->resolution = 800; | ||
191 | } else | ||
192 | psmouse_set_resolution(psmouse, resolution); | ||
193 | } | ||
194 | |||
195 | static void ps2pp_disconnect(struct psmouse *psmouse) | ||
196 | { | ||
197 | device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll); | ||
198 | } | ||
199 | |||
200 | static struct ps2pp_info *get_model_info(unsigned char model) | ||
201 | { | ||
202 | static struct ps2pp_info ps2pp_list[] = { | ||
203 | { 12, 0, PS2PP_SIDE_BTN}, | ||
204 | { 13, 0, 0 }, | ||
205 | { 15, PS2PP_KIND_MX, /* MX1000 */ | ||
206 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
207 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, | ||
208 | { 40, 0, PS2PP_SIDE_BTN }, | ||
209 | { 41, 0, PS2PP_SIDE_BTN }, | ||
210 | { 42, 0, PS2PP_SIDE_BTN }, | ||
211 | { 43, 0, PS2PP_SIDE_BTN }, | ||
212 | { 50, 0, 0 }, | ||
213 | { 51, 0, 0 }, | ||
214 | { 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, | ||
215 | { 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
216 | { 61, PS2PP_KIND_MX, /* MX700 */ | ||
217 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
218 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, | ||
219 | { 73, 0, PS2PP_SIDE_BTN }, | ||
220 | { 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
221 | { 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
222 | { 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, | ||
223 | { 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
224 | { 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
225 | { 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
226 | { 96, 0, 0 }, | ||
227 | { 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL }, | ||
228 | { 100, PS2PP_KIND_MX, /* MX510 */ | ||
229 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
230 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, | ||
231 | { 111, PS2PP_KIND_MX, /* MX300 */ | ||
232 | PS2PP_WHEEL | PS2PP_EXTRA_BTN | PS2PP_TASK_BTN }, | ||
233 | { 112, PS2PP_KIND_MX, /* MX500 */ | ||
234 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
235 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, | ||
236 | { 114, PS2PP_KIND_MX, /* MX310 */ | ||
237 | PS2PP_WHEEL | PS2PP_SIDE_BTN | | ||
238 | PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }, | ||
239 | { } | ||
240 | }; | ||
241 | int i; | ||
242 | |||
243 | for (i = 0; ps2pp_list[i].model; i++) | ||
244 | if (model == ps2pp_list[i].model) | ||
245 | return &ps2pp_list[i]; | ||
246 | |||
247 | printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model); | ||
248 | return NULL; | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Set up input device's properties based on the detected mouse model. | ||
253 | */ | ||
254 | |||
255 | static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info, | ||
256 | int using_ps2pp) | ||
257 | { | ||
258 | if (model_info->features & PS2PP_SIDE_BTN) | ||
259 | set_bit(BTN_SIDE, psmouse->dev.keybit); | ||
260 | |||
261 | if (model_info->features & PS2PP_EXTRA_BTN) | ||
262 | set_bit(BTN_EXTRA, psmouse->dev.keybit); | ||
263 | |||
264 | if (model_info->features & PS2PP_TASK_BTN) | ||
265 | set_bit(BTN_TASK, psmouse->dev.keybit); | ||
266 | |||
267 | if (model_info->features & PS2PP_NAV_BTN) { | ||
268 | set_bit(BTN_FORWARD, psmouse->dev.keybit); | ||
269 | set_bit(BTN_BACK, psmouse->dev.keybit); | ||
270 | } | ||
271 | |||
272 | if (model_info->features & PS2PP_WHEEL) | ||
273 | set_bit(REL_WHEEL, psmouse->dev.relbit); | ||
274 | |||
275 | if (model_info->features & PS2PP_HWHEEL) | ||
276 | set_bit(REL_HWHEEL, psmouse->dev.relbit); | ||
277 | |||
278 | switch (model_info->kind) { | ||
279 | case PS2PP_KIND_WHEEL: | ||
280 | psmouse->name = "Wheel Mouse"; | ||
281 | break; | ||
282 | |||
283 | case PS2PP_KIND_MX: | ||
284 | psmouse->name = "MX Mouse"; | ||
285 | break; | ||
286 | |||
287 | case PS2PP_KIND_TP3: | ||
288 | psmouse->name = "TouchPad 3"; | ||
289 | break; | ||
290 | |||
291 | default: | ||
292 | /* | ||
293 | * Set name to "Mouse" only when using PS2++, | ||
294 | * otherwise let other protocols define suitable | ||
295 | * name | ||
296 | */ | ||
297 | if (using_ps2pp) | ||
298 | psmouse->name = "Mouse"; | ||
299 | break; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | |||
304 | /* | ||
305 | * Logitech magic init. Detect whether the mouse is a Logitech one | ||
306 | * and its exact model and try turning on extended protocol for ones | ||
307 | * that support it. | ||
308 | */ | ||
309 | |||
310 | int ps2pp_init(struct psmouse *psmouse, int set_properties) | ||
311 | { | ||
312 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
313 | unsigned char param[4]; | ||
314 | unsigned char model, buttons; | ||
315 | struct ps2pp_info *model_info; | ||
316 | int use_ps2pp = 0; | ||
317 | |||
318 | param[0] = 0; | ||
319 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
320 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
321 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
322 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
323 | param[1] = 0; | ||
324 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | ||
325 | |||
326 | if (!param[1]) | ||
327 | return -1; | ||
328 | |||
329 | model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); | ||
330 | buttons = param[1]; | ||
331 | |||
332 | if ((model_info = get_model_info(model)) != NULL) { | ||
333 | |||
334 | /* | ||
335 | * Do Logitech PS2++ / PS2T++ magic init. | ||
336 | */ | ||
337 | if (model == 97) { /* Touch Pad 3 */ | ||
338 | |||
339 | /* Unprotect RAM */ | ||
340 | param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; | ||
341 | ps2_command(ps2dev, param, 0x30d1); | ||
342 | /* Enable features */ | ||
343 | param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; | ||
344 | ps2_command(ps2dev, param, 0x30d1); | ||
345 | /* Enable PS2++ */ | ||
346 | param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; | ||
347 | ps2_command(ps2dev, param, 0x30d1); | ||
348 | |||
349 | param[0] = 0; | ||
350 | if (!ps2_command(ps2dev, param, 0x13d1) && | ||
351 | param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { | ||
352 | use_ps2pp = 1; | ||
353 | } | ||
354 | |||
355 | } else { | ||
356 | |||
357 | param[0] = param[1] = param[2] = 0; | ||
358 | ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ | ||
359 | ps2pp_cmd(psmouse, param, 0xDB); | ||
360 | |||
361 | if ((param[0] & 0x78) == 0x48 && | ||
362 | (param[1] & 0xf3) == 0xc2 && | ||
363 | (param[2] & 0x03) == ((param[1] >> 2) & 3)) { | ||
364 | ps2pp_set_smartscroll(psmouse, psmouse->smartscroll); | ||
365 | use_ps2pp = 1; | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | |||
370 | if (set_properties) { | ||
371 | psmouse->vendor = "Logitech"; | ||
372 | psmouse->model = model; | ||
373 | |||
374 | if (use_ps2pp) { | ||
375 | psmouse->protocol_handler = ps2pp_process_byte; | ||
376 | psmouse->pktsize = 3; | ||
377 | |||
378 | if (model_info->kind != PS2PP_KIND_TP3) { | ||
379 | psmouse->set_resolution = ps2pp_set_resolution; | ||
380 | psmouse->disconnect = ps2pp_disconnect; | ||
381 | |||
382 | device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll); | ||
383 | } | ||
384 | } | ||
385 | |||
386 | if (buttons < 3) | ||
387 | clear_bit(BTN_MIDDLE, psmouse->dev.keybit); | ||
388 | if (buttons < 2) | ||
389 | clear_bit(BTN_RIGHT, psmouse->dev.keybit); | ||
390 | |||
391 | if (model_info) | ||
392 | ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); | ||
393 | } | ||
394 | |||
395 | return use_ps2pp ? 0 : -1; | ||
396 | } | ||
397 | |||
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h new file mode 100644 index 000000000000..64a8ec52ea6d --- /dev/null +++ b/drivers/input/mouse/logips2pp.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * Logitech PS/2++ mouse driver header | ||
3 | * | ||
4 | * Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License version 2 as published by | ||
8 | * the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef _LOGIPS2PP_H | ||
12 | #define _LOGIPS2PP_H | ||
13 | |||
14 | int ps2pp_init(struct psmouse *psmouse, int set_properties); | ||
15 | |||
16 | #endif | ||
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c new file mode 100644 index 000000000000..12dc0ef5020f --- /dev/null +++ b/drivers/input/mouse/maplemouse.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * $Id: maplemouse.c,v 1.2 2004/03/22 01:18:15 lethal Exp $ | ||
3 | * SEGA Dreamcast mouse driver | ||
4 | * Based on drivers/usb/usbmouse.c | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/slab.h> | ||
9 | #include <linux/input.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/timer.h> | ||
13 | #include <linux/maple.h> | ||
14 | |||
15 | MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>"); | ||
16 | MODULE_DESCRIPTION("SEGA Dreamcast mouse driver"); | ||
17 | |||
18 | struct dc_mouse { | ||
19 | struct input_dev dev; | ||
20 | int open; | ||
21 | }; | ||
22 | |||
23 | |||
24 | static void dc_mouse_callback(struct mapleq *mq) | ||
25 | { | ||
26 | int buttons, relx, rely, relz; | ||
27 | struct maple_device *mapledev = mq->dev; | ||
28 | struct dc_mouse *mouse = mapledev->private_data; | ||
29 | struct input_dev *dev = &mouse->dev; | ||
30 | unsigned char *res = mq->recvbuf; | ||
31 | |||
32 | buttons = ~res[8]; | ||
33 | relx=*(unsigned short *)(res+12)-512; | ||
34 | rely=*(unsigned short *)(res+14)-512; | ||
35 | relz=*(unsigned short *)(res+16)-512; | ||
36 | |||
37 | input_report_key(dev, BTN_LEFT, buttons&4); | ||
38 | input_report_key(dev, BTN_MIDDLE, buttons&9); | ||
39 | input_report_key(dev, BTN_RIGHT, buttons&2); | ||
40 | input_report_rel(dev, REL_X, relx); | ||
41 | input_report_rel(dev, REL_Y, rely); | ||
42 | input_report_rel(dev, REL_WHEEL, relz); | ||
43 | input_sync(dev); | ||
44 | } | ||
45 | |||
46 | |||
47 | static int dc_mouse_open(struct input_dev *dev) | ||
48 | { | ||
49 | struct dc_mouse *mouse = dev->private; | ||
50 | mouse->open++; | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | |||
55 | static void dc_mouse_close(struct input_dev *dev) | ||
56 | { | ||
57 | struct dc_mouse *mouse = dev->private; | ||
58 | mouse->open--; | ||
59 | } | ||
60 | |||
61 | |||
62 | static int dc_mouse_connect(struct maple_device *dev) | ||
63 | { | ||
64 | unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); | ||
65 | struct dc_mouse *mouse; | ||
66 | |||
67 | if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL))) | ||
68 | return -1; | ||
69 | memset(mouse, 0, sizeof(struct dc_mouse)); | ||
70 | |||
71 | dev->private_data = mouse; | ||
72 | |||
73 | mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
74 | mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); | ||
75 | mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); | ||
76 | |||
77 | init_input_dev(&mouse->dev); | ||
78 | |||
79 | mouse->dev.private = mouse; | ||
80 | mouse->dev.open = dc_mouse_open; | ||
81 | mouse->dev.close = dc_mouse_close; | ||
82 | mouse->dev.event = NULL; | ||
83 | |||
84 | mouse->dev.name = dev->product_name; | ||
85 | mouse->dev.id.bustype = BUS_MAPLE; | ||
86 | |||
87 | input_register_device(&mouse->dev); | ||
88 | |||
89 | maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE); | ||
90 | |||
91 | printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, mouse->dev.name); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | |||
97 | static void dc_mouse_disconnect(struct maple_device *dev) | ||
98 | { | ||
99 | struct dc_mouse *mouse = dev->private_data; | ||
100 | |||
101 | input_unregister_device(&mouse->dev); | ||
102 | kfree(mouse); | ||
103 | } | ||
104 | |||
105 | |||
106 | static struct maple_driver dc_mouse_driver = { | ||
107 | .function = MAPLE_FUNC_MOUSE, | ||
108 | .name = "Dreamcast mouse", | ||
109 | .connect = dc_mouse_connect, | ||
110 | .disconnect = dc_mouse_disconnect, | ||
111 | }; | ||
112 | |||
113 | |||
114 | static int __init dc_mouse_init(void) | ||
115 | { | ||
116 | maple_register_driver(&dc_mouse_driver); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | |||
121 | static void __exit dc_mouse_exit(void) | ||
122 | { | ||
123 | maple_unregister_driver(&dc_mouse_driver); | ||
124 | } | ||
125 | |||
126 | |||
127 | module_init(dc_mouse_init); | ||
128 | module_exit(dc_mouse_exit); | ||
129 | |||
130 | /* | ||
131 | * Local variables: | ||
132 | * c-basic-offset: 8 | ||
133 | * End: | ||
134 | */ | ||
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c new file mode 100644 index 000000000000..0c74918fe254 --- /dev/null +++ b/drivers/input/mouse/pc110pad.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ | ||
3 | * | ||
4 | * Copyright (c) 2000-2001 Vojtech Pavlik | ||
5 | * | ||
6 | * Based on the work of: | ||
7 | * Alan Cox Robin O'Leary | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * IBM PC110 touchpad driver for Linux | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
28 | * | ||
29 | * Should you need to contact me, the author, you can do so either by | ||
30 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
31 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
32 | */ | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/ioport.h> | ||
38 | #include <linux/input.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/interrupt.h> | ||
41 | #include <linux/pci.h> | ||
42 | |||
43 | #include <asm/io.h> | ||
44 | #include <asm/irq.h> | ||
45 | |||
46 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
47 | MODULE_DESCRIPTION("IBM PC110 touchpad driver"); | ||
48 | MODULE_LICENSE("GPL"); | ||
49 | |||
50 | #define PC110PAD_OFF 0x30 | ||
51 | #define PC110PAD_ON 0x38 | ||
52 | |||
53 | static int pc110pad_irq = 10; | ||
54 | static int pc110pad_io = 0x15e0; | ||
55 | |||
56 | static struct input_dev pc110pad_dev; | ||
57 | static int pc110pad_data[3]; | ||
58 | static int pc110pad_count; | ||
59 | static int pc110pad_used; | ||
60 | |||
61 | static char *pc110pad_name = "IBM PC110 TouchPad"; | ||
62 | static char *pc110pad_phys = "isa15e0/input0"; | ||
63 | |||
64 | static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs) | ||
65 | { | ||
66 | int value = inb_p(pc110pad_io); | ||
67 | int handshake = inb_p(pc110pad_io + 2); | ||
68 | |||
69 | outb_p(handshake | 1, pc110pad_io + 2); | ||
70 | outb_p(handshake & ~1, pc110pad_io + 2); | ||
71 | inb_p(0x64); | ||
72 | |||
73 | pc110pad_data[pc110pad_count++] = value; | ||
74 | |||
75 | if (pc110pad_count < 3) | ||
76 | return IRQ_HANDLED; | ||
77 | |||
78 | input_regs(&pc110pad_dev, regs); | ||
79 | input_report_key(&pc110pad_dev, BTN_TOUCH, | ||
80 | pc110pad_data[0] & 0x01); | ||
81 | input_report_abs(&pc110pad_dev, ABS_X, | ||
82 | pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); | ||
83 | input_report_abs(&pc110pad_dev, ABS_Y, | ||
84 | pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); | ||
85 | input_sync(&pc110pad_dev); | ||
86 | |||
87 | pc110pad_count = 0; | ||
88 | return IRQ_HANDLED; | ||
89 | } | ||
90 | |||
91 | static void pc110pad_close(struct input_dev *dev) | ||
92 | { | ||
93 | if (!--pc110pad_used) | ||
94 | outb(PC110PAD_OFF, pc110pad_io + 2); | ||
95 | } | ||
96 | |||
97 | static int pc110pad_open(struct input_dev *dev) | ||
98 | { | ||
99 | if (pc110pad_used++) | ||
100 | return 0; | ||
101 | |||
102 | pc110pad_interrupt(0,NULL,NULL); | ||
103 | pc110pad_interrupt(0,NULL,NULL); | ||
104 | pc110pad_interrupt(0,NULL,NULL); | ||
105 | outb(PC110PAD_ON, pc110pad_io + 2); | ||
106 | pc110pad_count = 0; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * We try to avoid enabling the hardware if it's not | ||
113 | * there, but we don't know how to test. But we do know | ||
114 | * that the PC110 is not a PCI system. So if we find any | ||
115 | * PCI devices in the machine, we don't have a PC110. | ||
116 | */ | ||
117 | static int __init pc110pad_init(void) | ||
118 | { | ||
119 | struct pci_dev *dev; | ||
120 | |||
121 | dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | ||
122 | if (dev) { | ||
123 | pci_dev_put(dev); | ||
124 | return -ENOENT; | ||
125 | } | ||
126 | |||
127 | if (!request_region(pc110pad_io, 4, "pc110pad")) { | ||
128 | printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", | ||
129 | pc110pad_io, pc110pad_io + 4); | ||
130 | return -EBUSY; | ||
131 | } | ||
132 | |||
133 | outb(PC110PAD_OFF, pc110pad_io + 2); | ||
134 | |||
135 | if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) | ||
136 | { | ||
137 | release_region(pc110pad_io, 4); | ||
138 | printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); | ||
139 | return -EBUSY; | ||
140 | } | ||
141 | |||
142 | pc110pad_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); | ||
143 | pc110pad_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); | ||
144 | pc110pad_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); | ||
145 | |||
146 | pc110pad_dev.absmax[ABS_X] = 0x1ff; | ||
147 | pc110pad_dev.absmax[ABS_Y] = 0x0ff; | ||
148 | |||
149 | pc110pad_dev.open = pc110pad_open; | ||
150 | pc110pad_dev.close = pc110pad_close; | ||
151 | |||
152 | pc110pad_dev.name = pc110pad_name; | ||
153 | pc110pad_dev.phys = pc110pad_phys; | ||
154 | pc110pad_dev.id.bustype = BUS_ISA; | ||
155 | pc110pad_dev.id.vendor = 0x0003; | ||
156 | pc110pad_dev.id.product = 0x0001; | ||
157 | pc110pad_dev.id.version = 0x0100; | ||
158 | |||
159 | input_register_device(&pc110pad_dev); | ||
160 | |||
161 | printk(KERN_INFO "input: %s at %#x irq %d\n", | ||
162 | pc110pad_name, pc110pad_io, pc110pad_irq); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static void __exit pc110pad_exit(void) | ||
168 | { | ||
169 | input_unregister_device(&pc110pad_dev); | ||
170 | |||
171 | outb(PC110PAD_OFF, pc110pad_io + 2); | ||
172 | |||
173 | free_irq(pc110pad_irq, NULL); | ||
174 | release_region(pc110pad_io, 4); | ||
175 | } | ||
176 | |||
177 | module_init(pc110pad_init); | ||
178 | module_exit(pc110pad_exit); | ||
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c new file mode 100644 index 000000000000..cd8509549eac --- /dev/null +++ b/drivers/input/mouse/psmouse-base.c | |||
@@ -0,0 +1,1011 @@ | |||
1 | /* | ||
2 | * PS/2 mouse driver | ||
3 | * | ||
4 | * Copyright (c) 1999-2002 Vojtech Pavlik | ||
5 | * Copyright (c) 2003-2004 Dmitry Torokhov | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/delay.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/input.h> | ||
20 | #include <linux/serio.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/libps2.h> | ||
23 | #include "psmouse.h" | ||
24 | #include "synaptics.h" | ||
25 | #include "logips2pp.h" | ||
26 | #include "alps.h" | ||
27 | |||
28 | #define DRIVER_DESC "PS/2 mouse driver" | ||
29 | |||
30 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); | ||
31 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | static unsigned int psmouse_max_proto = -1U; | ||
35 | static int psmouse_set_maxproto(const char *val, struct kernel_param *kp); | ||
36 | static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp); | ||
37 | static char *psmouse_proto_abbrev[] = { NULL, "bare", NULL, NULL, NULL, "imps", "exps", NULL, NULL, NULL }; | ||
38 | #define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) | ||
39 | #define param_set_proto_abbrev psmouse_set_maxproto | ||
40 | #define param_get_proto_abbrev psmouse_get_maxproto | ||
41 | module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644); | ||
42 | MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches."); | ||
43 | |||
44 | static unsigned int psmouse_resolution = 200; | ||
45 | module_param_named(resolution, psmouse_resolution, uint, 0644); | ||
46 | MODULE_PARM_DESC(resolution, "Resolution, in dpi."); | ||
47 | |||
48 | static unsigned int psmouse_rate = 100; | ||
49 | module_param_named(rate, psmouse_rate, uint, 0644); | ||
50 | MODULE_PARM_DESC(rate, "Report rate, in reports per second."); | ||
51 | |||
52 | static unsigned int psmouse_smartscroll = 1; | ||
53 | module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); | ||
54 | MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); | ||
55 | |||
56 | static unsigned int psmouse_resetafter; | ||
57 | module_param_named(resetafter, psmouse_resetafter, uint, 0644); | ||
58 | MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); | ||
59 | |||
60 | PSMOUSE_DEFINE_ATTR(rate); | ||
61 | PSMOUSE_DEFINE_ATTR(resolution); | ||
62 | PSMOUSE_DEFINE_ATTR(resetafter); | ||
63 | |||
64 | __obsolete_setup("psmouse_noext"); | ||
65 | __obsolete_setup("psmouse_resolution="); | ||
66 | __obsolete_setup("psmouse_smartscroll="); | ||
67 | __obsolete_setup("psmouse_resetafter="); | ||
68 | __obsolete_setup("psmouse_rate="); | ||
69 | |||
70 | static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2" }; | ||
71 | |||
72 | /* | ||
73 | * psmouse_process_byte() analyzes the PS/2 data stream and reports | ||
74 | * relevant events to the input module once full packet has arrived. | ||
75 | */ | ||
76 | |||
77 | static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_regs *regs) | ||
78 | { | ||
79 | struct input_dev *dev = &psmouse->dev; | ||
80 | unsigned char *packet = psmouse->packet; | ||
81 | |||
82 | if (psmouse->pktcnt < psmouse->pktsize) | ||
83 | return PSMOUSE_GOOD_DATA; | ||
84 | |||
85 | /* | ||
86 | * Full packet accumulated, process it | ||
87 | */ | ||
88 | |||
89 | input_regs(dev, regs); | ||
90 | |||
91 | /* | ||
92 | * Scroll wheel on IntelliMice, scroll buttons on NetMice | ||
93 | */ | ||
94 | |||
95 | if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) | ||
96 | input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); | ||
97 | |||
98 | /* | ||
99 | * Scroll wheel and buttons on IntelliMouse Explorer | ||
100 | */ | ||
101 | |||
102 | if (psmouse->type == PSMOUSE_IMEX) { | ||
103 | input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); | ||
104 | input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); | ||
105 | input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Extra buttons on Genius NewNet 3D | ||
110 | */ | ||
111 | |||
112 | if (psmouse->type == PSMOUSE_GENPS) { | ||
113 | input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); | ||
114 | input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * Extra button on ThinkingMouse | ||
119 | */ | ||
120 | if (psmouse->type == PSMOUSE_THINKPS) { | ||
121 | input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); | ||
122 | /* Without this bit of weirdness moving up gives wildly high Y changes. */ | ||
123 | packet[1] |= (packet[0] & 0x40) << 1; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Generic PS/2 Mouse | ||
128 | */ | ||
129 | |||
130 | input_report_key(dev, BTN_LEFT, packet[0] & 1); | ||
131 | input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); | ||
132 | input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); | ||
133 | |||
134 | input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); | ||
135 | input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); | ||
136 | |||
137 | input_sync(dev); | ||
138 | |||
139 | return PSMOUSE_FULL_PACKET; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * psmouse_interrupt() handles incoming characters, either gathering them into | ||
144 | * packets or passing them to the command routine as command output. | ||
145 | */ | ||
146 | |||
147 | static irqreturn_t psmouse_interrupt(struct serio *serio, | ||
148 | unsigned char data, unsigned int flags, struct pt_regs *regs) | ||
149 | { | ||
150 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
151 | psmouse_ret_t rc; | ||
152 | |||
153 | if (psmouse->state == PSMOUSE_IGNORE) | ||
154 | goto out; | ||
155 | |||
156 | if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) { | ||
157 | if (psmouse->state == PSMOUSE_ACTIVATED) | ||
158 | printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n", | ||
159 | flags & SERIO_TIMEOUT ? " timeout" : "", | ||
160 | flags & SERIO_PARITY ? " bad parity" : ""); | ||
161 | ps2_cmd_aborted(&psmouse->ps2dev); | ||
162 | goto out; | ||
163 | } | ||
164 | |||
165 | if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK)) | ||
166 | if (ps2_handle_ack(&psmouse->ps2dev, data)) | ||
167 | goto out; | ||
168 | |||
169 | if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD)) | ||
170 | if (ps2_handle_response(&psmouse->ps2dev, data)) | ||
171 | goto out; | ||
172 | |||
173 | if (psmouse->state == PSMOUSE_INITIALIZING) | ||
174 | goto out; | ||
175 | |||
176 | if (psmouse->state == PSMOUSE_ACTIVATED && | ||
177 | psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { | ||
178 | printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", | ||
179 | psmouse->name, psmouse->phys, psmouse->pktcnt); | ||
180 | psmouse->pktcnt = 0; | ||
181 | } | ||
182 | |||
183 | psmouse->last = jiffies; | ||
184 | psmouse->packet[psmouse->pktcnt++] = data; | ||
185 | |||
186 | if (psmouse->packet[0] == PSMOUSE_RET_BAT) { | ||
187 | if (psmouse->pktcnt == 1) | ||
188 | goto out; | ||
189 | |||
190 | if (psmouse->pktcnt == 2) { | ||
191 | if (psmouse->packet[1] == PSMOUSE_RET_ID) { | ||
192 | psmouse->state = PSMOUSE_IGNORE; | ||
193 | serio_reconnect(serio); | ||
194 | goto out; | ||
195 | } | ||
196 | if (psmouse->type == PSMOUSE_SYNAPTICS) { | ||
197 | /* neither 0xAA nor 0x00 are valid first bytes | ||
198 | * for a packet in absolute mode | ||
199 | */ | ||
200 | psmouse->pktcnt = 0; | ||
201 | goto out; | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | rc = psmouse->protocol_handler(psmouse, regs); | ||
207 | |||
208 | switch (rc) { | ||
209 | case PSMOUSE_BAD_DATA: | ||
210 | printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", | ||
211 | psmouse->name, psmouse->phys, psmouse->pktcnt); | ||
212 | psmouse->pktcnt = 0; | ||
213 | |||
214 | if (++psmouse->out_of_sync == psmouse->resetafter) { | ||
215 | psmouse->state = PSMOUSE_IGNORE; | ||
216 | printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); | ||
217 | serio_reconnect(psmouse->ps2dev.serio); | ||
218 | } | ||
219 | break; | ||
220 | |||
221 | case PSMOUSE_FULL_PACKET: | ||
222 | psmouse->pktcnt = 0; | ||
223 | if (psmouse->out_of_sync) { | ||
224 | psmouse->out_of_sync = 0; | ||
225 | printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", | ||
226 | psmouse->name, psmouse->phys); | ||
227 | } | ||
228 | break; | ||
229 | |||
230 | case PSMOUSE_GOOD_DATA: | ||
231 | break; | ||
232 | } | ||
233 | out: | ||
234 | return IRQ_HANDLED; | ||
235 | } | ||
236 | |||
237 | |||
238 | /* | ||
239 | * psmouse_sliced_command() sends an extended PS/2 command to the mouse | ||
240 | * using sliced syntax, understood by advanced devices, such as Logitech | ||
241 | * or Synaptics touchpads. The command is encoded as: | ||
242 | * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu | ||
243 | * is the command. | ||
244 | */ | ||
245 | int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) | ||
246 | { | ||
247 | int i; | ||
248 | |||
249 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) | ||
250 | return -1; | ||
251 | |||
252 | for (i = 6; i >= 0; i -= 2) { | ||
253 | unsigned char d = (command >> i) & 3; | ||
254 | if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES)) | ||
255 | return -1; | ||
256 | } | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | |||
262 | /* | ||
263 | * psmouse_reset() resets the mouse into power-on state. | ||
264 | */ | ||
265 | int psmouse_reset(struct psmouse *psmouse) | ||
266 | { | ||
267 | unsigned char param[2]; | ||
268 | |||
269 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) | ||
270 | return -1; | ||
271 | |||
272 | if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) | ||
273 | return -1; | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | |||
279 | /* | ||
280 | * Genius NetMouse magic init. | ||
281 | */ | ||
282 | static int genius_detect(struct psmouse *psmouse, int set_properties) | ||
283 | { | ||
284 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
285 | unsigned char param[4]; | ||
286 | |||
287 | param[0] = 3; | ||
288 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
289 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
290 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
291 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
292 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | ||
293 | |||
294 | if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) | ||
295 | return -1; | ||
296 | |||
297 | if (set_properties) { | ||
298 | set_bit(BTN_EXTRA, psmouse->dev.keybit); | ||
299 | set_bit(BTN_SIDE, psmouse->dev.keybit); | ||
300 | set_bit(REL_WHEEL, psmouse->dev.relbit); | ||
301 | |||
302 | psmouse->vendor = "Genius"; | ||
303 | psmouse->name = "Wheel Mouse"; | ||
304 | psmouse->pktsize = 4; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * IntelliMouse magic init. | ||
312 | */ | ||
313 | static int intellimouse_detect(struct psmouse *psmouse, int set_properties) | ||
314 | { | ||
315 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
316 | unsigned char param[2]; | ||
317 | |||
318 | param[0] = 200; | ||
319 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
320 | param[0] = 100; | ||
321 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
322 | param[0] = 80; | ||
323 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
324 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
325 | |||
326 | if (param[0] != 3) | ||
327 | return -1; | ||
328 | |||
329 | if (set_properties) { | ||
330 | set_bit(REL_WHEEL, psmouse->dev.relbit); | ||
331 | |||
332 | if (!psmouse->vendor) psmouse->vendor = "Generic"; | ||
333 | if (!psmouse->name) psmouse->name = "Wheel Mouse"; | ||
334 | psmouse->pktsize = 4; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Try IntelliMouse/Explorer magic init. | ||
342 | */ | ||
343 | static int im_explorer_detect(struct psmouse *psmouse, int set_properties) | ||
344 | { | ||
345 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
346 | unsigned char param[2]; | ||
347 | |||
348 | intellimouse_detect(psmouse, 0); | ||
349 | |||
350 | param[0] = 200; | ||
351 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
352 | param[0] = 200; | ||
353 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
354 | param[0] = 80; | ||
355 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
356 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
357 | |||
358 | if (param[0] != 4) | ||
359 | return -1; | ||
360 | |||
361 | if (set_properties) { | ||
362 | set_bit(REL_WHEEL, psmouse->dev.relbit); | ||
363 | set_bit(BTN_SIDE, psmouse->dev.keybit); | ||
364 | set_bit(BTN_EXTRA, psmouse->dev.keybit); | ||
365 | |||
366 | if (!psmouse->vendor) psmouse->vendor = "Generic"; | ||
367 | if (!psmouse->name) psmouse->name = "Explorer Mouse"; | ||
368 | psmouse->pktsize = 4; | ||
369 | } | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * Kensington ThinkingMouse / ExpertMouse magic init. | ||
376 | */ | ||
377 | static int thinking_detect(struct psmouse *psmouse, int set_properties) | ||
378 | { | ||
379 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
380 | unsigned char param[2]; | ||
381 | unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20, 0 }; | ||
382 | int i; | ||
383 | |||
384 | param[0] = 10; | ||
385 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
386 | param[0] = 0; | ||
387 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
388 | for (i = 0; seq[i]; i++) | ||
389 | ps2_command(ps2dev, seq + i, PSMOUSE_CMD_SETRATE); | ||
390 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
391 | |||
392 | if (param[0] != 2) | ||
393 | return -1; | ||
394 | |||
395 | if (set_properties) { | ||
396 | set_bit(BTN_EXTRA, psmouse->dev.keybit); | ||
397 | |||
398 | psmouse->vendor = "Kensington"; | ||
399 | psmouse->name = "ThinkingMouse"; | ||
400 | } | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | /* | ||
406 | * Bare PS/2 protocol "detection". Always succeeds. | ||
407 | */ | ||
408 | static int ps2bare_detect(struct psmouse *psmouse, int set_properties) | ||
409 | { | ||
410 | if (!psmouse->vendor) psmouse->vendor = "Generic"; | ||
411 | if (!psmouse->name) psmouse->name = "Mouse"; | ||
412 | |||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * psmouse_extensions() probes for any extensions to the basic PS/2 protocol | ||
418 | * the mouse may have. | ||
419 | */ | ||
420 | |||
421 | static int psmouse_extensions(struct psmouse *psmouse, | ||
422 | unsigned int max_proto, int set_properties) | ||
423 | { | ||
424 | int synaptics_hardware = 0; | ||
425 | |||
426 | /* | ||
427 | * Try Kensington ThinkingMouse (we try first, because synaptics probe | ||
428 | * upsets the thinkingmouse). | ||
429 | */ | ||
430 | |||
431 | if (max_proto > PSMOUSE_IMEX && thinking_detect(psmouse, set_properties) == 0) | ||
432 | return PSMOUSE_THINKPS; | ||
433 | |||
434 | /* | ||
435 | * Try Synaptics TouchPad | ||
436 | */ | ||
437 | if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) { | ||
438 | synaptics_hardware = 1; | ||
439 | |||
440 | if (max_proto > PSMOUSE_IMEX) { | ||
441 | if (!set_properties || synaptics_init(psmouse) == 0) | ||
442 | return PSMOUSE_SYNAPTICS; | ||
443 | /* | ||
444 | * Some Synaptics touchpads can emulate extended protocols (like IMPS/2). | ||
445 | * Unfortunately Logitech/Genius probes confuse some firmware versions so | ||
446 | * we'll have to skip them. | ||
447 | */ | ||
448 | max_proto = PSMOUSE_IMEX; | ||
449 | } | ||
450 | /* | ||
451 | * Make sure that touchpad is in relative mode, gestures (taps) are enabled | ||
452 | */ | ||
453 | synaptics_reset(psmouse); | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * Try ALPS TouchPad | ||
458 | */ | ||
459 | if (max_proto > PSMOUSE_IMEX) { | ||
460 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
461 | if (alps_detect(psmouse, set_properties) == 0) { | ||
462 | if (!set_properties || alps_init(psmouse) == 0) | ||
463 | return PSMOUSE_ALPS; | ||
464 | /* | ||
465 | * Init failed, try basic relative protocols | ||
466 | */ | ||
467 | max_proto = PSMOUSE_IMEX; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse, set_properties) == 0) | ||
472 | return PSMOUSE_GENPS; | ||
473 | |||
474 | if (max_proto > PSMOUSE_IMEX && ps2pp_init(psmouse, set_properties) == 0) | ||
475 | return PSMOUSE_PS2PP; | ||
476 | |||
477 | /* | ||
478 | * Reset to defaults in case the device got confused by extended | ||
479 | * protocol probes. | ||
480 | */ | ||
481 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
482 | |||
483 | if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0) | ||
484 | return PSMOUSE_IMEX; | ||
485 | |||
486 | if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0) | ||
487 | return PSMOUSE_IMPS; | ||
488 | |||
489 | /* | ||
490 | * Okay, all failed, we have a standard mouse here. The number of the buttons | ||
491 | * is still a question, though. We assume 3. | ||
492 | */ | ||
493 | ps2bare_detect(psmouse, set_properties); | ||
494 | |||
495 | if (synaptics_hardware) { | ||
496 | /* | ||
497 | * We detected Synaptics hardware but it did not respond to IMPS/2 probes. | ||
498 | * We need to reset the touchpad because if there is a track point on the | ||
499 | * pass through port it could get disabled while probing for protocol | ||
500 | * extensions. | ||
501 | */ | ||
502 | psmouse_reset(psmouse); | ||
503 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
504 | } | ||
505 | |||
506 | return PSMOUSE_PS2; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * psmouse_probe() probes for a PS/2 mouse. | ||
511 | */ | ||
512 | |||
513 | static int psmouse_probe(struct psmouse *psmouse) | ||
514 | { | ||
515 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
516 | unsigned char param[2]; | ||
517 | |||
518 | /* | ||
519 | * First, we check if it's a mouse. It should send 0x00 or 0x03 | ||
520 | * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. | ||
521 | */ | ||
522 | |||
523 | param[0] = 0xa5; | ||
524 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) | ||
525 | return -1; | ||
526 | |||
527 | if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04) | ||
528 | return -1; | ||
529 | |||
530 | /* | ||
531 | * Then we reset and disable the mouse so that it doesn't generate events. | ||
532 | */ | ||
533 | |||
534 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) | ||
535 | printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys); | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * Here we set the mouse resolution. | ||
542 | */ | ||
543 | |||
544 | void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) | ||
545 | { | ||
546 | unsigned char params[] = { 0, 1, 2, 2, 3 }; | ||
547 | |||
548 | if (resolution == 0 || resolution > 200) | ||
549 | resolution = 200; | ||
550 | |||
551 | ps2_command(&psmouse->ps2dev, ¶ms[resolution / 50], PSMOUSE_CMD_SETRES); | ||
552 | psmouse->resolution = 25 << params[resolution / 50]; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * Here we set the mouse report rate. | ||
557 | */ | ||
558 | |||
559 | static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) | ||
560 | { | ||
561 | unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; | ||
562 | int i = 0; | ||
563 | |||
564 | while (rates[i] > rate) i++; | ||
565 | ps2_command(&psmouse->ps2dev, &rates[i], PSMOUSE_CMD_SETRATE); | ||
566 | psmouse->rate = rates[i]; | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * psmouse_initialize() initializes the mouse to a sane state. | ||
571 | */ | ||
572 | |||
573 | static void psmouse_initialize(struct psmouse *psmouse) | ||
574 | { | ||
575 | /* | ||
576 | * We set the mouse into streaming mode. | ||
577 | */ | ||
578 | |||
579 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM); | ||
580 | |||
581 | /* | ||
582 | * We set the mouse report rate, resolution and scaling. | ||
583 | */ | ||
584 | |||
585 | if (psmouse_max_proto != PSMOUSE_PS2) { | ||
586 | psmouse->set_rate(psmouse, psmouse->rate); | ||
587 | psmouse->set_resolution(psmouse, psmouse->resolution); | ||
588 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * psmouse_set_state() sets new psmouse state and resets all flags and | ||
594 | * counters while holding serio lock so fighting with interrupt handler | ||
595 | * is not a concern. | ||
596 | */ | ||
597 | |||
598 | static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) | ||
599 | { | ||
600 | serio_pause_rx(psmouse->ps2dev.serio); | ||
601 | psmouse->state = new_state; | ||
602 | psmouse->pktcnt = psmouse->out_of_sync = 0; | ||
603 | psmouse->ps2dev.flags = 0; | ||
604 | serio_continue_rx(psmouse->ps2dev.serio); | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * psmouse_activate() enables the mouse so that we get motion reports from it. | ||
609 | */ | ||
610 | |||
611 | static void psmouse_activate(struct psmouse *psmouse) | ||
612 | { | ||
613 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) | ||
614 | printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", | ||
615 | psmouse->ps2dev.serio->phys); | ||
616 | |||
617 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
618 | } | ||
619 | |||
620 | |||
621 | /* | ||
622 | * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion | ||
623 | * reports from it unless we explicitely request it. | ||
624 | */ | ||
625 | |||
626 | static void psmouse_deactivate(struct psmouse *psmouse) | ||
627 | { | ||
628 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) | ||
629 | printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", | ||
630 | psmouse->ps2dev.serio->phys); | ||
631 | |||
632 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
633 | } | ||
634 | |||
635 | |||
636 | /* | ||
637 | * psmouse_cleanup() resets the mouse into power-on state. | ||
638 | */ | ||
639 | |||
640 | static void psmouse_cleanup(struct serio *serio) | ||
641 | { | ||
642 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
643 | |||
644 | psmouse_reset(psmouse); | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * psmouse_disconnect() closes and frees. | ||
649 | */ | ||
650 | |||
651 | static void psmouse_disconnect(struct serio *serio) | ||
652 | { | ||
653 | struct psmouse *psmouse, *parent; | ||
654 | |||
655 | device_remove_file(&serio->dev, &psmouse_attr_rate); | ||
656 | device_remove_file(&serio->dev, &psmouse_attr_resolution); | ||
657 | device_remove_file(&serio->dev, &psmouse_attr_resetafter); | ||
658 | |||
659 | psmouse = serio_get_drvdata(serio); | ||
660 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
661 | |||
662 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
663 | parent = serio_get_drvdata(serio->parent); | ||
664 | if (parent->pt_deactivate) | ||
665 | parent->pt_deactivate(parent); | ||
666 | } | ||
667 | |||
668 | if (psmouse->disconnect) | ||
669 | psmouse->disconnect(psmouse); | ||
670 | |||
671 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
672 | |||
673 | input_unregister_device(&psmouse->dev); | ||
674 | serio_close(serio); | ||
675 | serio_set_drvdata(serio, NULL); | ||
676 | kfree(psmouse); | ||
677 | } | ||
678 | |||
679 | /* | ||
680 | * psmouse_connect() is a callback from the serio module when | ||
681 | * an unhandled serio port is found. | ||
682 | */ | ||
683 | static int psmouse_connect(struct serio *serio, struct serio_driver *drv) | ||
684 | { | ||
685 | struct psmouse *psmouse, *parent = NULL; | ||
686 | int retval; | ||
687 | |||
688 | /* | ||
689 | * If this is a pass-through port deactivate parent so the device | ||
690 | * connected to this port can be successfully identified | ||
691 | */ | ||
692 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
693 | parent = serio_get_drvdata(serio->parent); | ||
694 | psmouse_deactivate(parent); | ||
695 | } | ||
696 | |||
697 | if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) { | ||
698 | retval = -ENOMEM; | ||
699 | goto out; | ||
700 | } | ||
701 | |||
702 | memset(psmouse, 0, sizeof(struct psmouse)); | ||
703 | |||
704 | ps2_init(&psmouse->ps2dev, serio); | ||
705 | sprintf(psmouse->phys, "%s/input0", serio->phys); | ||
706 | psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
707 | psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); | ||
708 | psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
709 | psmouse->dev.private = psmouse; | ||
710 | psmouse->dev.dev = &serio->dev; | ||
711 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
712 | |||
713 | serio_set_drvdata(serio, psmouse); | ||
714 | |||
715 | retval = serio_open(serio, drv); | ||
716 | if (retval) { | ||
717 | serio_set_drvdata(serio, NULL); | ||
718 | kfree(psmouse); | ||
719 | goto out; | ||
720 | } | ||
721 | |||
722 | if (psmouse_probe(psmouse) < 0) { | ||
723 | serio_close(serio); | ||
724 | serio_set_drvdata(serio, NULL); | ||
725 | kfree(psmouse); | ||
726 | retval = -ENODEV; | ||
727 | goto out; | ||
728 | } | ||
729 | |||
730 | psmouse->rate = psmouse_rate; | ||
731 | psmouse->resolution = psmouse_resolution; | ||
732 | psmouse->resetafter = psmouse_resetafter; | ||
733 | psmouse->smartscroll = psmouse_smartscroll; | ||
734 | psmouse->set_rate = psmouse_set_rate; | ||
735 | psmouse->set_resolution = psmouse_set_resolution; | ||
736 | psmouse->protocol_handler = psmouse_process_byte; | ||
737 | psmouse->pktsize = 3; | ||
738 | |||
739 | psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); | ||
740 | |||
741 | sprintf(psmouse->devname, "%s %s %s", | ||
742 | psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name); | ||
743 | |||
744 | psmouse->dev.name = psmouse->devname; | ||
745 | psmouse->dev.phys = psmouse->phys; | ||
746 | psmouse->dev.id.bustype = BUS_I8042; | ||
747 | psmouse->dev.id.vendor = 0x0002; | ||
748 | psmouse->dev.id.product = psmouse->type; | ||
749 | psmouse->dev.id.version = psmouse->model; | ||
750 | |||
751 | input_register_device(&psmouse->dev); | ||
752 | |||
753 | printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); | ||
754 | |||
755 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
756 | |||
757 | psmouse_initialize(psmouse); | ||
758 | |||
759 | if (parent && parent->pt_activate) | ||
760 | parent->pt_activate(parent); | ||
761 | |||
762 | device_create_file(&serio->dev, &psmouse_attr_rate); | ||
763 | device_create_file(&serio->dev, &psmouse_attr_resolution); | ||
764 | device_create_file(&serio->dev, &psmouse_attr_resetafter); | ||
765 | |||
766 | psmouse_activate(psmouse); | ||
767 | |||
768 | retval = 0; | ||
769 | |||
770 | out: | ||
771 | /* If this is a pass-through port the parent awaits to be activated */ | ||
772 | if (parent) | ||
773 | psmouse_activate(parent); | ||
774 | |||
775 | return retval; | ||
776 | } | ||
777 | |||
778 | |||
779 | static int psmouse_reconnect(struct serio *serio) | ||
780 | { | ||
781 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
782 | struct psmouse *parent = NULL; | ||
783 | struct serio_driver *drv = serio->drv; | ||
784 | int rc = -1; | ||
785 | |||
786 | if (!drv || !psmouse) { | ||
787 | printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n"); | ||
788 | return -1; | ||
789 | } | ||
790 | |||
791 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
792 | parent = serio_get_drvdata(serio->parent); | ||
793 | psmouse_deactivate(parent); | ||
794 | } | ||
795 | |||
796 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
797 | |||
798 | if (psmouse->reconnect) { | ||
799 | if (psmouse->reconnect(psmouse)) | ||
800 | goto out; | ||
801 | } else if (psmouse_probe(psmouse) < 0 || | ||
802 | psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0)) | ||
803 | goto out; | ||
804 | |||
805 | /* ok, the device type (and capabilities) match the old one, | ||
806 | * we can continue using it, complete intialization | ||
807 | */ | ||
808 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
809 | |||
810 | psmouse_initialize(psmouse); | ||
811 | |||
812 | if (parent && parent->pt_activate) | ||
813 | parent->pt_activate(parent); | ||
814 | |||
815 | psmouse_activate(psmouse); | ||
816 | rc = 0; | ||
817 | |||
818 | out: | ||
819 | /* If this is a pass-through port the parent waits to be activated */ | ||
820 | if (parent) | ||
821 | psmouse_activate(parent); | ||
822 | |||
823 | return rc; | ||
824 | } | ||
825 | |||
826 | static struct serio_device_id psmouse_serio_ids[] = { | ||
827 | { | ||
828 | .type = SERIO_8042, | ||
829 | .proto = SERIO_ANY, | ||
830 | .id = SERIO_ANY, | ||
831 | .extra = SERIO_ANY, | ||
832 | }, | ||
833 | { | ||
834 | .type = SERIO_PS_PSTHRU, | ||
835 | .proto = SERIO_ANY, | ||
836 | .id = SERIO_ANY, | ||
837 | .extra = SERIO_ANY, | ||
838 | }, | ||
839 | { 0 } | ||
840 | }; | ||
841 | |||
842 | MODULE_DEVICE_TABLE(serio, psmouse_serio_ids); | ||
843 | |||
844 | static struct serio_driver psmouse_drv = { | ||
845 | .driver = { | ||
846 | .name = "psmouse", | ||
847 | }, | ||
848 | .description = DRIVER_DESC, | ||
849 | .id_table = psmouse_serio_ids, | ||
850 | .interrupt = psmouse_interrupt, | ||
851 | .connect = psmouse_connect, | ||
852 | .reconnect = psmouse_reconnect, | ||
853 | .disconnect = psmouse_disconnect, | ||
854 | .cleanup = psmouse_cleanup, | ||
855 | }; | ||
856 | |||
857 | ssize_t psmouse_attr_show_helper(struct device *dev, char *buf, | ||
858 | ssize_t (*handler)(struct psmouse *, char *)) | ||
859 | { | ||
860 | struct serio *serio = to_serio_port(dev); | ||
861 | int retval; | ||
862 | |||
863 | retval = serio_pin_driver(serio); | ||
864 | if (retval) | ||
865 | return retval; | ||
866 | |||
867 | if (serio->drv != &psmouse_drv) { | ||
868 | retval = -ENODEV; | ||
869 | goto out; | ||
870 | } | ||
871 | |||
872 | retval = handler(serio_get_drvdata(serio), buf); | ||
873 | |||
874 | out: | ||
875 | serio_unpin_driver(serio); | ||
876 | return retval; | ||
877 | } | ||
878 | |||
879 | ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t count, | ||
880 | ssize_t (*handler)(struct psmouse *, const char *, size_t)) | ||
881 | { | ||
882 | struct serio *serio = to_serio_port(dev); | ||
883 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
884 | struct psmouse *parent = NULL; | ||
885 | int retval; | ||
886 | |||
887 | retval = serio_pin_driver(serio); | ||
888 | if (retval) | ||
889 | return retval; | ||
890 | |||
891 | if (serio->drv != &psmouse_drv) { | ||
892 | retval = -ENODEV; | ||
893 | goto out; | ||
894 | } | ||
895 | |||
896 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
897 | parent = serio_get_drvdata(serio->parent); | ||
898 | psmouse_deactivate(parent); | ||
899 | } | ||
900 | psmouse_deactivate(psmouse); | ||
901 | |||
902 | retval = handler(psmouse, buf, count); | ||
903 | |||
904 | psmouse_activate(psmouse); | ||
905 | if (parent) | ||
906 | psmouse_activate(parent); | ||
907 | |||
908 | out: | ||
909 | serio_unpin_driver(serio); | ||
910 | return retval; | ||
911 | } | ||
912 | |||
913 | static ssize_t psmouse_attr_show_rate(struct psmouse *psmouse, char *buf) | ||
914 | { | ||
915 | return sprintf(buf, "%d\n", psmouse->rate); | ||
916 | } | ||
917 | |||
918 | static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, const char *buf, size_t count) | ||
919 | { | ||
920 | unsigned long value; | ||
921 | char *rest; | ||
922 | |||
923 | value = simple_strtoul(buf, &rest, 10); | ||
924 | if (*rest) | ||
925 | return -EINVAL; | ||
926 | |||
927 | psmouse->set_rate(psmouse, value); | ||
928 | return count; | ||
929 | } | ||
930 | |||
931 | static ssize_t psmouse_attr_show_resolution(struct psmouse *psmouse, char *buf) | ||
932 | { | ||
933 | return sprintf(buf, "%d\n", psmouse->resolution); | ||
934 | } | ||
935 | |||
936 | static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, const char *buf, size_t count) | ||
937 | { | ||
938 | unsigned long value; | ||
939 | char *rest; | ||
940 | |||
941 | value = simple_strtoul(buf, &rest, 10); | ||
942 | if (*rest) | ||
943 | return -EINVAL; | ||
944 | |||
945 | psmouse->set_resolution(psmouse, value); | ||
946 | return count; | ||
947 | } | ||
948 | |||
949 | static ssize_t psmouse_attr_show_resetafter(struct psmouse *psmouse, char *buf) | ||
950 | { | ||
951 | return sprintf(buf, "%d\n", psmouse->resetafter); | ||
952 | } | ||
953 | |||
954 | static ssize_t psmouse_attr_set_resetafter(struct psmouse *psmouse, const char *buf, size_t count) | ||
955 | { | ||
956 | unsigned long value; | ||
957 | char *rest; | ||
958 | |||
959 | value = simple_strtoul(buf, &rest, 10); | ||
960 | if (*rest) | ||
961 | return -EINVAL; | ||
962 | |||
963 | psmouse->resetafter = value; | ||
964 | return count; | ||
965 | } | ||
966 | |||
967 | static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) | ||
968 | { | ||
969 | int i; | ||
970 | |||
971 | if (!val) | ||
972 | return -EINVAL; | ||
973 | |||
974 | if (!strncmp(val, "any", 3)) { | ||
975 | *((unsigned int *)kp->arg) = -1UL; | ||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | for (i = 0; i < ARRAY_SIZE(psmouse_proto_abbrev); i++) { | ||
980 | if (!psmouse_proto_abbrev[i]) | ||
981 | continue; | ||
982 | |||
983 | if (!strncmp(val, psmouse_proto_abbrev[i], strlen(psmouse_proto_abbrev[i]))) { | ||
984 | *((unsigned int *)kp->arg) = i; | ||
985 | return 0; | ||
986 | } | ||
987 | } | ||
988 | |||
989 | return -EINVAL; \ | ||
990 | } | ||
991 | |||
992 | static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) | ||
993 | { | ||
994 | return sprintf(buffer, "%s\n", | ||
995 | psmouse_max_proto < ARRAY_SIZE(psmouse_proto_abbrev) ? | ||
996 | psmouse_proto_abbrev[psmouse_max_proto] : "any"); | ||
997 | } | ||
998 | |||
999 | static int __init psmouse_init(void) | ||
1000 | { | ||
1001 | serio_register_driver(&psmouse_drv); | ||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | static void __exit psmouse_exit(void) | ||
1006 | { | ||
1007 | serio_unregister_driver(&psmouse_drv); | ||
1008 | } | ||
1009 | |||
1010 | module_init(psmouse_init); | ||
1011 | module_exit(psmouse_exit); | ||
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h new file mode 100644 index 000000000000..bda5b065d03c --- /dev/null +++ b/drivers/input/mouse/psmouse.h | |||
@@ -0,0 +1,106 @@ | |||
1 | #ifndef _PSMOUSE_H | ||
2 | #define _PSMOUSE_H | ||
3 | |||
4 | #define PSMOUSE_CMD_SETSCALE11 0x00e6 | ||
5 | #define PSMOUSE_CMD_SETSCALE21 0x00e7 | ||
6 | #define PSMOUSE_CMD_SETRES 0x10e8 | ||
7 | #define PSMOUSE_CMD_GETINFO 0x03e9 | ||
8 | #define PSMOUSE_CMD_SETSTREAM 0x00ea | ||
9 | #define PSMOUSE_CMD_SETPOLL 0x00f0 | ||
10 | #define PSMOUSE_CMD_POLL 0x03eb | ||
11 | #define PSMOUSE_CMD_GETID 0x02f2 | ||
12 | #define PSMOUSE_CMD_SETRATE 0x10f3 | ||
13 | #define PSMOUSE_CMD_ENABLE 0x00f4 | ||
14 | #define PSMOUSE_CMD_DISABLE 0x00f5 | ||
15 | #define PSMOUSE_CMD_RESET_DIS 0x00f6 | ||
16 | #define PSMOUSE_CMD_RESET_BAT 0x02ff | ||
17 | |||
18 | #define PSMOUSE_RET_BAT 0xaa | ||
19 | #define PSMOUSE_RET_ID 0x00 | ||
20 | #define PSMOUSE_RET_ACK 0xfa | ||
21 | #define PSMOUSE_RET_NAK 0xfe | ||
22 | |||
23 | enum psmouse_state { | ||
24 | PSMOUSE_IGNORE, | ||
25 | PSMOUSE_INITIALIZING, | ||
26 | PSMOUSE_CMD_MODE, | ||
27 | PSMOUSE_ACTIVATED, | ||
28 | }; | ||
29 | |||
30 | /* psmouse protocol handler return codes */ | ||
31 | typedef enum { | ||
32 | PSMOUSE_BAD_DATA, | ||
33 | PSMOUSE_GOOD_DATA, | ||
34 | PSMOUSE_FULL_PACKET | ||
35 | } psmouse_ret_t; | ||
36 | |||
37 | struct psmouse { | ||
38 | void *private; | ||
39 | struct input_dev dev; | ||
40 | struct ps2dev ps2dev; | ||
41 | char *vendor; | ||
42 | char *name; | ||
43 | unsigned char packet[8]; | ||
44 | unsigned char pktcnt; | ||
45 | unsigned char pktsize; | ||
46 | unsigned char type; | ||
47 | unsigned int model; | ||
48 | unsigned long last; | ||
49 | unsigned long out_of_sync; | ||
50 | enum psmouse_state state; | ||
51 | char devname[64]; | ||
52 | char phys[32]; | ||
53 | |||
54 | unsigned int rate; | ||
55 | unsigned int resolution; | ||
56 | unsigned int resetafter; | ||
57 | unsigned int smartscroll; /* Logitech only */ | ||
58 | |||
59 | psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs); | ||
60 | void (*set_rate)(struct psmouse *psmouse, unsigned int rate); | ||
61 | void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution); | ||
62 | |||
63 | int (*reconnect)(struct psmouse *psmouse); | ||
64 | void (*disconnect)(struct psmouse *psmouse); | ||
65 | |||
66 | void (*pt_activate)(struct psmouse *psmouse); | ||
67 | void (*pt_deactivate)(struct psmouse *psmouse); | ||
68 | }; | ||
69 | |||
70 | enum psmouse_type { | ||
71 | PSMOUSE_NONE, | ||
72 | PSMOUSE_PS2, | ||
73 | PSMOUSE_PS2PP, | ||
74 | PSMOUSE_THINKPS, | ||
75 | PSMOUSE_GENPS, | ||
76 | PSMOUSE_IMPS, | ||
77 | PSMOUSE_IMEX, | ||
78 | PSMOUSE_SYNAPTICS, | ||
79 | PSMOUSE_ALPS, | ||
80 | }; | ||
81 | |||
82 | int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); | ||
83 | int psmouse_reset(struct psmouse *psmouse); | ||
84 | void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); | ||
85 | |||
86 | ssize_t psmouse_attr_show_helper(struct device *dev, char *buf, | ||
87 | ssize_t (*handler)(struct psmouse *, char *)); | ||
88 | ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t count, | ||
89 | ssize_t (*handler)(struct psmouse *, const char *, size_t)); | ||
90 | |||
91 | #define PSMOUSE_DEFINE_ATTR(_name) \ | ||
92 | static ssize_t psmouse_attr_show_##_name(struct psmouse *, char *); \ | ||
93 | static ssize_t psmouse_attr_set_##_name(struct psmouse *, const char *, size_t);\ | ||
94 | static ssize_t psmouse_do_show_##_name(struct device *d, char *b) \ | ||
95 | { \ | ||
96 | return psmouse_attr_show_helper(d, b, psmouse_attr_show_##_name); \ | ||
97 | } \ | ||
98 | static ssize_t psmouse_do_set_##_name(struct device *d, const char *b, size_t s)\ | ||
99 | { \ | ||
100 | return psmouse_attr_set_helper(d, b, s, psmouse_attr_set_##_name); \ | ||
101 | } \ | ||
102 | static struct device_attribute psmouse_attr_##_name = \ | ||
103 | __ATTR(_name, S_IWUSR | S_IRUGO, \ | ||
104 | psmouse_do_show_##_name, psmouse_do_set_##_name); | ||
105 | |||
106 | #endif /* _PSMOUSE_H */ | ||
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c new file mode 100644 index 000000000000..7280f68afcee --- /dev/null +++ b/drivers/input/mouse/rpcmouse.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Acorn RiscPC mouse driver for Linux/ARM | ||
3 | * | ||
4 | * Copyright (c) 2000-2002 Vojtech Pavlik | ||
5 | * Copyright (C) 1996-2002 Russell King | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License version 2 as published by | ||
12 | * the Free Software Foundation. | ||
13 | * | ||
14 | * This handles the Acorn RiscPCs mouse. We basically have a couple of | ||
15 | * hardware registers that track the sensor count for the X-Y movement and | ||
16 | * another register holding the button state. On every VSYNC interrupt we read | ||
17 | * the complete state and then work out if something has changed. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/ptrace.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/input.h> | ||
26 | |||
27 | #include <asm/hardware.h> | ||
28 | #include <asm/irq.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/hardware/iomd.h> | ||
31 | |||
32 | MODULE_AUTHOR("Vojtech Pavlik, Russell King"); | ||
33 | MODULE_DESCRIPTION("Acorn RiscPC mouse driver"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | static short rpcmouse_lastx, rpcmouse_lasty; | ||
37 | |||
38 | static struct input_dev rpcmouse_dev = { | ||
39 | .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, | ||
40 | .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) }, | ||
41 | .relbit = { BIT(REL_X) | BIT(REL_Y) }, | ||
42 | .name = "Acorn RiscPC Mouse", | ||
43 | .phys = "rpcmouse/input0", | ||
44 | .id = { | ||
45 | .bustype = BUS_HOST, | ||
46 | .vendor = 0x0005, | ||
47 | .product = 0x0001, | ||
48 | .version = 0x0100, | ||
49 | }, | ||
50 | }; | ||
51 | |||
52 | static irqreturn_t rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
53 | { | ||
54 | struct input_dev *dev = dev_id; | ||
55 | short x, y, dx, dy, b; | ||
56 | |||
57 | x = (short) iomd_readl(IOMD_MOUSEX); | ||
58 | y = (short) iomd_readl(IOMD_MOUSEY); | ||
59 | b = (short) (__raw_readl(0xe0310000) ^ 0x70); | ||
60 | |||
61 | dx = x - rpcmouse_lastx; | ||
62 | dy = y - rpcmouse_lasty; | ||
63 | |||
64 | rpcmouse_lastx = x; | ||
65 | rpcmouse_lasty = y; | ||
66 | |||
67 | input_regs(dev, regs); | ||
68 | |||
69 | input_report_rel(dev, REL_X, dx); | ||
70 | input_report_rel(dev, REL_Y, -dy); | ||
71 | |||
72 | input_report_key(dev, BTN_LEFT, b & 0x40); | ||
73 | input_report_key(dev, BTN_MIDDLE, b & 0x20); | ||
74 | input_report_key(dev, BTN_RIGHT, b & 0x10); | ||
75 | |||
76 | input_sync(dev); | ||
77 | |||
78 | return IRQ_HANDLED; | ||
79 | } | ||
80 | |||
81 | static int __init rpcmouse_init(void) | ||
82 | { | ||
83 | init_input_dev(&rpcmouse_dev); | ||
84 | |||
85 | rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX); | ||
86 | rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY); | ||
87 | |||
88 | if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, SA_SHIRQ, "rpcmouse", &rpcmouse_dev)) { | ||
89 | printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n"); | ||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | input_register_device(&rpcmouse_dev); | ||
94 | |||
95 | printk(KERN_INFO "input: Acorn RiscPC mouse\n"); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static void __exit rpcmouse_exit(void) | ||
101 | { | ||
102 | input_unregister_device(&rpcmouse_dev); | ||
103 | free_irq(IRQ_VSYNCPULSE, &rpcmouse_dev); | ||
104 | } | ||
105 | |||
106 | module_init(rpcmouse_init); | ||
107 | module_exit(rpcmouse_exit); | ||
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c new file mode 100644 index 000000000000..d12b93ae3900 --- /dev/null +++ b/drivers/input/mouse/sermouse.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | * $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $ | ||
3 | * | ||
4 | * Copyright (c) 1999-2001 Vojtech Pavlik | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Serial mouse driver for Linux | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | * | ||
26 | * Should you need to contact me, the author, you can do so either by | ||
27 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
28 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
29 | */ | ||
30 | |||
31 | #include <linux/delay.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/input.h> | ||
36 | #include <linux/config.h> | ||
37 | #include <linux/serio.h> | ||
38 | #include <linux/init.h> | ||
39 | |||
40 | #define DRIVER_DESC "Serial mouse driver" | ||
41 | |||
42 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
43 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | |||
46 | static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", | ||
47 | "Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse", | ||
48 | "Logitech MZ++ Mouse"}; | ||
49 | |||
50 | struct sermouse { | ||
51 | struct input_dev dev; | ||
52 | signed char buf[8]; | ||
53 | unsigned char count; | ||
54 | unsigned char type; | ||
55 | unsigned long last; | ||
56 | char phys[32]; | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and | ||
61 | * applies some prediction to the data, resulting in 96 updates per | ||
62 | * second, which is as good as a PS/2 or USB mouse. | ||
63 | */ | ||
64 | |||
65 | static void sermouse_process_msc(struct sermouse *sermouse, signed char data, struct pt_regs *regs) | ||
66 | { | ||
67 | struct input_dev *dev = &sermouse->dev; | ||
68 | signed char *buf = sermouse->buf; | ||
69 | |||
70 | input_regs(dev, regs); | ||
71 | |||
72 | switch (sermouse->count) { | ||
73 | |||
74 | case 0: | ||
75 | if ((data & 0xf8) != 0x80) return; | ||
76 | input_report_key(dev, BTN_LEFT, !(data & 4)); | ||
77 | input_report_key(dev, BTN_RIGHT, !(data & 1)); | ||
78 | input_report_key(dev, BTN_MIDDLE, !(data & 2)); | ||
79 | break; | ||
80 | |||
81 | case 1: | ||
82 | case 3: | ||
83 | input_report_rel(dev, REL_X, data / 2); | ||
84 | input_report_rel(dev, REL_Y, -buf[1]); | ||
85 | buf[0] = data - data / 2; | ||
86 | break; | ||
87 | |||
88 | case 2: | ||
89 | case 4: | ||
90 | input_report_rel(dev, REL_X, buf[0]); | ||
91 | input_report_rel(dev, REL_Y, buf[1] - data); | ||
92 | buf[1] = data / 2; | ||
93 | break; | ||
94 | } | ||
95 | |||
96 | input_sync(dev); | ||
97 | |||
98 | if (++sermouse->count == (5 - ((sermouse->type == SERIO_SUN) << 1))) | ||
99 | sermouse->count = 0; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and | ||
104 | * generates events. With prediction it gets 80 updates/sec, assuming | ||
105 | * standard 3-byte packets and 1200 bps. | ||
106 | */ | ||
107 | |||
108 | static void sermouse_process_ms(struct sermouse *sermouse, signed char data, struct pt_regs *regs) | ||
109 | { | ||
110 | struct input_dev *dev = &sermouse->dev; | ||
111 | signed char *buf = sermouse->buf; | ||
112 | |||
113 | if (data & 0x40) sermouse->count = 0; | ||
114 | |||
115 | input_regs(dev, regs); | ||
116 | |||
117 | switch (sermouse->count) { | ||
118 | |||
119 | case 0: | ||
120 | buf[1] = data; | ||
121 | input_report_key(dev, BTN_LEFT, (data >> 5) & 1); | ||
122 | input_report_key(dev, BTN_RIGHT, (data >> 4) & 1); | ||
123 | break; | ||
124 | |||
125 | case 1: | ||
126 | buf[2] = data; | ||
127 | data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f)); | ||
128 | input_report_rel(dev, REL_X, data / 2); | ||
129 | input_report_rel(dev, REL_Y, buf[4]); | ||
130 | buf[3] = data - data / 2; | ||
131 | break; | ||
132 | |||
133 | case 2: | ||
134 | /* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */ | ||
135 | if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1])) | ||
136 | input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key)); | ||
137 | buf[0] = buf[1]; | ||
138 | |||
139 | data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f)); | ||
140 | input_report_rel(dev, REL_X, buf[3]); | ||
141 | input_report_rel(dev, REL_Y, data - buf[4]); | ||
142 | buf[4] = data / 2; | ||
143 | break; | ||
144 | |||
145 | case 3: | ||
146 | |||
147 | switch (sermouse->type) { | ||
148 | |||
149 | case SERIO_MS: | ||
150 | sermouse->type = SERIO_MP; | ||
151 | |||
152 | case SERIO_MP: | ||
153 | if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */ | ||
154 | input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1); | ||
155 | input_report_key(dev, BTN_SIDE, (data >> 4) & 1); | ||
156 | break; | ||
157 | |||
158 | case SERIO_MZP: | ||
159 | case SERIO_MZPP: | ||
160 | input_report_key(dev, BTN_SIDE, (data >> 5) & 1); | ||
161 | |||
162 | case SERIO_MZ: | ||
163 | input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1); | ||
164 | input_report_rel(dev, REL_WHEEL, (data & 8) - (data & 7)); | ||
165 | break; | ||
166 | } | ||
167 | |||
168 | break; | ||
169 | |||
170 | case 4: | ||
171 | case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */ | ||
172 | buf[1] = (data >> 2) & 0x0f; | ||
173 | break; | ||
174 | |||
175 | case 5: | ||
176 | case 7: /* Ignore anything besides MZ++ */ | ||
177 | if (sermouse->type != SERIO_MZPP) break; | ||
178 | |||
179 | switch (buf[1]) { | ||
180 | |||
181 | case 1: /* Extra mouse info */ | ||
182 | |||
183 | input_report_key(dev, BTN_SIDE, (data >> 4) & 1); | ||
184 | input_report_key(dev, BTN_EXTRA, (data >> 5) & 1); | ||
185 | input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8)); | ||
186 | |||
187 | break; | ||
188 | |||
189 | default: /* We don't decode anything else yet. */ | ||
190 | |||
191 | printk(KERN_WARNING | ||
192 | "sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]); | ||
193 | break; | ||
194 | } | ||
195 | |||
196 | break; | ||
197 | } | ||
198 | |||
199 | input_sync(dev); | ||
200 | |||
201 | sermouse->count++; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * sermouse_interrupt() handles incoming characters, either gathering them into | ||
206 | * packets or passing them to the command routine as command output. | ||
207 | */ | ||
208 | |||
209 | static irqreturn_t sermouse_interrupt(struct serio *serio, | ||
210 | unsigned char data, unsigned int flags, struct pt_regs *regs) | ||
211 | { | ||
212 | struct sermouse *sermouse = serio_get_drvdata(serio); | ||
213 | |||
214 | if (time_after(jiffies, sermouse->last + HZ/10)) sermouse->count = 0; | ||
215 | sermouse->last = jiffies; | ||
216 | |||
217 | if (sermouse->type > SERIO_SUN) | ||
218 | sermouse_process_ms(sermouse, data, regs); | ||
219 | else | ||
220 | sermouse_process_msc(sermouse, data, regs); | ||
221 | return IRQ_HANDLED; | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * sermouse_disconnect() cleans up after we don't want talk | ||
226 | * to the mouse anymore. | ||
227 | */ | ||
228 | |||
229 | static void sermouse_disconnect(struct serio *serio) | ||
230 | { | ||
231 | struct sermouse *sermouse = serio_get_drvdata(serio); | ||
232 | |||
233 | input_unregister_device(&sermouse->dev); | ||
234 | serio_close(serio); | ||
235 | serio_set_drvdata(serio, NULL); | ||
236 | kfree(sermouse); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * sermouse_connect() is a callback form the serio module when | ||
241 | * an unhandled serio port is found. | ||
242 | */ | ||
243 | |||
244 | static int sermouse_connect(struct serio *serio, struct serio_driver *drv) | ||
245 | { | ||
246 | struct sermouse *sermouse; | ||
247 | unsigned char c; | ||
248 | int err; | ||
249 | |||
250 | if (!serio->id.proto || serio->id.proto > SERIO_MZPP) | ||
251 | return -ENODEV; | ||
252 | |||
253 | if (!(sermouse = kmalloc(sizeof(struct sermouse), GFP_KERNEL))) | ||
254 | return -ENOMEM; | ||
255 | |||
256 | memset(sermouse, 0, sizeof(struct sermouse)); | ||
257 | |||
258 | init_input_dev(&sermouse->dev); | ||
259 | sermouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
260 | sermouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); | ||
261 | sermouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
262 | sermouse->dev.private = sermouse; | ||
263 | |||
264 | sermouse->type = serio->id.proto; | ||
265 | c = serio->id.extra; | ||
266 | |||
267 | if (c & 0x01) set_bit(BTN_MIDDLE, sermouse->dev.keybit); | ||
268 | if (c & 0x02) set_bit(BTN_SIDE, sermouse->dev.keybit); | ||
269 | if (c & 0x04) set_bit(BTN_EXTRA, sermouse->dev.keybit); | ||
270 | if (c & 0x10) set_bit(REL_WHEEL, sermouse->dev.relbit); | ||
271 | if (c & 0x20) set_bit(REL_HWHEEL, sermouse->dev.relbit); | ||
272 | |||
273 | sprintf(sermouse->phys, "%s/input0", serio->phys); | ||
274 | |||
275 | sermouse->dev.name = sermouse_protocols[sermouse->type]; | ||
276 | sermouse->dev.phys = sermouse->phys; | ||
277 | sermouse->dev.id.bustype = BUS_RS232; | ||
278 | sermouse->dev.id.vendor = sermouse->type; | ||
279 | sermouse->dev.id.product = c; | ||
280 | sermouse->dev.id.version = 0x0100; | ||
281 | sermouse->dev.dev = &serio->dev; | ||
282 | |||
283 | serio_set_drvdata(serio, sermouse); | ||
284 | |||
285 | err = serio_open(serio, drv); | ||
286 | if (err) { | ||
287 | serio_set_drvdata(serio, NULL); | ||
288 | kfree(sermouse); | ||
289 | return err; | ||
290 | } | ||
291 | |||
292 | input_register_device(&sermouse->dev); | ||
293 | |||
294 | printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static struct serio_device_id sermouse_serio_ids[] = { | ||
300 | { | ||
301 | .type = SERIO_RS232, | ||
302 | .proto = SERIO_MSC, | ||
303 | .id = SERIO_ANY, | ||
304 | .extra = SERIO_ANY, | ||
305 | }, | ||
306 | { | ||
307 | .type = SERIO_RS232, | ||
308 | .proto = SERIO_SUN, | ||
309 | .id = SERIO_ANY, | ||
310 | .extra = SERIO_ANY, | ||
311 | }, | ||
312 | { | ||
313 | .type = SERIO_RS232, | ||
314 | .proto = SERIO_MS, | ||
315 | .id = SERIO_ANY, | ||
316 | .extra = SERIO_ANY, | ||
317 | }, | ||
318 | { | ||
319 | .type = SERIO_RS232, | ||
320 | .proto = SERIO_MP, | ||
321 | .id = SERIO_ANY, | ||
322 | .extra = SERIO_ANY, | ||
323 | }, | ||
324 | { | ||
325 | .type = SERIO_RS232, | ||
326 | .proto = SERIO_MZ, | ||
327 | .id = SERIO_ANY, | ||
328 | .extra = SERIO_ANY, | ||
329 | }, | ||
330 | { | ||
331 | .type = SERIO_RS232, | ||
332 | .proto = SERIO_MZP, | ||
333 | .id = SERIO_ANY, | ||
334 | .extra = SERIO_ANY, | ||
335 | }, | ||
336 | { | ||
337 | .type = SERIO_RS232, | ||
338 | .proto = SERIO_MZPP, | ||
339 | .id = SERIO_ANY, | ||
340 | .extra = SERIO_ANY, | ||
341 | }, | ||
342 | { 0 } | ||
343 | }; | ||
344 | |||
345 | MODULE_DEVICE_TABLE(serio, sermouse_serio_ids); | ||
346 | |||
347 | static struct serio_driver sermouse_drv = { | ||
348 | .driver = { | ||
349 | .name = "sermouse", | ||
350 | }, | ||
351 | .description = DRIVER_DESC, | ||
352 | .id_table = sermouse_serio_ids, | ||
353 | .interrupt = sermouse_interrupt, | ||
354 | .connect = sermouse_connect, | ||
355 | .disconnect = sermouse_disconnect, | ||
356 | }; | ||
357 | |||
358 | static int __init sermouse_init(void) | ||
359 | { | ||
360 | serio_register_driver(&sermouse_drv); | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static void __exit sermouse_exit(void) | ||
365 | { | ||
366 | serio_unregister_driver(&sermouse_drv); | ||
367 | } | ||
368 | |||
369 | module_init(sermouse_init); | ||
370 | module_exit(sermouse_exit); | ||
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c new file mode 100644 index 000000000000..69832f8fb720 --- /dev/null +++ b/drivers/input/mouse/synaptics.c | |||
@@ -0,0 +1,700 @@ | |||
1 | /* | ||
2 | * Synaptics TouchPad PS/2 mouse driver | ||
3 | * | ||
4 | * 2003 Dmitry Torokhov <dtor@mail.ru> | ||
5 | * Added support for pass-through port. Special thanks to Peter Berg Larsen | ||
6 | * for explaining various Synaptics quirks. | ||
7 | * | ||
8 | * 2003 Peter Osterlund <petero2@telia.com> | ||
9 | * Ported to 2.5 input device infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch> | ||
12 | * start merging tpconfig and gpm code to a xfree-input module | ||
13 | * adding some changes and extensions (ex. 3rd and 4th button) | ||
14 | * | ||
15 | * Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu> | ||
16 | * Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com> | ||
17 | * code for the special synaptics commands (from the tpconfig-source) | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify it | ||
20 | * under the terms of the GNU General Public License version 2 as published by | ||
21 | * the Free Software Foundation. | ||
22 | * | ||
23 | * Trademarks are the property of their respective owners. | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/input.h> | ||
28 | #include <linux/serio.h> | ||
29 | #include <linux/libps2.h> | ||
30 | #include "psmouse.h" | ||
31 | #include "synaptics.h" | ||
32 | |||
33 | /* | ||
34 | * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, | ||
35 | * section 2.3.2, which says that they should be valid regardless of the | ||
36 | * actual size of the sensor. | ||
37 | */ | ||
38 | #define XMIN_NOMINAL 1472 | ||
39 | #define XMAX_NOMINAL 5472 | ||
40 | #define YMIN_NOMINAL 1408 | ||
41 | #define YMAX_NOMINAL 4448 | ||
42 | |||
43 | /***************************************************************************** | ||
44 | * Synaptics communications functions | ||
45 | ****************************************************************************/ | ||
46 | |||
47 | /* | ||
48 | * Send a command to the synpatics touchpad by special commands | ||
49 | */ | ||
50 | static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) | ||
51 | { | ||
52 | if (psmouse_sliced_command(psmouse, c)) | ||
53 | return -1; | ||
54 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
55 | return -1; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Set the synaptics touchpad mode byte by special commands | ||
61 | */ | ||
62 | static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) | ||
63 | { | ||
64 | unsigned char param[1]; | ||
65 | |||
66 | if (psmouse_sliced_command(psmouse, mode)) | ||
67 | return -1; | ||
68 | param[0] = SYN_PS_SET_MODE2; | ||
69 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE)) | ||
70 | return -1; | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * Read the model-id bytes from the touchpad | ||
76 | * see also SYN_MODEL_* macros | ||
77 | */ | ||
78 | static int synaptics_model_id(struct psmouse *psmouse) | ||
79 | { | ||
80 | struct synaptics_data *priv = psmouse->private; | ||
81 | unsigned char mi[3]; | ||
82 | |||
83 | if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi)) | ||
84 | return -1; | ||
85 | priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2]; | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Read the capability-bits from the touchpad | ||
91 | * see also the SYN_CAP_* macros | ||
92 | */ | ||
93 | static int synaptics_capability(struct psmouse *psmouse) | ||
94 | { | ||
95 | struct synaptics_data *priv = psmouse->private; | ||
96 | unsigned char cap[3]; | ||
97 | |||
98 | if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) | ||
99 | return -1; | ||
100 | priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | ||
101 | priv->ext_cap = 0; | ||
102 | if (!SYN_CAP_VALID(priv->capabilities)) | ||
103 | return -1; | ||
104 | |||
105 | /* | ||
106 | * Unless capExtended is set the rest of the flags should be ignored | ||
107 | */ | ||
108 | if (!SYN_CAP_EXTENDED(priv->capabilities)) | ||
109 | priv->capabilities = 0; | ||
110 | |||
111 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { | ||
112 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { | ||
113 | printk(KERN_ERR "Synaptics claims to have extended capabilities," | ||
114 | " but I'm not able to read them."); | ||
115 | } else { | ||
116 | priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | ||
117 | |||
118 | /* | ||
119 | * if nExtBtn is greater than 8 it should be considered | ||
120 | * invalid and treated as 0 | ||
121 | */ | ||
122 | if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8) | ||
123 | priv->ext_cap &= 0xff0fff; | ||
124 | } | ||
125 | } | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Identify Touchpad | ||
131 | * See also the SYN_ID_* macros | ||
132 | */ | ||
133 | static int synaptics_identify(struct psmouse *psmouse) | ||
134 | { | ||
135 | struct synaptics_data *priv = psmouse->private; | ||
136 | unsigned char id[3]; | ||
137 | |||
138 | if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id)) | ||
139 | return -1; | ||
140 | priv->identity = (id[0]<<16) | (id[1]<<8) | id[2]; | ||
141 | if (SYN_ID_IS_SYNAPTICS(priv->identity)) | ||
142 | return 0; | ||
143 | return -1; | ||
144 | } | ||
145 | |||
146 | static void print_ident(struct synaptics_data *priv) | ||
147 | { | ||
148 | printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity)); | ||
149 | printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity), | ||
150 | SYN_ID_MINOR(priv->identity)); | ||
151 | if (SYN_MODEL_ROT180(priv->model_id)) | ||
152 | printk(KERN_INFO " 180 degree mounted touchpad\n"); | ||
153 | if (SYN_MODEL_PORTRAIT(priv->model_id)) | ||
154 | printk(KERN_INFO " portrait touchpad\n"); | ||
155 | printk(KERN_INFO " Sensor: %ld\n", SYN_MODEL_SENSOR(priv->model_id)); | ||
156 | if (SYN_MODEL_NEWABS(priv->model_id)) | ||
157 | printk(KERN_INFO " new absolute packet format\n"); | ||
158 | if (SYN_MODEL_PEN(priv->model_id)) | ||
159 | printk(KERN_INFO " pen detection\n"); | ||
160 | |||
161 | if (SYN_CAP_EXTENDED(priv->capabilities)) { | ||
162 | printk(KERN_INFO " Touchpad has extended capability bits\n"); | ||
163 | if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) | ||
164 | printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n", | ||
165 | (int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))); | ||
166 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | ||
167 | printk(KERN_INFO " -> middle button\n"); | ||
168 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) | ||
169 | printk(KERN_INFO " -> four buttons\n"); | ||
170 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) | ||
171 | printk(KERN_INFO " -> multifinger detection\n"); | ||
172 | if (SYN_CAP_PALMDETECT(priv->capabilities)) | ||
173 | printk(KERN_INFO " -> palm detection\n"); | ||
174 | if (SYN_CAP_PASS_THROUGH(priv->capabilities)) | ||
175 | printk(KERN_INFO " -> pass-through port\n"); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | static int synaptics_query_hardware(struct psmouse *psmouse) | ||
180 | { | ||
181 | int retries = 0; | ||
182 | |||
183 | while ((retries++ < 3) && psmouse_reset(psmouse)) | ||
184 | printk(KERN_ERR "synaptics reset failed\n"); | ||
185 | |||
186 | if (synaptics_identify(psmouse)) | ||
187 | return -1; | ||
188 | if (synaptics_model_id(psmouse)) | ||
189 | return -1; | ||
190 | if (synaptics_capability(psmouse)) | ||
191 | return -1; | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int synaptics_set_absolute_mode(struct psmouse *psmouse) | ||
197 | { | ||
198 | struct synaptics_data *priv = psmouse->private; | ||
199 | |||
200 | priv->mode = SYN_BIT_ABSOLUTE_MODE; | ||
201 | if (SYN_ID_MAJOR(priv->identity) >= 4) | ||
202 | priv->mode |= SYN_BIT_DISABLE_GESTURE; | ||
203 | if (SYN_CAP_EXTENDED(priv->capabilities)) | ||
204 | priv->mode |= SYN_BIT_W_MODE; | ||
205 | |||
206 | if (synaptics_mode_cmd(psmouse, priv->mode)) | ||
207 | return -1; | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate) | ||
213 | { | ||
214 | struct synaptics_data *priv = psmouse->private; | ||
215 | |||
216 | if (rate >= 80) { | ||
217 | priv->mode |= SYN_BIT_HIGH_RATE; | ||
218 | psmouse->rate = 80; | ||
219 | } else { | ||
220 | priv->mode &= ~SYN_BIT_HIGH_RATE; | ||
221 | psmouse->rate = 40; | ||
222 | } | ||
223 | |||
224 | synaptics_mode_cmd(psmouse, priv->mode); | ||
225 | } | ||
226 | |||
227 | /***************************************************************************** | ||
228 | * Synaptics pass-through PS/2 port support | ||
229 | ****************************************************************************/ | ||
230 | static int synaptics_pt_write(struct serio *serio, unsigned char c) | ||
231 | { | ||
232 | struct psmouse *parent = serio_get_drvdata(serio->parent); | ||
233 | char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ | ||
234 | |||
235 | if (psmouse_sliced_command(parent, c)) | ||
236 | return -1; | ||
237 | if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE)) | ||
238 | return -1; | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static inline int synaptics_is_pt_packet(unsigned char *buf) | ||
243 | { | ||
244 | return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; | ||
245 | } | ||
246 | |||
247 | static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) | ||
248 | { | ||
249 | struct psmouse *child = serio_get_drvdata(ptport); | ||
250 | |||
251 | if (child && child->state == PSMOUSE_ACTIVATED) { | ||
252 | serio_interrupt(ptport, packet[1], 0, NULL); | ||
253 | serio_interrupt(ptport, packet[4], 0, NULL); | ||
254 | serio_interrupt(ptport, packet[5], 0, NULL); | ||
255 | if (child->type >= PSMOUSE_GENPS) | ||
256 | serio_interrupt(ptport, packet[2], 0, NULL); | ||
257 | } else | ||
258 | serio_interrupt(ptport, packet[1], 0, NULL); | ||
259 | } | ||
260 | |||
261 | static void synaptics_pt_activate(struct psmouse *psmouse) | ||
262 | { | ||
263 | struct serio *ptport = psmouse->ps2dev.serio->child; | ||
264 | struct psmouse *child = serio_get_drvdata(ptport); | ||
265 | struct synaptics_data *priv = psmouse->private; | ||
266 | |||
267 | /* adjust the touchpad to child's choice of protocol */ | ||
268 | if (child) { | ||
269 | if (child->type >= PSMOUSE_GENPS) | ||
270 | priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT; | ||
271 | else | ||
272 | priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT; | ||
273 | |||
274 | if (synaptics_mode_cmd(psmouse, priv->mode)) | ||
275 | printk(KERN_INFO "synaptics: failed to switch guest protocol\n"); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | static void synaptics_pt_create(struct psmouse *psmouse) | ||
280 | { | ||
281 | struct serio *serio; | ||
282 | |||
283 | serio = kmalloc(sizeof(struct serio), GFP_KERNEL); | ||
284 | if (!serio) { | ||
285 | printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); | ||
286 | return; | ||
287 | } | ||
288 | |||
289 | memset(serio, 0, sizeof(struct serio)); | ||
290 | |||
291 | serio->id.type = SERIO_PS_PSTHRU; | ||
292 | strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); | ||
293 | strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); | ||
294 | serio->write = synaptics_pt_write; | ||
295 | serio->parent = psmouse->ps2dev.serio; | ||
296 | |||
297 | psmouse->pt_activate = synaptics_pt_activate; | ||
298 | |||
299 | printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys); | ||
300 | serio_register_port(serio); | ||
301 | } | ||
302 | |||
303 | /***************************************************************************** | ||
304 | * Functions to interpret the absolute mode packets | ||
305 | ****************************************************************************/ | ||
306 | |||
307 | static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) | ||
308 | { | ||
309 | memset(hw, 0, sizeof(struct synaptics_hw_state)); | ||
310 | |||
311 | if (SYN_MODEL_NEWABS(priv->model_id)) { | ||
312 | hw->x = (((buf[3] & 0x10) << 8) | | ||
313 | ((buf[1] & 0x0f) << 8) | | ||
314 | buf[4]); | ||
315 | hw->y = (((buf[3] & 0x20) << 7) | | ||
316 | ((buf[1] & 0xf0) << 4) | | ||
317 | buf[5]); | ||
318 | |||
319 | hw->z = buf[2]; | ||
320 | hw->w = (((buf[0] & 0x30) >> 2) | | ||
321 | ((buf[0] & 0x04) >> 1) | | ||
322 | ((buf[3] & 0x04) >> 2)); | ||
323 | |||
324 | hw->left = (buf[0] & 0x01) ? 1 : 0; | ||
325 | hw->right = (buf[0] & 0x02) ? 1 : 0; | ||
326 | |||
327 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | ||
328 | hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | ||
329 | if (hw->w == 2) | ||
330 | hw->scroll = (signed char)(buf[1]); | ||
331 | } | ||
332 | |||
333 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | ||
334 | hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | ||
335 | hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; | ||
336 | } | ||
337 | |||
338 | if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && | ||
339 | ((buf[0] ^ buf[3]) & 0x02)) { | ||
340 | switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { | ||
341 | default: | ||
342 | /* | ||
343 | * if nExtBtn is greater than 8 it should be | ||
344 | * considered invalid and treated as 0 | ||
345 | */ | ||
346 | break; | ||
347 | case 8: | ||
348 | hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; | ||
349 | hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; | ||
350 | case 6: | ||
351 | hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; | ||
352 | hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; | ||
353 | case 4: | ||
354 | hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; | ||
355 | hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; | ||
356 | case 2: | ||
357 | hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; | ||
358 | hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; | ||
359 | } | ||
360 | } | ||
361 | } else { | ||
362 | hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); | ||
363 | hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); | ||
364 | |||
365 | hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); | ||
366 | hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); | ||
367 | |||
368 | hw->left = (buf[0] & 0x01) ? 1 : 0; | ||
369 | hw->right = (buf[0] & 0x02) ? 1 : 0; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * called for each full received packet from the touchpad | ||
375 | */ | ||
376 | static void synaptics_process_packet(struct psmouse *psmouse) | ||
377 | { | ||
378 | struct input_dev *dev = &psmouse->dev; | ||
379 | struct synaptics_data *priv = psmouse->private; | ||
380 | struct synaptics_hw_state hw; | ||
381 | int num_fingers; | ||
382 | int finger_width; | ||
383 | int i; | ||
384 | |||
385 | synaptics_parse_hw_state(psmouse->packet, priv, &hw); | ||
386 | |||
387 | if (hw.scroll) { | ||
388 | priv->scroll += hw.scroll; | ||
389 | |||
390 | while (priv->scroll >= 4) { | ||
391 | input_report_key(dev, BTN_BACK, !hw.down); | ||
392 | input_sync(dev); | ||
393 | input_report_key(dev, BTN_BACK, hw.down); | ||
394 | input_sync(dev); | ||
395 | priv->scroll -= 4; | ||
396 | } | ||
397 | while (priv->scroll <= -4) { | ||
398 | input_report_key(dev, BTN_FORWARD, !hw.up); | ||
399 | input_sync(dev); | ||
400 | input_report_key(dev, BTN_FORWARD, hw.up); | ||
401 | input_sync(dev); | ||
402 | priv->scroll += 4; | ||
403 | } | ||
404 | return; | ||
405 | } | ||
406 | |||
407 | if (hw.z > 0) { | ||
408 | num_fingers = 1; | ||
409 | finger_width = 5; | ||
410 | if (SYN_CAP_EXTENDED(priv->capabilities)) { | ||
411 | switch (hw.w) { | ||
412 | case 0 ... 1: | ||
413 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) | ||
414 | num_fingers = hw.w + 2; | ||
415 | break; | ||
416 | case 2: | ||
417 | if (SYN_MODEL_PEN(priv->model_id)) | ||
418 | ; /* Nothing, treat a pen as a single finger */ | ||
419 | break; | ||
420 | case 4 ... 15: | ||
421 | if (SYN_CAP_PALMDETECT(priv->capabilities)) | ||
422 | finger_width = hw.w; | ||
423 | break; | ||
424 | } | ||
425 | } | ||
426 | } else { | ||
427 | num_fingers = 0; | ||
428 | finger_width = 0; | ||
429 | } | ||
430 | |||
431 | /* Post events | ||
432 | * BTN_TOUCH has to be first as mousedev relies on it when doing | ||
433 | * absolute -> relative conversion | ||
434 | */ | ||
435 | if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1); | ||
436 | if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); | ||
437 | |||
438 | if (hw.z > 0) { | ||
439 | input_report_abs(dev, ABS_X, hw.x); | ||
440 | input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y); | ||
441 | } | ||
442 | input_report_abs(dev, ABS_PRESSURE, hw.z); | ||
443 | |||
444 | input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); | ||
445 | input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); | ||
446 | input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); | ||
447 | input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); | ||
448 | |||
449 | input_report_key(dev, BTN_LEFT, hw.left); | ||
450 | input_report_key(dev, BTN_RIGHT, hw.right); | ||
451 | |||
452 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | ||
453 | input_report_key(dev, BTN_MIDDLE, hw.middle); | ||
454 | |||
455 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | ||
456 | input_report_key(dev, BTN_FORWARD, hw.up); | ||
457 | input_report_key(dev, BTN_BACK, hw.down); | ||
458 | } | ||
459 | |||
460 | for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | ||
461 | input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i)); | ||
462 | |||
463 | input_sync(dev); | ||
464 | } | ||
465 | |||
466 | static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type) | ||
467 | { | ||
468 | static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; | ||
469 | static unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; | ||
470 | static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; | ||
471 | static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; | ||
472 | static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; | ||
473 | |||
474 | if (idx < 0 || idx > 4) | ||
475 | return 0; | ||
476 | |||
477 | switch (pkt_type) { | ||
478 | case SYN_NEWABS: | ||
479 | case SYN_NEWABS_RELAXED: | ||
480 | return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; | ||
481 | |||
482 | case SYN_NEWABS_STRICT: | ||
483 | return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; | ||
484 | |||
485 | case SYN_OLDABS: | ||
486 | return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; | ||
487 | |||
488 | default: | ||
489 | printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); | ||
490 | return 0; | ||
491 | } | ||
492 | } | ||
493 | |||
494 | static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse) | ||
495 | { | ||
496 | int i; | ||
497 | |||
498 | for (i = 0; i < 5; i++) | ||
499 | if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) { | ||
500 | printk(KERN_INFO "synaptics: using relaxed packet validation\n"); | ||
501 | return SYN_NEWABS_RELAXED; | ||
502 | } | ||
503 | |||
504 | return SYN_NEWABS_STRICT; | ||
505 | } | ||
506 | |||
507 | static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs) | ||
508 | { | ||
509 | struct input_dev *dev = &psmouse->dev; | ||
510 | struct synaptics_data *priv = psmouse->private; | ||
511 | |||
512 | input_regs(dev, regs); | ||
513 | |||
514 | if (psmouse->pktcnt >= 6) { /* Full packet received */ | ||
515 | if (unlikely(priv->pkt_type == SYN_NEWABS)) | ||
516 | priv->pkt_type = synaptics_detect_pkt_type(psmouse); | ||
517 | |||
518 | if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { | ||
519 | if (psmouse->ps2dev.serio->child) | ||
520 | synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); | ||
521 | } else | ||
522 | synaptics_process_packet(psmouse); | ||
523 | |||
524 | return PSMOUSE_FULL_PACKET; | ||
525 | } | ||
526 | |||
527 | return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ? | ||
528 | PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | ||
529 | } | ||
530 | |||
531 | /***************************************************************************** | ||
532 | * Driver initialization/cleanup functions | ||
533 | ****************************************************************************/ | ||
534 | static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | ||
535 | { | ||
536 | int i; | ||
537 | |||
538 | set_bit(EV_ABS, dev->evbit); | ||
539 | input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); | ||
540 | input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); | ||
541 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); | ||
542 | set_bit(ABS_TOOL_WIDTH, dev->absbit); | ||
543 | |||
544 | set_bit(EV_KEY, dev->evbit); | ||
545 | set_bit(BTN_TOUCH, dev->keybit); | ||
546 | set_bit(BTN_TOOL_FINGER, dev->keybit); | ||
547 | set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); | ||
548 | set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); | ||
549 | |||
550 | set_bit(BTN_LEFT, dev->keybit); | ||
551 | set_bit(BTN_RIGHT, dev->keybit); | ||
552 | |||
553 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | ||
554 | set_bit(BTN_MIDDLE, dev->keybit); | ||
555 | |||
556 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || | ||
557 | SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | ||
558 | set_bit(BTN_FORWARD, dev->keybit); | ||
559 | set_bit(BTN_BACK, dev->keybit); | ||
560 | } | ||
561 | |||
562 | for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | ||
563 | set_bit(BTN_0 + i, dev->keybit); | ||
564 | |||
565 | clear_bit(EV_REL, dev->evbit); | ||
566 | clear_bit(REL_X, dev->relbit); | ||
567 | clear_bit(REL_Y, dev->relbit); | ||
568 | } | ||
569 | |||
570 | void synaptics_reset(struct psmouse *psmouse) | ||
571 | { | ||
572 | /* reset touchpad back to relative mode, gestures enabled */ | ||
573 | synaptics_mode_cmd(psmouse, 0); | ||
574 | } | ||
575 | |||
576 | static void synaptics_disconnect(struct psmouse *psmouse) | ||
577 | { | ||
578 | synaptics_reset(psmouse); | ||
579 | kfree(psmouse->private); | ||
580 | psmouse->private = NULL; | ||
581 | } | ||
582 | |||
583 | static int synaptics_reconnect(struct psmouse *psmouse) | ||
584 | { | ||
585 | struct synaptics_data *priv = psmouse->private; | ||
586 | struct synaptics_data old_priv = *priv; | ||
587 | |||
588 | if (synaptics_detect(psmouse, 0)) | ||
589 | return -1; | ||
590 | |||
591 | if (synaptics_query_hardware(psmouse)) { | ||
592 | printk(KERN_ERR "Unable to query Synaptics hardware.\n"); | ||
593 | return -1; | ||
594 | } | ||
595 | |||
596 | if (old_priv.identity != priv->identity || | ||
597 | old_priv.model_id != priv->model_id || | ||
598 | old_priv.capabilities != priv->capabilities || | ||
599 | old_priv.ext_cap != priv->ext_cap) | ||
600 | return -1; | ||
601 | |||
602 | if (synaptics_set_absolute_mode(psmouse)) { | ||
603 | printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); | ||
604 | return -1; | ||
605 | } | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | int synaptics_detect(struct psmouse *psmouse, int set_properties) | ||
611 | { | ||
612 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
613 | unsigned char param[4]; | ||
614 | |||
615 | param[0] = 0; | ||
616 | |||
617 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
618 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
619 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
620 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
621 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | ||
622 | |||
623 | if (param[1] != 0x47) | ||
624 | return -1; | ||
625 | |||
626 | if (set_properties) { | ||
627 | psmouse->vendor = "Synaptics"; | ||
628 | psmouse->name = "TouchPad"; | ||
629 | } | ||
630 | |||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | #if defined(__i386__) | ||
635 | #include <linux/dmi.h> | ||
636 | static struct dmi_system_id toshiba_dmi_table[] = { | ||
637 | { | ||
638 | .ident = "Toshiba Satellite", | ||
639 | .matches = { | ||
640 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | ||
641 | DMI_MATCH(DMI_PRODUCT_NAME , "Satellite"), | ||
642 | }, | ||
643 | }, | ||
644 | { } | ||
645 | }; | ||
646 | #endif | ||
647 | |||
648 | int synaptics_init(struct psmouse *psmouse) | ||
649 | { | ||
650 | struct synaptics_data *priv; | ||
651 | |||
652 | psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL); | ||
653 | if (!priv) | ||
654 | return -1; | ||
655 | memset(priv, 0, sizeof(struct synaptics_data)); | ||
656 | |||
657 | if (synaptics_query_hardware(psmouse)) { | ||
658 | printk(KERN_ERR "Unable to query Synaptics hardware.\n"); | ||
659 | goto init_fail; | ||
660 | } | ||
661 | |||
662 | if (synaptics_set_absolute_mode(psmouse)) { | ||
663 | printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); | ||
664 | goto init_fail; | ||
665 | } | ||
666 | |||
667 | priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; | ||
668 | |||
669 | print_ident(priv); | ||
670 | set_input_params(&psmouse->dev, priv); | ||
671 | |||
672 | psmouse->protocol_handler = synaptics_process_byte; | ||
673 | psmouse->set_rate = synaptics_set_rate; | ||
674 | psmouse->disconnect = synaptics_disconnect; | ||
675 | psmouse->reconnect = synaptics_reconnect; | ||
676 | psmouse->pktsize = 6; | ||
677 | |||
678 | if (SYN_CAP_PASS_THROUGH(priv->capabilities)) | ||
679 | synaptics_pt_create(psmouse); | ||
680 | |||
681 | #if defined(__i386__) | ||
682 | /* | ||
683 | * Toshiba's KBC seems to have trouble handling data from | ||
684 | * Synaptics as full rate, switch to lower rate which is roughly | ||
685 | * thye same as rate of standard PS/2 mouse. | ||
686 | */ | ||
687 | if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) { | ||
688 | printk(KERN_INFO "synaptics: Toshiba Satellite detected, limiting rate to 40pps.\n"); | ||
689 | psmouse->rate = 40; | ||
690 | } | ||
691 | #endif | ||
692 | |||
693 | return 0; | ||
694 | |||
695 | init_fail: | ||
696 | kfree(priv); | ||
697 | return -1; | ||
698 | } | ||
699 | |||
700 | |||
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h new file mode 100644 index 000000000000..68fff1dcd7de --- /dev/null +++ b/drivers/input/mouse/synaptics.h | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Synaptics TouchPad PS/2 mouse driver | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License version 2 as published by | ||
6 | * the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _SYNAPTICS_H | ||
10 | #define _SYNAPTICS_H | ||
11 | |||
12 | extern int synaptics_detect(struct psmouse *psmouse, int set_properties); | ||
13 | extern int synaptics_init(struct psmouse *psmouse); | ||
14 | extern void synaptics_reset(struct psmouse *psmouse); | ||
15 | |||
16 | /* synaptics queries */ | ||
17 | #define SYN_QUE_IDENTIFY 0x00 | ||
18 | #define SYN_QUE_MODES 0x01 | ||
19 | #define SYN_QUE_CAPABILITIES 0x02 | ||
20 | #define SYN_QUE_MODEL 0x03 | ||
21 | #define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06 | ||
22 | #define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07 | ||
23 | #define SYN_QUE_RESOLUTION 0x08 | ||
24 | #define SYN_QUE_EXT_CAPAB 0x09 | ||
25 | |||
26 | /* synatics modes */ | ||
27 | #define SYN_BIT_ABSOLUTE_MODE (1 << 7) | ||
28 | #define SYN_BIT_HIGH_RATE (1 << 6) | ||
29 | #define SYN_BIT_SLEEP_MODE (1 << 3) | ||
30 | #define SYN_BIT_DISABLE_GESTURE (1 << 2) | ||
31 | #define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1) | ||
32 | #define SYN_BIT_W_MODE (1 << 0) | ||
33 | |||
34 | /* synaptics model ID bits */ | ||
35 | #define SYN_MODEL_ROT180(m) ((m) & (1 << 23)) | ||
36 | #define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22)) | ||
37 | #define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f) | ||
38 | #define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f) | ||
39 | #define SYN_MODEL_NEWABS(m) ((m) & (1 << 7)) | ||
40 | #define SYN_MODEL_PEN(m) ((m) & (1 << 6)) | ||
41 | #define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5)) | ||
42 | #define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f) | ||
43 | |||
44 | /* synaptics capability bits */ | ||
45 | #define SYN_CAP_EXTENDED(c) ((c) & (1 << 23)) | ||
46 | #define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18)) | ||
47 | #define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7)) | ||
48 | #define SYN_CAP_SLEEP(c) ((c) & (1 << 4)) | ||
49 | #define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3)) | ||
50 | #define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1)) | ||
51 | #define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0)) | ||
52 | #define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47) | ||
53 | #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) | ||
54 | #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) | ||
55 | |||
56 | /* synaptics modes query bits */ | ||
57 | #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) | ||
58 | #define SYN_MODE_RATE(m) ((m) & (1 << 6)) | ||
59 | #define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3)) | ||
60 | #define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2)) | ||
61 | #define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1)) | ||
62 | #define SYN_MODE_WMODE(m) ((m) & (1 << 0)) | ||
63 | |||
64 | /* synaptics identify query bits */ | ||
65 | #define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f) | ||
66 | #define SYN_ID_MAJOR(i) ((i) & 0x0f) | ||
67 | #define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) | ||
68 | #define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) | ||
69 | |||
70 | /* synaptics special commands */ | ||
71 | #define SYN_PS_SET_MODE2 0x14 | ||
72 | #define SYN_PS_CLIENT_CMD 0x28 | ||
73 | |||
74 | /* synaptics packet types */ | ||
75 | #define SYN_NEWABS 0 | ||
76 | #define SYN_NEWABS_STRICT 1 | ||
77 | #define SYN_NEWABS_RELAXED 2 | ||
78 | #define SYN_OLDABS 3 | ||
79 | |||
80 | /* | ||
81 | * A structure to describe the state of the touchpad hardware (buttons and pad) | ||
82 | */ | ||
83 | |||
84 | struct synaptics_hw_state { | ||
85 | int x; | ||
86 | int y; | ||
87 | int z; | ||
88 | int w; | ||
89 | unsigned int left:1; | ||
90 | unsigned int right:1; | ||
91 | unsigned int middle:1; | ||
92 | unsigned int up:1; | ||
93 | unsigned int down:1; | ||
94 | unsigned char ext_buttons; | ||
95 | signed char scroll; | ||
96 | }; | ||
97 | |||
98 | struct synaptics_data { | ||
99 | /* Data read from the touchpad */ | ||
100 | unsigned long int model_id; /* Model-ID */ | ||
101 | unsigned long int capabilities; /* Capabilities */ | ||
102 | unsigned long int ext_cap; /* Extended Capabilities */ | ||
103 | unsigned long int identity; /* Identification */ | ||
104 | |||
105 | unsigned char pkt_type; /* packet type - old, new, etc */ | ||
106 | unsigned char mode; /* current mode byte */ | ||
107 | int scroll; | ||
108 | }; | ||
109 | |||
110 | #endif /* _SYNAPTICS_H */ | ||
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c new file mode 100644 index 000000000000..b2cb101c8110 --- /dev/null +++ b/drivers/input/mouse/vsxxxaa.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /* | ||
2 | * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers) | ||
3 | * DEC VSXXX-GA mouse (rectangular mouse, with ball) | ||
4 | * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) | ||
5 | * | ||
6 | * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de> | ||
7 | * | ||
8 | * The packet format was initially taken from a patch to GPM which is (C) 2001 | ||
9 | * by Karsten Merker <merker@linuxtag.org> | ||
10 | * and Maciej W. Rozycki <macro@ds2.pg.gda.pl> | ||
11 | * Later on, I had access to the device's documentation (referenced below). | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * Building an adaptor to DE9 / DB25 RS232 | ||
32 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
33 | * | ||
34 | * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for | ||
35 | * anything if you break your mouse, your computer or whatever! | ||
36 | * | ||
37 | * In theory, this mouse is a simple RS232 device. In practice, it has got | ||
38 | * a quite uncommon plug and the requirement to additionally get a power | ||
39 | * supply at +5V and -12V. | ||
40 | * | ||
41 | * If you look at the socket/jack (_not_ at the plug), we use this pin | ||
42 | * numbering: | ||
43 | * _______ | ||
44 | * / 7 6 5 \ | ||
45 | * | 4 --- 3 | | ||
46 | * \ 2 1 / | ||
47 | * ------- | ||
48 | * | ||
49 | * DEC socket DE9 DB25 Note | ||
50 | * 1 (GND) 5 7 - | ||
51 | * 2 (RxD) 2 3 - | ||
52 | * 3 (TxD) 3 2 - | ||
53 | * 4 (-12V) - - Somewhere from the PSU. At ATX, it's | ||
54 | * the thin blue wire at pin 12 of the | ||
55 | * ATX power connector. Only required for | ||
56 | * VSXXX-AA/-GA mice. | ||
57 | * 5 (+5V) - - PSU (red wires of ATX power connector | ||
58 | * on pin 4, 6, 19 or 20) or HDD power | ||
59 | * connector (also red wire). | ||
60 | * 6 (+12V) - - HDD power connector, yellow wire. Only | ||
61 | * required for VSXXX-AB digitizer. | ||
62 | * 7 (dev. avail.) - - The mouse shorts this one to pin 1. | ||
63 | * This way, the host computer can detect | ||
64 | * the mouse. To use it with the adaptor, | ||
65 | * simply don't connect this pin. | ||
66 | * | ||
67 | * So to get a working adaptor, you need to connect the mouse with three | ||
68 | * wires to a RS232 port and two or three additional wires for +5V, +12V and | ||
69 | * -12V to the PSU. | ||
70 | * | ||
71 | * Flow specification for the link is 4800, 8o1. | ||
72 | * | ||
73 | * The mice and tablet are described in "VCB02 Video Subsystem - Technical | ||
74 | * Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine | ||
75 | * specific for DEC documentation. Try | ||
76 | * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1 | ||
77 | */ | ||
78 | |||
79 | #include <linux/delay.h> | ||
80 | #include <linux/module.h> | ||
81 | #include <linux/slab.h> | ||
82 | #include <linux/interrupt.h> | ||
83 | #include <linux/input.h> | ||
84 | #include <linux/config.h> | ||
85 | #include <linux/serio.h> | ||
86 | #include <linux/init.h> | ||
87 | |||
88 | #define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" | ||
89 | |||
90 | MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); | ||
91 | MODULE_DESCRIPTION (DRIVER_DESC); | ||
92 | MODULE_LICENSE ("GPL"); | ||
93 | |||
94 | #undef VSXXXAA_DEBUG | ||
95 | #ifdef VSXXXAA_DEBUG | ||
96 | #define DBG(x...) printk (x) | ||
97 | #else | ||
98 | #define DBG(x...) do {} while (0) | ||
99 | #endif | ||
100 | |||
101 | #define VSXXXAA_INTRO_MASK 0x80 | ||
102 | #define VSXXXAA_INTRO_HEAD 0x80 | ||
103 | #define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \ | ||
104 | == VSXXXAA_INTRO_HEAD) | ||
105 | |||
106 | #define VSXXXAA_PACKET_MASK 0xe0 | ||
107 | #define VSXXXAA_PACKET_REL 0x80 | ||
108 | #define VSXXXAA_PACKET_ABS 0xc0 | ||
109 | #define VSXXXAA_PACKET_POR 0xa0 | ||
110 | #define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type)) | ||
111 | |||
112 | |||
113 | |||
114 | struct vsxxxaa { | ||
115 | struct input_dev dev; | ||
116 | struct serio *serio; | ||
117 | #define BUFLEN 15 /* At least 5 is needed for a full tablet packet */ | ||
118 | unsigned char buf[BUFLEN]; | ||
119 | unsigned char count; | ||
120 | unsigned char version; | ||
121 | unsigned char country; | ||
122 | unsigned char type; | ||
123 | char name[64]; | ||
124 | char phys[32]; | ||
125 | }; | ||
126 | |||
127 | static void | ||
128 | vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num) | ||
129 | { | ||
130 | if (num >= mouse->count) | ||
131 | mouse->count = 0; | ||
132 | else { | ||
133 | memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num); | ||
134 | mouse->count -= num; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte) | ||
140 | { | ||
141 | if (mouse->count == BUFLEN) { | ||
142 | printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", | ||
143 | mouse->name, mouse->phys); | ||
144 | vsxxxaa_drop_bytes (mouse, 1); | ||
145 | } | ||
146 | DBG (KERN_INFO "Queueing byte 0x%02x\n", byte); | ||
147 | |||
148 | mouse->buf[mouse->count++] = byte; | ||
149 | } | ||
150 | |||
151 | static void | ||
152 | vsxxxaa_detection_done (struct vsxxxaa *mouse) | ||
153 | { | ||
154 | switch (mouse->type) { | ||
155 | case 0x02: | ||
156 | sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse"); | ||
157 | break; | ||
158 | |||
159 | case 0x04: | ||
160 | sprintf (mouse->name, "DEC VSXXX-AB digitizer"); | ||
161 | break; | ||
162 | |||
163 | default: | ||
164 | sprintf (mouse->name, "unknown DEC pointer device " | ||
165 | "(type = 0x%02x)", mouse->type); | ||
166 | break; | ||
167 | } | ||
168 | |||
169 | printk (KERN_INFO "Found %s version 0x%02x from country 0x%02x " | ||
170 | "on port %s\n", mouse->name, mouse->version, | ||
171 | mouse->country, mouse->phys); | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Returns number of bytes to be dropped, 0 if packet is okay. | ||
176 | */ | ||
177 | static int | ||
178 | vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len) | ||
179 | { | ||
180 | int i; | ||
181 | |||
182 | /* First byte must be a header byte */ | ||
183 | if (!IS_HDR_BYTE (mouse->buf[0])) { | ||
184 | DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); | ||
185 | return 1; | ||
186 | } | ||
187 | |||
188 | /* Check all following bytes */ | ||
189 | if (packet_len > 1) { | ||
190 | for (i = 1; i < packet_len; i++) { | ||
191 | if (IS_HDR_BYTE (mouse->buf[i])) { | ||
192 | printk (KERN_ERR "Need to drop %d bytes " | ||
193 | "of a broken packet.\n", | ||
194 | i - 1); | ||
195 | DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n", | ||
196 | packet_len, i, mouse->buf[i]); | ||
197 | return i - 1; | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static __inline__ int | ||
206 | vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len) | ||
207 | { | ||
208 | return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type); | ||
209 | } | ||
210 | |||
211 | static void | ||
212 | vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse, struct pt_regs *regs) | ||
213 | { | ||
214 | struct input_dev *dev = &mouse->dev; | ||
215 | unsigned char *buf = mouse->buf; | ||
216 | int left, middle, right; | ||
217 | int dx, dy; | ||
218 | |||
219 | /* | ||
220 | * Check for normal stream packets. This is three bytes, | ||
221 | * with the first byte's 3 MSB set to 100. | ||
222 | * | ||
223 | * [0]: 1 0 0 SignX SignY Left Middle Right | ||
224 | * [1]: 0 dx dx dx dx dx dx dx | ||
225 | * [2]: 0 dy dy dy dy dy dy dy | ||
226 | */ | ||
227 | |||
228 | /* | ||
229 | * Low 7 bit of byte 1 are abs(dx), bit 7 is | ||
230 | * 0, bit 4 of byte 0 is direction. | ||
231 | */ | ||
232 | dx = buf[1] & 0x7f; | ||
233 | dx *= ((buf[0] >> 4) & 0x01)? 1: -1; | ||
234 | |||
235 | /* | ||
236 | * Low 7 bit of byte 2 are abs(dy), bit 7 is | ||
237 | * 0, bit 3 of byte 0 is direction. | ||
238 | */ | ||
239 | dy = buf[2] & 0x7f; | ||
240 | dy *= ((buf[0] >> 3) & 0x01)? -1: 1; | ||
241 | |||
242 | /* | ||
243 | * Get button state. It's the low three bits | ||
244 | * (for three buttons) of byte 0. | ||
245 | */ | ||
246 | left = (buf[0] & 0x04)? 1: 0; | ||
247 | middle = (buf[0] & 0x02)? 1: 0; | ||
248 | right = (buf[0] & 0x01)? 1: 0; | ||
249 | |||
250 | vsxxxaa_drop_bytes (mouse, 3); | ||
251 | |||
252 | DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", | ||
253 | mouse->name, mouse->phys, dx, dy, | ||
254 | left? "L": "l", middle? "M": "m", right? "R": "r"); | ||
255 | |||
256 | /* | ||
257 | * Report what we've found so far... | ||
258 | */ | ||
259 | input_regs (dev, regs); | ||
260 | input_report_key (dev, BTN_LEFT, left); | ||
261 | input_report_key (dev, BTN_MIDDLE, middle); | ||
262 | input_report_key (dev, BTN_RIGHT, right); | ||
263 | input_report_key (dev, BTN_TOUCH, 0); | ||
264 | input_report_rel (dev, REL_X, dx); | ||
265 | input_report_rel (dev, REL_Y, dy); | ||
266 | input_sync (dev); | ||
267 | } | ||
268 | |||
269 | static void | ||
270 | vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse, struct pt_regs *regs) | ||
271 | { | ||
272 | struct input_dev *dev = &mouse->dev; | ||
273 | unsigned char *buf = mouse->buf; | ||
274 | int left, middle, right, touch; | ||
275 | int x, y; | ||
276 | |||
277 | /* | ||
278 | * Tablet position / button packet | ||
279 | * | ||
280 | * [0]: 1 1 0 B4 B3 B2 B1 Pr | ||
281 | * [1]: 0 0 X5 X4 X3 X2 X1 X0 | ||
282 | * [2]: 0 0 X11 X10 X9 X8 X7 X6 | ||
283 | * [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0 | ||
284 | * [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6 | ||
285 | */ | ||
286 | |||
287 | /* | ||
288 | * Get X/Y position. Y axis needs to be inverted since VSXXX-AB | ||
289 | * counts down->top while monitor counts top->bottom. | ||
290 | */ | ||
291 | x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f); | ||
292 | y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f); | ||
293 | y = 1023 - y; | ||
294 | |||
295 | /* | ||
296 | * Get button state. It's bits <4..1> of byte 0. | ||
297 | */ | ||
298 | left = (buf[0] & 0x02)? 1: 0; | ||
299 | middle = (buf[0] & 0x04)? 1: 0; | ||
300 | right = (buf[0] & 0x08)? 1: 0; | ||
301 | touch = (buf[0] & 0x10)? 1: 0; | ||
302 | |||
303 | vsxxxaa_drop_bytes (mouse, 5); | ||
304 | |||
305 | DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", | ||
306 | mouse->name, mouse->phys, x, y, | ||
307 | left? "L": "l", middle? "M": "m", | ||
308 | right? "R": "r", touch? "T": "t"); | ||
309 | |||
310 | /* | ||
311 | * Report what we've found so far... | ||
312 | */ | ||
313 | input_regs (dev, regs); | ||
314 | input_report_key (dev, BTN_LEFT, left); | ||
315 | input_report_key (dev, BTN_MIDDLE, middle); | ||
316 | input_report_key (dev, BTN_RIGHT, right); | ||
317 | input_report_key (dev, BTN_TOUCH, touch); | ||
318 | input_report_abs (dev, ABS_X, x); | ||
319 | input_report_abs (dev, ABS_Y, y); | ||
320 | input_sync (dev); | ||
321 | } | ||
322 | |||
323 | static void | ||
324 | vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse, struct pt_regs *regs) | ||
325 | { | ||
326 | struct input_dev *dev = &mouse->dev; | ||
327 | unsigned char *buf = mouse->buf; | ||
328 | int left, middle, right; | ||
329 | unsigned char error; | ||
330 | |||
331 | /* | ||
332 | * Check for Power-On-Reset packets. These are sent out | ||
333 | * after plugging the mouse in, or when explicitely | ||
334 | * requested by sending 'T'. | ||
335 | * | ||
336 | * [0]: 1 0 1 0 R3 R2 R1 R0 | ||
337 | * [1]: 0 M2 M1 M0 D3 D2 D1 D0 | ||
338 | * [2]: 0 E6 E5 E4 E3 E2 E1 E0 | ||
339 | * [3]: 0 0 0 0 0 Left Middle Right | ||
340 | * | ||
341 | * M: manufacturer location code | ||
342 | * R: revision code | ||
343 | * E: Error code. If it's in the range of 0x00..0x1f, only some | ||
344 | * minor problem occured. Errors >= 0x20 are considered bad | ||
345 | * and the device may not work properly... | ||
346 | * D: <0010> == mouse, <0100> == tablet | ||
347 | */ | ||
348 | |||
349 | mouse->version = buf[0] & 0x0f; | ||
350 | mouse->country = (buf[1] >> 4) & 0x07; | ||
351 | mouse->type = buf[1] & 0x0f; | ||
352 | error = buf[2] & 0x7f; | ||
353 | |||
354 | /* | ||
355 | * Get button state. It's the low three bits | ||
356 | * (for three buttons) of byte 0. Maybe even the bit <3> | ||
357 | * has some meaning if a tablet is attached. | ||
358 | */ | ||
359 | left = (buf[0] & 0x04)? 1: 0; | ||
360 | middle = (buf[0] & 0x02)? 1: 0; | ||
361 | right = (buf[0] & 0x01)? 1: 0; | ||
362 | |||
363 | vsxxxaa_drop_bytes (mouse, 4); | ||
364 | vsxxxaa_detection_done (mouse); | ||
365 | |||
366 | if (error <= 0x1f) { | ||
367 | /* No (serious) error. Report buttons */ | ||
368 | input_regs (dev, regs); | ||
369 | input_report_key (dev, BTN_LEFT, left); | ||
370 | input_report_key (dev, BTN_MIDDLE, middle); | ||
371 | input_report_key (dev, BTN_RIGHT, right); | ||
372 | input_report_key (dev, BTN_TOUCH, 0); | ||
373 | input_sync (dev); | ||
374 | |||
375 | if (error != 0) | ||
376 | printk (KERN_INFO "Your %s on %s reports error=0x%02x\n", | ||
377 | mouse->name, mouse->phys, error); | ||
378 | |||
379 | } | ||
380 | |||
381 | /* | ||
382 | * If the mouse was hot-plugged, we need to force differential mode | ||
383 | * now... However, give it a second to recover from it's reset. | ||
384 | */ | ||
385 | printk (KERN_NOTICE "%s on %s: Forceing standard packet format, " | ||
386 | "incremental streaming mode and 72 samples/sec\n", | ||
387 | mouse->name, mouse->phys); | ||
388 | mouse->serio->write (mouse->serio, 'S'); /* Standard format */ | ||
389 | mdelay (50); | ||
390 | mouse->serio->write (mouse->serio, 'R'); /* Incremental */ | ||
391 | mdelay (50); | ||
392 | mouse->serio->write (mouse->serio, 'L'); /* 72 samples/sec */ | ||
393 | } | ||
394 | |||
395 | static void | ||
396 | vsxxxaa_parse_buffer (struct vsxxxaa *mouse, struct pt_regs *regs) | ||
397 | { | ||
398 | unsigned char *buf = mouse->buf; | ||
399 | int stray_bytes; | ||
400 | |||
401 | /* | ||
402 | * Parse buffer to death... | ||
403 | */ | ||
404 | do { | ||
405 | /* | ||
406 | * Out of sync? Throw away what we don't understand. Each | ||
407 | * packet starts with a byte whose bit 7 is set. Unhandled | ||
408 | * packets (ie. which we don't know about or simply b0rk3d | ||
409 | * data...) will get shifted out of the buffer after some | ||
410 | * activity on the mouse. | ||
411 | */ | ||
412 | while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) { | ||
413 | printk (KERN_ERR "%s on %s: Dropping a byte to regain " | ||
414 | "sync with mouse data stream...\n", | ||
415 | mouse->name, mouse->phys); | ||
416 | vsxxxaa_drop_bytes (mouse, 1); | ||
417 | } | ||
418 | |||
419 | /* | ||
420 | * Check for packets we know about. | ||
421 | */ | ||
422 | |||
423 | if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) { | ||
424 | /* Check for broken packet */ | ||
425 | stray_bytes = vsxxxaa_check_packet (mouse, 3); | ||
426 | if (stray_bytes > 0) { | ||
427 | printk (KERN_ERR "Dropping %d bytes now...\n", | ||
428 | stray_bytes); | ||
429 | vsxxxaa_drop_bytes (mouse, stray_bytes); | ||
430 | continue; | ||
431 | } | ||
432 | |||
433 | vsxxxaa_handle_REL_packet (mouse, regs); | ||
434 | continue; /* More to parse? */ | ||
435 | } | ||
436 | |||
437 | if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) { | ||
438 | /* Check for broken packet */ | ||
439 | stray_bytes = vsxxxaa_check_packet (mouse, 5); | ||
440 | if (stray_bytes > 0) { | ||
441 | printk (KERN_ERR "Dropping %d bytes now...\n", | ||
442 | stray_bytes); | ||
443 | vsxxxaa_drop_bytes (mouse, stray_bytes); | ||
444 | continue; | ||
445 | } | ||
446 | |||
447 | vsxxxaa_handle_ABS_packet (mouse, regs); | ||
448 | continue; /* More to parse? */ | ||
449 | } | ||
450 | |||
451 | if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) { | ||
452 | /* Check for broken packet */ | ||
453 | stray_bytes = vsxxxaa_check_packet (mouse, 4); | ||
454 | if (stray_bytes > 0) { | ||
455 | printk (KERN_ERR "Dropping %d bytes now...\n", | ||
456 | stray_bytes); | ||
457 | vsxxxaa_drop_bytes (mouse, stray_bytes); | ||
458 | continue; | ||
459 | } | ||
460 | |||
461 | vsxxxaa_handle_POR_packet (mouse, regs); | ||
462 | continue; /* More to parse? */ | ||
463 | } | ||
464 | |||
465 | break; /* No REL, ABS or POR packet found */ | ||
466 | } while (1); | ||
467 | } | ||
468 | |||
469 | static irqreturn_t | ||
470 | vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags, | ||
471 | struct pt_regs *regs) | ||
472 | { | ||
473 | struct vsxxxaa *mouse = serio_get_drvdata (serio); | ||
474 | |||
475 | vsxxxaa_queue_byte (mouse, data); | ||
476 | vsxxxaa_parse_buffer (mouse, regs); | ||
477 | |||
478 | return IRQ_HANDLED; | ||
479 | } | ||
480 | |||
481 | static void | ||
482 | vsxxxaa_disconnect (struct serio *serio) | ||
483 | { | ||
484 | struct vsxxxaa *mouse = serio_get_drvdata (serio); | ||
485 | |||
486 | input_unregister_device (&mouse->dev); | ||
487 | serio_close (serio); | ||
488 | serio_set_drvdata (serio, NULL); | ||
489 | kfree (mouse); | ||
490 | } | ||
491 | |||
492 | static int | ||
493 | vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) | ||
494 | { | ||
495 | struct vsxxxaa *mouse; | ||
496 | int err; | ||
497 | |||
498 | if (!(mouse = kmalloc (sizeof (struct vsxxxaa), GFP_KERNEL))) | ||
499 | return -ENOMEM; | ||
500 | |||
501 | memset (mouse, 0, sizeof (struct vsxxxaa)); | ||
502 | |||
503 | init_input_dev (&mouse->dev); | ||
504 | set_bit (EV_KEY, mouse->dev.evbit); /* We have buttons */ | ||
505 | set_bit (EV_REL, mouse->dev.evbit); | ||
506 | set_bit (EV_ABS, mouse->dev.evbit); | ||
507 | set_bit (BTN_LEFT, mouse->dev.keybit); /* We have 3 buttons */ | ||
508 | set_bit (BTN_MIDDLE, mouse->dev.keybit); | ||
509 | set_bit (BTN_RIGHT, mouse->dev.keybit); | ||
510 | set_bit (BTN_TOUCH, mouse->dev.keybit); /* ...and Tablet */ | ||
511 | set_bit (REL_X, mouse->dev.relbit); | ||
512 | set_bit (REL_Y, mouse->dev.relbit); | ||
513 | set_bit (ABS_X, mouse->dev.absbit); | ||
514 | set_bit (ABS_Y, mouse->dev.absbit); | ||
515 | |||
516 | mouse->dev.absmin[ABS_X] = 0; | ||
517 | mouse->dev.absmax[ABS_X] = 1023; | ||
518 | mouse->dev.absmin[ABS_Y] = 0; | ||
519 | mouse->dev.absmax[ABS_Y] = 1023; | ||
520 | |||
521 | mouse->dev.private = mouse; | ||
522 | |||
523 | sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer"); | ||
524 | sprintf (mouse->phys, "%s/input0", serio->phys); | ||
525 | mouse->dev.name = mouse->name; | ||
526 | mouse->dev.phys = mouse->phys; | ||
527 | mouse->dev.id.bustype = BUS_RS232; | ||
528 | mouse->dev.dev = &serio->dev; | ||
529 | mouse->serio = serio; | ||
530 | |||
531 | serio_set_drvdata (serio, mouse); | ||
532 | |||
533 | err = serio_open (serio, drv); | ||
534 | if (err) { | ||
535 | serio_set_drvdata (serio, NULL); | ||
536 | kfree (mouse); | ||
537 | return err; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * Request selftest. Standard packet format and differential | ||
542 | * mode will be requested after the device ID'ed successfully. | ||
543 | */ | ||
544 | mouse->serio->write (mouse->serio, 'T'); /* Test */ | ||
545 | |||
546 | input_register_device (&mouse->dev); | ||
547 | |||
548 | printk (KERN_INFO "input: %s on %s\n", mouse->name, mouse->phys); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static struct serio_device_id vsxxaa_serio_ids[] = { | ||
554 | { | ||
555 | .type = SERIO_RS232, | ||
556 | .proto = SERIO_VSXXXAA, | ||
557 | .id = SERIO_ANY, | ||
558 | .extra = SERIO_ANY, | ||
559 | }, | ||
560 | { 0 } | ||
561 | }; | ||
562 | |||
563 | MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids); | ||
564 | |||
565 | static struct serio_driver vsxxxaa_drv = { | ||
566 | .driver = { | ||
567 | .name = "vsxxxaa", | ||
568 | }, | ||
569 | .description = DRIVER_DESC, | ||
570 | .id_table = vsxxaa_serio_ids, | ||
571 | .connect = vsxxxaa_connect, | ||
572 | .interrupt = vsxxxaa_interrupt, | ||
573 | .disconnect = vsxxxaa_disconnect, | ||
574 | }; | ||
575 | |||
576 | static int __init | ||
577 | vsxxxaa_init (void) | ||
578 | { | ||
579 | serio_register_driver(&vsxxxaa_drv); | ||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static void __exit | ||
584 | vsxxxaa_exit (void) | ||
585 | { | ||
586 | serio_unregister_driver(&vsxxxaa_drv); | ||
587 | } | ||
588 | |||
589 | module_init (vsxxxaa_init); | ||
590 | module_exit (vsxxxaa_exit); | ||
591 | |||