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/tc/lk201.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/tc/lk201.c')
-rw-r--r-- | drivers/tc/lk201.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/drivers/tc/lk201.c b/drivers/tc/lk201.c new file mode 100644 index 000000000000..cf10d5cdfb93 --- /dev/null +++ b/drivers/tc/lk201.c | |||
@@ -0,0 +1,441 @@ | |||
1 | /* | ||
2 | * | ||
3 | * This file is subject to the terms and conditions of the GNU General Public | ||
4 | * License. See the file "COPYING" in the main directory of this archive | ||
5 | * for more details. | ||
6 | * | ||
7 | * Copyright (C) 1999-2002 Harald Koerfgen <hkoerfg@web.de> | ||
8 | * Copyright (C) 2001, 2002, 2003, 2004 Maciej W. Rozycki | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/tty.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/kbd_ll.h> | ||
20 | #include <linux/kbd_kern.h> | ||
21 | #include <linux/vt_kern.h> | ||
22 | |||
23 | #include <asm/keyboard.h> | ||
24 | #include <asm/dec/tc.h> | ||
25 | #include <asm/dec/machtype.h> | ||
26 | #include <asm/dec/serial.h> | ||
27 | |||
28 | #include "lk201.h" | ||
29 | |||
30 | /* | ||
31 | * Only handle DECstations that have an LK201 interface. | ||
32 | * Maxine uses LK501 at the Access.Bus and various DECsystems | ||
33 | * have no keyboard interface at all. | ||
34 | */ | ||
35 | #define LK_IFACE (mips_machtype == MACH_DS23100 || \ | ||
36 | mips_machtype == MACH_DS5000_200 || \ | ||
37 | mips_machtype == MACH_DS5000_1XX || \ | ||
38 | mips_machtype == MACH_DS5000_2X0) | ||
39 | /* | ||
40 | * These use the Z8530 SCC. Others use the DZ11. | ||
41 | */ | ||
42 | #define LK_IFACE_ZS (mips_machtype == MACH_DS5000_1XX || \ | ||
43 | mips_machtype == MACH_DS5000_2X0) | ||
44 | |||
45 | /* Simple translation table for the SysRq keys */ | ||
46 | |||
47 | #ifdef CONFIG_MAGIC_SYSRQ | ||
48 | /* | ||
49 | * Actually no translation at all, at least until we figure out | ||
50 | * how to define SysRq for LK201 and friends. --macro | ||
51 | */ | ||
52 | unsigned char lk201_sysrq_xlate[128]; | ||
53 | unsigned char *kbd_sysrq_xlate = lk201_sysrq_xlate; | ||
54 | |||
55 | unsigned char kbd_sysrq_key = -1; | ||
56 | #endif | ||
57 | |||
58 | #define KEYB_LINE 3 | ||
59 | |||
60 | static int __init lk201_init(void *); | ||
61 | static void __init lk201_info(void *); | ||
62 | static void lk201_rx_char(unsigned char, unsigned char); | ||
63 | |||
64 | static struct dec_serial_hook lk201_hook = { | ||
65 | .init_channel = lk201_init, | ||
66 | .init_info = lk201_info, | ||
67 | .rx_char = NULL, | ||
68 | .poll_rx_char = NULL, | ||
69 | .poll_tx_char = NULL, | ||
70 | .cflags = B4800 | CS8 | CSTOPB | CLOCAL, | ||
71 | }; | ||
72 | |||
73 | /* | ||
74 | * This is used during keyboard initialisation | ||
75 | */ | ||
76 | static unsigned char lk201_reset_string[] = { | ||
77 | LK_CMD_SET_DEFAULTS, | ||
78 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 1), | ||
79 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 2), | ||
80 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 3), | ||
81 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 4), | ||
82 | LK_CMD_MODE(LK_MODE_DOWN_UP, 5), | ||
83 | LK_CMD_MODE(LK_MODE_DOWN_UP, 6), | ||
84 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 7), | ||
85 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 8), | ||
86 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 9), | ||
87 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 10), | ||
88 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 11), | ||
89 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 12), | ||
90 | LK_CMD_MODE(LK_MODE_DOWN, 13), | ||
91 | LK_CMD_MODE(LK_MODE_RPT_DOWN, 14), | ||
92 | LK_CMD_DIS_KEYCLK, | ||
93 | LK_CMD_ENB_BELL, LK_PARAM_VOLUME(4), | ||
94 | }; | ||
95 | |||
96 | static void *lk201_handle; | ||
97 | |||
98 | static int lk201_send(unsigned char ch) | ||
99 | { | ||
100 | if (lk201_hook.poll_tx_char(lk201_handle, ch)) { | ||
101 | printk(KERN_ERR "lk201: transmit timeout\n"); | ||
102 | return -EIO; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static inline int lk201_get_id(void) | ||
108 | { | ||
109 | return lk201_send(LK_CMD_REQ_ID); | ||
110 | } | ||
111 | |||
112 | static int lk201_reset(void) | ||
113 | { | ||
114 | int i, r; | ||
115 | |||
116 | for (i = 0; i < sizeof(lk201_reset_string); i++) { | ||
117 | r = lk201_send(lk201_reset_string[i]); | ||
118 | if (r < 0) | ||
119 | return r; | ||
120 | } | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static void lk201_report(unsigned char id[6]) | ||
125 | { | ||
126 | char *report = "lk201: keyboard attached, "; | ||
127 | |||
128 | switch (id[2]) { | ||
129 | case LK_STAT_PWRUP_OK: | ||
130 | printk(KERN_INFO "%sself-test OK\n", report); | ||
131 | break; | ||
132 | case LK_STAT_PWRUP_KDOWN: | ||
133 | /* The keyboard will resend the power-up ID | ||
134 | after all keys are released, so we don't | ||
135 | bother handling the error specially. Still | ||
136 | there may be a short-circuit inside. | ||
137 | */ | ||
138 | printk(KERN_ERR "%skey down (stuck?), code: 0x%02x\n", | ||
139 | report, id[3]); | ||
140 | break; | ||
141 | case LK_STAT_PWRUP_ERROR: | ||
142 | printk(KERN_ERR "%sself-test failure\n", report); | ||
143 | break; | ||
144 | default: | ||
145 | printk(KERN_ERR "%sunknown error: 0x%02x\n", | ||
146 | report, id[2]); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static void lk201_id(unsigned char id[6]) | ||
151 | { | ||
152 | /* | ||
153 | * Report whether there is an LK201 or an LK401 | ||
154 | * The LK401 has ALT keys... | ||
155 | */ | ||
156 | switch (id[4]) { | ||
157 | case 1: | ||
158 | printk(KERN_INFO "lk201: LK201 detected\n"); | ||
159 | break; | ||
160 | case 2: | ||
161 | printk(KERN_INFO "lk201: LK401 detected\n"); | ||
162 | break; | ||
163 | case 3: | ||
164 | printk(KERN_INFO "lk201: LK443 detected\n"); | ||
165 | break; | ||
166 | case 4: | ||
167 | printk(KERN_INFO "lk201: LK421 detected\n"); | ||
168 | break; | ||
169 | default: | ||
170 | printk(KERN_WARNING | ||
171 | "lk201: unknown keyboard detected, ID %d\n", id[4]); | ||
172 | printk(KERN_WARNING "lk201: ... please report to " | ||
173 | "<linux-mips@linux-mips.org>\n"); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | #define DEFAULT_KEYB_REP_DELAY (250/5) /* [5ms] */ | ||
178 | #define DEFAULT_KEYB_REP_RATE 30 /* [cps] */ | ||
179 | |||
180 | static struct kbd_repeat kbdrate = { | ||
181 | DEFAULT_KEYB_REP_DELAY, | ||
182 | DEFAULT_KEYB_REP_RATE | ||
183 | }; | ||
184 | |||
185 | static void parse_kbd_rate(struct kbd_repeat *r) | ||
186 | { | ||
187 | if (r->delay <= 0) | ||
188 | r->delay = kbdrate.delay; | ||
189 | if (r->rate <= 0) | ||
190 | r->rate = kbdrate.rate; | ||
191 | |||
192 | if (r->delay < 5) | ||
193 | r->delay = 5; | ||
194 | if (r->delay > 630) | ||
195 | r->delay = 630; | ||
196 | if (r->rate < 12) | ||
197 | r->rate = 12; | ||
198 | if (r->rate > 127) | ||
199 | r->rate = 127; | ||
200 | if (r->rate == 125) | ||
201 | r->rate = 124; | ||
202 | } | ||
203 | |||
204 | static int write_kbd_rate(struct kbd_repeat *rep) | ||
205 | { | ||
206 | int delay, rate; | ||
207 | int i; | ||
208 | |||
209 | delay = rep->delay / 5; | ||
210 | rate = rep->rate; | ||
211 | for (i = 0; i < 4; i++) { | ||
212 | if (lk201_hook.poll_tx_char(lk201_handle, | ||
213 | LK_CMD_RPT_RATE(i))) | ||
214 | return 1; | ||
215 | if (lk201_hook.poll_tx_char(lk201_handle, | ||
216 | LK_PARAM_DELAY(delay))) | ||
217 | return 1; | ||
218 | if (lk201_hook.poll_tx_char(lk201_handle, | ||
219 | LK_PARAM_RATE(rate))) | ||
220 | return 1; | ||
221 | } | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int lk201_kbd_rate(struct kbd_repeat *rep) | ||
226 | { | ||
227 | if (rep == NULL) | ||
228 | return -EINVAL; | ||
229 | |||
230 | parse_kbd_rate(rep); | ||
231 | |||
232 | if (write_kbd_rate(rep)) { | ||
233 | memcpy(rep, &kbdrate, sizeof(struct kbd_repeat)); | ||
234 | return -EIO; | ||
235 | } | ||
236 | |||
237 | memcpy(&kbdrate, rep, sizeof(struct kbd_repeat)); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static void lk201_kd_mksound(unsigned int hz, unsigned int ticks) | ||
243 | { | ||
244 | if (!ticks) | ||
245 | return; | ||
246 | |||
247 | /* | ||
248 | * Can't set frequency and we "approximate" | ||
249 | * duration by volume. ;-) | ||
250 | */ | ||
251 | ticks /= HZ / 32; | ||
252 | if (ticks > 7) | ||
253 | ticks = 7; | ||
254 | ticks = 7 - ticks; | ||
255 | |||
256 | if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_ENB_BELL)) | ||
257 | return; | ||
258 | if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_VOLUME(ticks))) | ||
259 | return; | ||
260 | if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_BELL)) | ||
261 | return; | ||
262 | } | ||
263 | |||
264 | void kbd_leds(unsigned char leds) | ||
265 | { | ||
266 | unsigned char l = 0; | ||
267 | |||
268 | if (!lk201_handle) /* FIXME */ | ||
269 | return; | ||
270 | |||
271 | /* FIXME -- Only Hold and Lock LEDs for now. --macro */ | ||
272 | if (leds & LED_SCR) | ||
273 | l |= LK_LED_HOLD; | ||
274 | if (leds & LED_CAP) | ||
275 | l |= LK_LED_LOCK; | ||
276 | |||
277 | if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_LEDS_ON)) | ||
278 | return; | ||
279 | if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_LED_MASK(l))) | ||
280 | return; | ||
281 | if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_LEDS_OFF)) | ||
282 | return; | ||
283 | if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_LED_MASK(~l))) | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | int kbd_setkeycode(unsigned int scancode, unsigned int keycode) | ||
288 | { | ||
289 | return -EINVAL; | ||
290 | } | ||
291 | |||
292 | int kbd_getkeycode(unsigned int scancode) | ||
293 | { | ||
294 | return -EINVAL; | ||
295 | } | ||
296 | |||
297 | int kbd_translate(unsigned char scancode, unsigned char *keycode, | ||
298 | char raw_mode) | ||
299 | { | ||
300 | *keycode = scancode; | ||
301 | return 1; | ||
302 | } | ||
303 | |||
304 | char kbd_unexpected_up(unsigned char keycode) | ||
305 | { | ||
306 | return 0x80; | ||
307 | } | ||
308 | |||
309 | static void lk201_rx_char(unsigned char ch, unsigned char fl) | ||
310 | { | ||
311 | static unsigned char id[6]; | ||
312 | static int id_i; | ||
313 | |||
314 | static int shift_state = 0; | ||
315 | static int prev_scancode; | ||
316 | unsigned char c = scancodeRemap[ch]; | ||
317 | |||
318 | if (fl != TTY_NORMAL && fl != TTY_OVERRUN) { | ||
319 | printk(KERN_ERR "lk201: keyboard receive error: 0x%02x\n", fl); | ||
320 | return; | ||
321 | } | ||
322 | |||
323 | /* Assume this is a power-up ID. */ | ||
324 | if (ch == LK_STAT_PWRUP_ID && !id_i) { | ||
325 | id[id_i++] = ch; | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | /* Handle the power-up sequence. */ | ||
330 | if (id_i) { | ||
331 | id[id_i++] = ch; | ||
332 | if (id_i == 4) { | ||
333 | /* OK, the power-up concluded. */ | ||
334 | lk201_report(id); | ||
335 | if (id[2] == LK_STAT_PWRUP_OK) | ||
336 | lk201_get_id(); | ||
337 | else { | ||
338 | id_i = 0; | ||
339 | printk(KERN_ERR "lk201: keyboard power-up " | ||
340 | "error, skipping initialization\n"); | ||
341 | } | ||
342 | } else if (id_i == 6) { | ||
343 | /* We got the ID; report it and start operation. */ | ||
344 | id_i = 0; | ||
345 | lk201_id(id); | ||
346 | lk201_reset(); | ||
347 | } | ||
348 | return; | ||
349 | } | ||
350 | |||
351 | /* Everything else is a scancode/status response. */ | ||
352 | id_i = 0; | ||
353 | switch (ch) { | ||
354 | case LK_STAT_RESUME_ERR: | ||
355 | case LK_STAT_ERROR: | ||
356 | case LK_STAT_INHIBIT_ACK: | ||
357 | case LK_STAT_TEST_ACK: | ||
358 | case LK_STAT_MODE_KEYDOWN: | ||
359 | case LK_STAT_MODE_ACK: | ||
360 | break; | ||
361 | case LK_KEY_LOCK: | ||
362 | shift_state ^= LK_LOCK; | ||
363 | handle_scancode(c, (shift_state & LK_LOCK) ? 1 : 0); | ||
364 | break; | ||
365 | case LK_KEY_SHIFT: | ||
366 | shift_state ^= LK_SHIFT; | ||
367 | handle_scancode(c, (shift_state & LK_SHIFT) ? 1 : 0); | ||
368 | break; | ||
369 | case LK_KEY_CTRL: | ||
370 | shift_state ^= LK_CTRL; | ||
371 | handle_scancode(c, (shift_state & LK_CTRL) ? 1 : 0); | ||
372 | break; | ||
373 | case LK_KEY_COMP: | ||
374 | shift_state ^= LK_COMP; | ||
375 | handle_scancode(c, (shift_state & LK_COMP) ? 1 : 0); | ||
376 | break; | ||
377 | case LK_KEY_RELEASE: | ||
378 | if (shift_state & LK_SHIFT) | ||
379 | handle_scancode(scancodeRemap[LK_KEY_SHIFT], 0); | ||
380 | if (shift_state & LK_CTRL) | ||
381 | handle_scancode(scancodeRemap[LK_KEY_CTRL], 0); | ||
382 | if (shift_state & LK_COMP) | ||
383 | handle_scancode(scancodeRemap[LK_KEY_COMP], 0); | ||
384 | if (shift_state & LK_LOCK) | ||
385 | handle_scancode(scancodeRemap[LK_KEY_LOCK], 0); | ||
386 | shift_state = 0; | ||
387 | break; | ||
388 | case LK_KEY_REPEAT: | ||
389 | handle_scancode(prev_scancode, 1); | ||
390 | break; | ||
391 | default: | ||
392 | prev_scancode = c; | ||
393 | handle_scancode(c, 1); | ||
394 | break; | ||
395 | } | ||
396 | tasklet_schedule(&keyboard_tasklet); | ||
397 | } | ||
398 | |||
399 | static void __init lk201_info(void *handle) | ||
400 | { | ||
401 | } | ||
402 | |||
403 | static int __init lk201_init(void *handle) | ||
404 | { | ||
405 | /* First install handlers. */ | ||
406 | lk201_handle = handle; | ||
407 | kbd_rate = lk201_kbd_rate; | ||
408 | kd_mksound = lk201_kd_mksound; | ||
409 | |||
410 | lk201_hook.rx_char = lk201_rx_char; | ||
411 | |||
412 | /* Then just issue a reset -- the handlers will do the rest. */ | ||
413 | lk201_send(LK_CMD_POWER_UP); | ||
414 | |||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | void __init kbd_init_hw(void) | ||
419 | { | ||
420 | /* Maxine uses LK501 at the Access.Bus. */ | ||
421 | if (!LK_IFACE) | ||
422 | return; | ||
423 | |||
424 | printk(KERN_INFO "lk201: DECstation LK keyboard driver v0.05.\n"); | ||
425 | |||
426 | if (LK_IFACE_ZS) { | ||
427 | /* | ||
428 | * kbd_init_hw() is being called before | ||
429 | * rs_init() so just register the kbd hook | ||
430 | * and let zs_init do the rest :-) | ||
431 | */ | ||
432 | if (!register_dec_serial_hook(KEYB_LINE, &lk201_hook)) | ||
433 | unregister_dec_serial_hook(KEYB_LINE); | ||
434 | } else { | ||
435 | /* | ||
436 | * TODO: modify dz.c to allow similar hooks | ||
437 | * for LK201 handling on DS2100, DS3100, and DS5000/200 | ||
438 | */ | ||
439 | printk(KERN_ERR "lk201: support for DZ11 not yet ready.\n"); | ||
440 | } | ||
441 | } | ||