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/serio/libps2.c |
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/serio/libps2.c')
-rw-r--r-- | drivers/input/serio/libps2.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c new file mode 100644 index 000000000000..c978657068c5 --- /dev/null +++ b/drivers/input/serio/libps2.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * PS/2 driver library | ||
3 | * | ||
4 | * Copyright (c) 1999-2002 Vojtech Pavlik | ||
5 | * Copyright (c) 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 | |||
24 | #define DRIVER_DESC "PS/2 driver library" | ||
25 | |||
26 | MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); | ||
27 | MODULE_DESCRIPTION("PS/2 driver library"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | EXPORT_SYMBOL(ps2_init); | ||
31 | EXPORT_SYMBOL(ps2_sendbyte); | ||
32 | EXPORT_SYMBOL(ps2_command); | ||
33 | EXPORT_SYMBOL(ps2_schedule_command); | ||
34 | EXPORT_SYMBOL(ps2_handle_ack); | ||
35 | EXPORT_SYMBOL(ps2_handle_response); | ||
36 | EXPORT_SYMBOL(ps2_cmd_aborted); | ||
37 | |||
38 | /* Work structure to schedule execution of a command */ | ||
39 | struct ps2work { | ||
40 | struct work_struct work; | ||
41 | struct ps2dev *ps2dev; | ||
42 | int command; | ||
43 | unsigned char param[0]; | ||
44 | }; | ||
45 | |||
46 | |||
47 | /* | ||
48 | * ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge. | ||
49 | * It doesn't handle retransmission, though it could - because when there would | ||
50 | * be need for retransmissions, the mouse has to be replaced anyway. | ||
51 | * | ||
52 | * ps2_sendbyte() can only be called from a process context | ||
53 | */ | ||
54 | |||
55 | int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) | ||
56 | { | ||
57 | serio_pause_rx(ps2dev->serio); | ||
58 | ps2dev->nak = 1; | ||
59 | ps2dev->flags |= PS2_FLAG_ACK; | ||
60 | serio_continue_rx(ps2dev->serio); | ||
61 | |||
62 | if (serio_write(ps2dev->serio, byte) == 0) | ||
63 | wait_event_timeout(ps2dev->wait, | ||
64 | !(ps2dev->flags & PS2_FLAG_ACK), | ||
65 | msecs_to_jiffies(timeout)); | ||
66 | |||
67 | serio_pause_rx(ps2dev->serio); | ||
68 | ps2dev->flags &= ~PS2_FLAG_ACK; | ||
69 | serio_continue_rx(ps2dev->serio); | ||
70 | |||
71 | return -ps2dev->nak; | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * ps2_command() sends a command and its parameters to the mouse, | ||
76 | * then waits for the response and puts it in the param array. | ||
77 | * | ||
78 | * ps2_command() can only be called from a process context | ||
79 | */ | ||
80 | |||
81 | int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) | ||
82 | { | ||
83 | int timeout; | ||
84 | int send = (command >> 12) & 0xf; | ||
85 | int receive = (command >> 8) & 0xf; | ||
86 | int rc = -1; | ||
87 | int i; | ||
88 | |||
89 | down(&ps2dev->cmd_sem); | ||
90 | |||
91 | serio_pause_rx(ps2dev->serio); | ||
92 | ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; | ||
93 | ps2dev->cmdcnt = receive; | ||
94 | if (receive && param) | ||
95 | for (i = 0; i < receive; i++) | ||
96 | ps2dev->cmdbuf[(receive - 1) - i] = param[i]; | ||
97 | serio_continue_rx(ps2dev->serio); | ||
98 | |||
99 | /* | ||
100 | * Some devices (Synaptics) peform the reset before | ||
101 | * ACKing the reset command, and so it can take a long | ||
102 | * time before the ACK arrrives. | ||
103 | */ | ||
104 | if (command & 0xff) | ||
105 | if (ps2_sendbyte(ps2dev, command & 0xff, | ||
106 | command == PS2_CMD_RESET_BAT ? 1000 : 200)) | ||
107 | goto out; | ||
108 | |||
109 | for (i = 0; i < send; i++) | ||
110 | if (ps2_sendbyte(ps2dev, param[i], 200)) | ||
111 | goto out; | ||
112 | |||
113 | /* | ||
114 | * The reset command takes a long time to execute. | ||
115 | */ | ||
116 | timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500); | ||
117 | |||
118 | timeout = wait_event_timeout(ps2dev->wait, | ||
119 | !(ps2dev->flags & PS2_FLAG_CMD1), timeout); | ||
120 | |||
121 | if (ps2dev->cmdcnt && timeout > 0) { | ||
122 | |||
123 | if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) { | ||
124 | /* | ||
125 | * Device has sent the first response byte | ||
126 | * after a reset command, reset is thus done, | ||
127 | * shorten the timeout. The next byte will come | ||
128 | * soon (keyboard) or not at all (mouse). | ||
129 | */ | ||
130 | timeout = msecs_to_jiffies(100); | ||
131 | } | ||
132 | |||
133 | if (command == PS2_CMD_GETID && | ||
134 | ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */ | ||
135 | ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */ | ||
136 | ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */ | ||
137 | ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */ | ||
138 | ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */ | ||
139 | ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */ | ||
140 | /* | ||
141 | * Device behind the port is not a keyboard | ||
142 | * so we don't need to wait for the 2nd byte | ||
143 | * of ID response. | ||
144 | */ | ||
145 | serio_pause_rx(ps2dev->serio); | ||
146 | ps2dev->flags = ps2dev->cmdcnt = 0; | ||
147 | serio_continue_rx(ps2dev->serio); | ||
148 | } | ||
149 | |||
150 | wait_event_timeout(ps2dev->wait, | ||
151 | !(ps2dev->flags & PS2_FLAG_CMD), timeout); | ||
152 | } | ||
153 | |||
154 | if (param) | ||
155 | for (i = 0; i < receive; i++) | ||
156 | param[i] = ps2dev->cmdbuf[(receive - 1) - i]; | ||
157 | |||
158 | if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) | ||
159 | goto out; | ||
160 | |||
161 | rc = 0; | ||
162 | |||
163 | out: | ||
164 | serio_pause_rx(ps2dev->serio); | ||
165 | ps2dev->flags = 0; | ||
166 | serio_continue_rx(ps2dev->serio); | ||
167 | |||
168 | up(&ps2dev->cmd_sem); | ||
169 | return rc; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * ps2_execute_scheduled_command() sends a command, previously scheduled by | ||
174 | * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.) | ||
175 | */ | ||
176 | |||
177 | static void ps2_execute_scheduled_command(void *data) | ||
178 | { | ||
179 | struct ps2work *ps2work = data; | ||
180 | |||
181 | ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command); | ||
182 | kfree(ps2work); | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * ps2_schedule_command() allows to schedule delayed execution of a PS/2 | ||
187 | * command and can be used to issue a command from an interrupt or softirq | ||
188 | * context. | ||
189 | */ | ||
190 | |||
191 | int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command) | ||
192 | { | ||
193 | struct ps2work *ps2work; | ||
194 | int send = (command >> 12) & 0xf; | ||
195 | int receive = (command >> 8) & 0xf; | ||
196 | |||
197 | if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC))) | ||
198 | return -1; | ||
199 | |||
200 | memset(ps2work, 0, sizeof(struct ps2work)); | ||
201 | ps2work->ps2dev = ps2dev; | ||
202 | ps2work->command = command; | ||
203 | memcpy(ps2work->param, param, send); | ||
204 | INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work); | ||
205 | |||
206 | if (!schedule_work(&ps2work->work)) { | ||
207 | kfree(ps2work); | ||
208 | return -1; | ||
209 | } | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * ps2_init() initializes ps2dev structure | ||
216 | */ | ||
217 | |||
218 | void ps2_init(struct ps2dev *ps2dev, struct serio *serio) | ||
219 | { | ||
220 | init_MUTEX(&ps2dev->cmd_sem); | ||
221 | init_waitqueue_head(&ps2dev->wait); | ||
222 | ps2dev->serio = serio; | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * ps2_handle_ack() is supposed to be used in interrupt handler | ||
227 | * to properly process ACK/NAK of a command from a PS/2 device. | ||
228 | */ | ||
229 | |||
230 | int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) | ||
231 | { | ||
232 | switch (data) { | ||
233 | case PS2_RET_ACK: | ||
234 | ps2dev->nak = 0; | ||
235 | break; | ||
236 | |||
237 | case PS2_RET_NAK: | ||
238 | ps2dev->nak = 1; | ||
239 | break; | ||
240 | |||
241 | /* | ||
242 | * Workaround for mice which don't ACK the Get ID command. | ||
243 | * These are valid mouse IDs that we recognize. | ||
244 | */ | ||
245 | case 0x00: | ||
246 | case 0x03: | ||
247 | case 0x04: | ||
248 | if (ps2dev->flags & PS2_FLAG_WAITID) { | ||
249 | ps2dev->nak = 0; | ||
250 | break; | ||
251 | } | ||
252 | /* Fall through */ | ||
253 | default: | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | |||
258 | if (!ps2dev->nak && ps2dev->cmdcnt) | ||
259 | ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; | ||
260 | |||
261 | ps2dev->flags &= ~PS2_FLAG_ACK; | ||
262 | wake_up(&ps2dev->wait); | ||
263 | |||
264 | if (data != PS2_RET_ACK) | ||
265 | ps2_handle_response(ps2dev, data); | ||
266 | |||
267 | return 1; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | * ps2_handle_response() is supposed to be used in interrupt handler | ||
272 | * to properly store device's response to a command and notify process | ||
273 | * waiting for completion of the command. | ||
274 | */ | ||
275 | |||
276 | int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) | ||
277 | { | ||
278 | if (ps2dev->cmdcnt) | ||
279 | ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; | ||
280 | |||
281 | if (ps2dev->flags & PS2_FLAG_CMD1) { | ||
282 | ps2dev->flags &= ~PS2_FLAG_CMD1; | ||
283 | if (ps2dev->cmdcnt) | ||
284 | wake_up(&ps2dev->wait); | ||
285 | } | ||
286 | |||
287 | if (!ps2dev->cmdcnt) { | ||
288 | ps2dev->flags &= ~PS2_FLAG_CMD; | ||
289 | wake_up(&ps2dev->wait); | ||
290 | } | ||
291 | |||
292 | return 1; | ||
293 | } | ||
294 | |||
295 | void ps2_cmd_aborted(struct ps2dev *ps2dev) | ||
296 | { | ||
297 | if (ps2dev->flags & PS2_FLAG_ACK) | ||
298 | ps2dev->nak = 1; | ||
299 | |||
300 | if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) | ||
301 | wake_up(&ps2dev->wait); | ||
302 | |||
303 | ps2dev->flags = 0; | ||
304 | } | ||
305 | |||