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/char/ec3104_keyb.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/char/ec3104_keyb.c')
-rw-r--r-- | drivers/char/ec3104_keyb.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/drivers/char/ec3104_keyb.c b/drivers/char/ec3104_keyb.c new file mode 100644 index 000000000000..4aed66968821 --- /dev/null +++ b/drivers/char/ec3104_keyb.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * linux/drivers/char/ec3104_keyb.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
5 | * | ||
6 | * based on linux/drivers/char/pc_keyb.c, which had the following comments: | ||
7 | * | ||
8 | * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 | ||
9 | * See keyboard.c for the whole history. | ||
10 | * | ||
11 | * Major cleanup by Martin Mares, May 1997 | ||
12 | * | ||
13 | * Combined the keyboard and PS/2 mouse handling into one file, | ||
14 | * because they share the same hardware. | ||
15 | * Johan Myreen <jem@iki.fi> 1998-10-08. | ||
16 | * | ||
17 | * Code fixes to handle mouse ACKs properly. | ||
18 | * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29. | ||
19 | */ | ||
20 | /* EC3104 note: | ||
21 | * This code was written without any documentation about the EC3104 chip. While | ||
22 | * I hope I got most of the basic functionality right, the register names I use | ||
23 | * are most likely completely different from those in the chip documentation. | ||
24 | * | ||
25 | * If you have any further information about the EC3104, please tell me | ||
26 | * (prumpf@tux.org). | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | |||
31 | #include <linux/spinlock.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/tty.h> | ||
35 | #include <linux/mm.h> | ||
36 | #include <linux/signal.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/kbd_ll.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include <linux/random.h> | ||
41 | #include <linux/poll.h> | ||
42 | #include <linux/miscdevice.h> | ||
43 | #include <linux/slab.h> | ||
44 | #include <linux/kbd_kern.h> | ||
45 | #include <linux/smp_lock.h> | ||
46 | #include <linux/bitops.h> | ||
47 | |||
48 | #include <asm/keyboard.h> | ||
49 | #include <asm/uaccess.h> | ||
50 | #include <asm/irq.h> | ||
51 | #include <asm/system.h> | ||
52 | #include <asm/ec3104.h> | ||
53 | |||
54 | #include <asm/io.h> | ||
55 | |||
56 | /* Some configuration switches are present in the include file... */ | ||
57 | |||
58 | #include <linux/pc_keyb.h> | ||
59 | |||
60 | #define MSR_CTS 0x10 | ||
61 | #define MCR_RTS 0x02 | ||
62 | #define LSR_DR 0x01 | ||
63 | #define LSR_BOTH_EMPTY 0x60 | ||
64 | |||
65 | static struct e5_struct { | ||
66 | u8 packet[8]; | ||
67 | int pos; | ||
68 | int length; | ||
69 | |||
70 | u8 cached_mcr; | ||
71 | u8 last_msr; | ||
72 | } ec3104_keyb; | ||
73 | |||
74 | /* Simple translation table for the SysRq keys */ | ||
75 | |||
76 | |||
77 | #ifdef CONFIG_MAGIC_SYSRQ | ||
78 | unsigned char ec3104_kbd_sysrq_xlate[128] = | ||
79 | "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ | ||
80 | "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ | ||
81 | "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ | ||
82 | "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ | ||
83 | "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ | ||
84 | "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ | ||
85 | "\r\000/"; /* 0x60 - 0x6f */ | ||
86 | #endif | ||
87 | |||
88 | static void kbd_write_command_w(int data); | ||
89 | static void kbd_write_output_w(int data); | ||
90 | #ifdef CONFIG_PSMOUSE | ||
91 | static void aux_write_ack(int val); | ||
92 | static void __aux_write_ack(int val); | ||
93 | #endif | ||
94 | |||
95 | static DEFINE_SPINLOCK(kbd_controller_lock); | ||
96 | static unsigned char handle_kbd_event(void); | ||
97 | |||
98 | /* used only by send_data - set by keyboard_interrupt */ | ||
99 | static volatile unsigned char reply_expected; | ||
100 | static volatile unsigned char acknowledge; | ||
101 | static volatile unsigned char resend; | ||
102 | |||
103 | |||
104 | int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode) | ||
105 | { | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | int ec3104_kbd_getkeycode(unsigned int scancode) | ||
110 | { | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | |||
115 | /* yes, it probably would be faster to use an array. I don't care. */ | ||
116 | |||
117 | static inline unsigned char ec3104_scan2key(unsigned char scancode) | ||
118 | { | ||
119 | switch (scancode) { | ||
120 | case 1: /* '`' */ | ||
121 | return 41; | ||
122 | |||
123 | case 2 ... 27: | ||
124 | return scancode; | ||
125 | |||
126 | case 28: /* '\\' */ | ||
127 | return 43; | ||
128 | |||
129 | case 29 ... 39: | ||
130 | return scancode + 1; | ||
131 | |||
132 | case 40: /* '\r' */ | ||
133 | return 28; | ||
134 | |||
135 | case 41 ... 50: | ||
136 | return scancode + 3; | ||
137 | |||
138 | case 51: /* ' ' */ | ||
139 | return 57; | ||
140 | |||
141 | case 52: /* escape */ | ||
142 | return 1; | ||
143 | |||
144 | case 54: /* insert/delete (labelled delete) */ | ||
145 | /* this should arguably be 110, but I'd like to have ctrl-alt-del | ||
146 | * working with a standard keymap */ | ||
147 | return 111; | ||
148 | |||
149 | case 55: /* left */ | ||
150 | return 105; | ||
151 | case 56: /* home */ | ||
152 | return 102; | ||
153 | case 57: /* end */ | ||
154 | return 107; | ||
155 | case 58: /* up */ | ||
156 | return 103; | ||
157 | case 59: /* down */ | ||
158 | return 108; | ||
159 | case 60: /* pgup */ | ||
160 | return 104; | ||
161 | case 61: /* pgdown */ | ||
162 | return 109; | ||
163 | case 62: /* right */ | ||
164 | return 106; | ||
165 | |||
166 | case 79 ... 88: /* f1 - f10 */ | ||
167 | return scancode - 20; | ||
168 | |||
169 | case 89 ... 90: /* f11 - f12 */ | ||
170 | return scancode - 2; | ||
171 | |||
172 | case 91: /* left shift */ | ||
173 | return 42; | ||
174 | |||
175 | case 92: /* right shift */ | ||
176 | return 54; | ||
177 | |||
178 | case 93: /* left alt */ | ||
179 | return 56; | ||
180 | case 94: /* right alt */ | ||
181 | return 100; | ||
182 | case 95: /* left ctrl */ | ||
183 | return 29; | ||
184 | case 96: /* right ctrl */ | ||
185 | return 97; | ||
186 | |||
187 | case 97: /* caps lock */ | ||
188 | return 58; | ||
189 | case 102: /* left windows */ | ||
190 | return 125; | ||
191 | case 103: /* right windows */ | ||
192 | return 126; | ||
193 | |||
194 | case 106: /* Fn */ | ||
195 | /* this is wrong. */ | ||
196 | return 84; | ||
197 | |||
198 | default: | ||
199 | return 0; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | int ec3104_kbd_translate(unsigned char scancode, unsigned char *keycode, | ||
204 | char raw_mode) | ||
205 | { | ||
206 | scancode &= 0x7f; | ||
207 | |||
208 | *keycode = ec3104_scan2key(scancode); | ||
209 | |||
210 | return 1; | ||
211 | } | ||
212 | |||
213 | char ec3104_kbd_unexpected_up(unsigned char keycode) | ||
214 | { | ||
215 | return 0200; | ||
216 | } | ||
217 | |||
218 | static inline void handle_keyboard_event(unsigned char scancode) | ||
219 | { | ||
220 | #ifdef CONFIG_VT | ||
221 | handle_scancode(scancode, !(scancode & 0x80)); | ||
222 | #endif | ||
223 | tasklet_schedule(&keyboard_tasklet); | ||
224 | } | ||
225 | |||
226 | void ec3104_kbd_leds(unsigned char leds) | ||
227 | { | ||
228 | } | ||
229 | |||
230 | static u8 e5_checksum(u8 *packet, int count) | ||
231 | { | ||
232 | int i; | ||
233 | u8 sum = 0; | ||
234 | |||
235 | for (i=0; i<count; i++) | ||
236 | sum ^= packet[i]; | ||
237 | |||
238 | if (sum & 0x80) | ||
239 | sum ^= 0xc0; | ||
240 | |||
241 | return sum; | ||
242 | } | ||
243 | |||
244 | static void e5_wait_for_cts(struct e5_struct *k) | ||
245 | { | ||
246 | u8 msr; | ||
247 | |||
248 | do { | ||
249 | msr = ctrl_inb(EC3104_SER4_MSR); | ||
250 | } while (!(msr & MSR_CTS)); | ||
251 | } | ||
252 | |||
253 | |||
254 | static void e5_send_byte(u8 byte, struct e5_struct *k) | ||
255 | { | ||
256 | u8 status; | ||
257 | |||
258 | do { | ||
259 | status = ctrl_inb(EC3104_SER4_LSR); | ||
260 | } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY); | ||
261 | |||
262 | printk("<%02x>", byte); | ||
263 | |||
264 | ctrl_outb(byte, EC3104_SER4_DATA); | ||
265 | |||
266 | do { | ||
267 | status = ctrl_inb(EC3104_SER4_LSR); | ||
268 | } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY); | ||
269 | |||
270 | } | ||
271 | |||
272 | static int e5_send_packet(u8 *packet, int count, struct e5_struct *k) | ||
273 | { | ||
274 | int i; | ||
275 | |||
276 | disable_irq(EC3104_IRQ_SER4); | ||
277 | |||
278 | if (k->cached_mcr & MCR_RTS) { | ||
279 | printk("e5_send_packet: too slow\n"); | ||
280 | enable_irq(EC3104_IRQ_SER4); | ||
281 | return -EAGAIN; | ||
282 | } | ||
283 | |||
284 | k->cached_mcr |= MCR_RTS; | ||
285 | ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); | ||
286 | |||
287 | e5_wait_for_cts(k); | ||
288 | |||
289 | printk("p: "); | ||
290 | |||
291 | for(i=0; i<count; i++) | ||
292 | e5_send_byte(packet[i], k); | ||
293 | |||
294 | e5_send_byte(e5_checksum(packet, count), k); | ||
295 | |||
296 | printk("\n"); | ||
297 | |||
298 | udelay(1500); | ||
299 | |||
300 | k->cached_mcr &= ~MCR_RTS; | ||
301 | ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); | ||
302 | |||
303 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
304 | |||
305 | |||
306 | |||
307 | enable_irq(EC3104_IRQ_SER4); | ||
308 | |||
309 | |||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * E5 packets we know about: | ||
316 | * E5->host 0x80 0x05 <checksum> - resend packet | ||
317 | * host->E5 0x83 0x43 <contrast> - set LCD contrast | ||
318 | * host->E5 0x85 0x41 0x02 <brightness> 0x02 - set LCD backlight | ||
319 | * E5->host 0x87 <ps2 packet> 0x00 <checksum> - external PS2 | ||
320 | * E5->host 0x88 <scancode> <checksum> - key press | ||
321 | */ | ||
322 | |||
323 | static void e5_receive(struct e5_struct *k) | ||
324 | { | ||
325 | k->packet[k->pos++] = ctrl_inb(EC3104_SER4_DATA); | ||
326 | |||
327 | if (k->pos == 1) { | ||
328 | switch(k->packet[0]) { | ||
329 | case 0x80: | ||
330 | k->length = 3; | ||
331 | break; | ||
332 | |||
333 | case 0x87: /* PS2 ext */ | ||
334 | k->length = 6; | ||
335 | break; | ||
336 | |||
337 | case 0x88: /* keyboard */ | ||
338 | k->length = 3; | ||
339 | break; | ||
340 | |||
341 | default: | ||
342 | k->length = 1; | ||
343 | printk(KERN_WARNING "unknown E5 packet %02x\n", | ||
344 | k->packet[0]); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | if (k->pos == k->length) { | ||
349 | int i; | ||
350 | |||
351 | if (e5_checksum(k->packet, k->length) != 0) | ||
352 | printk(KERN_WARNING "E5: wrong checksum\n"); | ||
353 | |||
354 | #if 0 | ||
355 | printk("E5 packet ["); | ||
356 | for(i=0; i<k->length; i++) { | ||
357 | printk("%02x ", k->packet[i]); | ||
358 | } | ||
359 | |||
360 | printk("(%02x)]\n", e5_checksum(k->packet, k->length-1)); | ||
361 | #endif | ||
362 | |||
363 | switch(k->packet[0]) { | ||
364 | case 0x80: | ||
365 | case 0x88: | ||
366 | handle_keyboard_event(k->packet[1]); | ||
367 | break; | ||
368 | } | ||
369 | |||
370 | k->pos = k->length = 0; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | static void ec3104_keyb_interrupt(int irq, void *data, struct pt_regs *regs) | ||
375 | { | ||
376 | struct e5_struct *k = &ec3104_keyb; | ||
377 | u8 msr, lsr; | ||
378 | |||
379 | msr = ctrl_inb(EC3104_SER4_MSR); | ||
380 | |||
381 | if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) { | ||
382 | if (k->cached_mcr & MCR_RTS) | ||
383 | printk("confused: RTS already high\n"); | ||
384 | /* CTS went high. Send RTS. */ | ||
385 | k->cached_mcr |= MCR_RTS; | ||
386 | |||
387 | ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); | ||
388 | } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) { | ||
389 | /* CTS went low. */ | ||
390 | if (!(k->cached_mcr & MCR_RTS)) | ||
391 | printk("confused: RTS already low\n"); | ||
392 | |||
393 | k->cached_mcr &= ~MCR_RTS; | ||
394 | |||
395 | ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); | ||
396 | } | ||
397 | |||
398 | k->last_msr = msr; | ||
399 | |||
400 | lsr = ctrl_inb(EC3104_SER4_LSR); | ||
401 | |||
402 | if (lsr & LSR_DR) | ||
403 | e5_receive(k); | ||
404 | } | ||
405 | |||
406 | static void ec3104_keyb_clear_state(void) | ||
407 | { | ||
408 | struct e5_struct *k = &ec3104_keyb; | ||
409 | u8 msr, lsr; | ||
410 | |||
411 | /* we want CTS to be low */ | ||
412 | k->last_msr = 0; | ||
413 | |||
414 | for (;;) { | ||
415 | msleep(100); | ||
416 | |||
417 | msr = ctrl_inb(EC3104_SER4_MSR); | ||
418 | |||
419 | lsr = ctrl_inb(EC3104_SER4_LSR); | ||
420 | |||
421 | if (lsr & LSR_DR) { | ||
422 | e5_receive(k); | ||
423 | continue; | ||
424 | } | ||
425 | |||
426 | if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) { | ||
427 | if (k->cached_mcr & MCR_RTS) | ||
428 | printk("confused: RTS already high\n"); | ||
429 | /* CTS went high. Send RTS. */ | ||
430 | k->cached_mcr |= MCR_RTS; | ||
431 | |||
432 | ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); | ||
433 | } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) { | ||
434 | /* CTS went low. */ | ||
435 | if (!(k->cached_mcr & MCR_RTS)) | ||
436 | printk("confused: RTS already low\n"); | ||
437 | |||
438 | k->cached_mcr &= ~MCR_RTS; | ||
439 | |||
440 | ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); | ||
441 | } else | ||
442 | break; | ||
443 | |||
444 | k->last_msr = msr; | ||
445 | |||
446 | continue; | ||
447 | } | ||
448 | } | ||
449 | |||
450 | void __init ec3104_kbd_init_hw(void) | ||
451 | { | ||
452 | ec3104_keyb.last_msr = ctrl_inb(EC3104_SER4_MSR); | ||
453 | ec3104_keyb.cached_mcr = ctrl_inb(EC3104_SER4_MCR); | ||
454 | |||
455 | ec3104_keyb_clear_state(); | ||
456 | |||
457 | /* Ok, finally allocate the IRQ, and off we go.. */ | ||
458 | request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL); | ||
459 | } | ||