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/s390/char/keyboard.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/s390/char/keyboard.c')
-rw-r--r-- | drivers/s390/char/keyboard.c | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c new file mode 100644 index 000000000000..fd43d99b45a3 --- /dev/null +++ b/drivers/s390/char/keyboard.c | |||
@@ -0,0 +1,519 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/keyboard.c | ||
3 | * ebcdic keycode functions for s390 console drivers | ||
4 | * | ||
5 | * S390 version | ||
6 | * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
7 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/sysrq.h> | ||
14 | |||
15 | #include <linux/kbd_kern.h> | ||
16 | #include <linux/kbd_diacr.h> | ||
17 | #include <asm/uaccess.h> | ||
18 | |||
19 | #include "keyboard.h" | ||
20 | |||
21 | /* | ||
22 | * Handler Tables. | ||
23 | */ | ||
24 | #define K_HANDLERS\ | ||
25 | k_self, k_fn, k_spec, k_ignore,\ | ||
26 | k_dead, k_ignore, k_ignore, k_ignore,\ | ||
27 | k_ignore, k_ignore, k_ignore, k_ignore,\ | ||
28 | k_ignore, k_ignore, k_ignore, k_ignore | ||
29 | |||
30 | typedef void (k_handler_fn)(struct kbd_data *, unsigned char); | ||
31 | static k_handler_fn K_HANDLERS; | ||
32 | static k_handler_fn *k_handler[16] = { K_HANDLERS }; | ||
33 | |||
34 | /* maximum values each key_handler can handle */ | ||
35 | static const int kbd_max_vals[] = { | ||
36 | 255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0, | ||
37 | NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
38 | }; | ||
39 | static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals); | ||
40 | |||
41 | static unsigned char ret_diacr[NR_DEAD] = { | ||
42 | '`', '\'', '^', '~', '"', ',' | ||
43 | }; | ||
44 | |||
45 | /* | ||
46 | * Alloc/free of kbd_data structures. | ||
47 | */ | ||
48 | struct kbd_data * | ||
49 | kbd_alloc(void) { | ||
50 | struct kbd_data *kbd; | ||
51 | int i, len; | ||
52 | |||
53 | kbd = kmalloc(sizeof(struct kbd_data), GFP_KERNEL); | ||
54 | if (!kbd) | ||
55 | goto out; | ||
56 | memset(kbd, 0, sizeof(struct kbd_data)); | ||
57 | kbd->key_maps = kmalloc(sizeof(key_maps), GFP_KERNEL); | ||
58 | if (!key_maps) | ||
59 | goto out_kbd; | ||
60 | memset(kbd->key_maps, 0, sizeof(key_maps)); | ||
61 | for (i = 0; i < ARRAY_SIZE(key_maps); i++) { | ||
62 | if (key_maps[i]) { | ||
63 | kbd->key_maps[i] = | ||
64 | kmalloc(sizeof(u_short)*NR_KEYS, GFP_KERNEL); | ||
65 | if (!kbd->key_maps[i]) | ||
66 | goto out_maps; | ||
67 | memcpy(kbd->key_maps[i], key_maps[i], | ||
68 | sizeof(u_short)*NR_KEYS); | ||
69 | } | ||
70 | } | ||
71 | kbd->func_table = kmalloc(sizeof(func_table), GFP_KERNEL); | ||
72 | if (!kbd->func_table) | ||
73 | goto out_maps; | ||
74 | memset(kbd->func_table, 0, sizeof(func_table)); | ||
75 | for (i = 0; i < ARRAY_SIZE(func_table); i++) { | ||
76 | if (func_table[i]) { | ||
77 | len = strlen(func_table[i]) + 1; | ||
78 | kbd->func_table[i] = kmalloc(len, GFP_KERNEL); | ||
79 | if (!kbd->func_table[i]) | ||
80 | goto out_func; | ||
81 | memcpy(kbd->func_table[i], func_table[i], len); | ||
82 | } | ||
83 | } | ||
84 | kbd->fn_handler = | ||
85 | kmalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL); | ||
86 | if (!kbd->fn_handler) | ||
87 | goto out_func; | ||
88 | memset(kbd->fn_handler, 0, sizeof(fn_handler_fn *) * NR_FN_HANDLER); | ||
89 | kbd->accent_table = | ||
90 | kmalloc(sizeof(struct kbdiacr)*MAX_DIACR, GFP_KERNEL); | ||
91 | if (!kbd->accent_table) | ||
92 | goto out_fn_handler; | ||
93 | memcpy(kbd->accent_table, accent_table, | ||
94 | sizeof(struct kbdiacr)*MAX_DIACR); | ||
95 | kbd->accent_table_size = accent_table_size; | ||
96 | return kbd; | ||
97 | |||
98 | out_fn_handler: | ||
99 | kfree(kbd->fn_handler); | ||
100 | out_func: | ||
101 | for (i = 0; i < ARRAY_SIZE(func_table); i++) | ||
102 | if (kbd->func_table[i]) | ||
103 | kfree(kbd->func_table[i]); | ||
104 | kfree(kbd->func_table); | ||
105 | out_maps: | ||
106 | for (i = 0; i < ARRAY_SIZE(key_maps); i++) | ||
107 | if (kbd->key_maps[i]) | ||
108 | kfree(kbd->key_maps[i]); | ||
109 | kfree(kbd->key_maps); | ||
110 | out_kbd: | ||
111 | kfree(kbd); | ||
112 | out: | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | void | ||
117 | kbd_free(struct kbd_data *kbd) | ||
118 | { | ||
119 | int i; | ||
120 | |||
121 | kfree(kbd->accent_table); | ||
122 | kfree(kbd->fn_handler); | ||
123 | for (i = 0; i < ARRAY_SIZE(func_table); i++) | ||
124 | if (kbd->func_table[i]) | ||
125 | kfree(kbd->func_table[i]); | ||
126 | kfree(kbd->func_table); | ||
127 | for (i = 0; i < ARRAY_SIZE(key_maps); i++) | ||
128 | if (kbd->key_maps[i]) | ||
129 | kfree(kbd->key_maps[i]); | ||
130 | kfree(kbd->key_maps); | ||
131 | kfree(kbd); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Generate ascii -> ebcdic translation table from kbd_data. | ||
136 | */ | ||
137 | void | ||
138 | kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc) | ||
139 | { | ||
140 | unsigned short *keymap, keysym; | ||
141 | int i, j, k; | ||
142 | |||
143 | memset(ascebc, 0x40, 256); | ||
144 | for (i = 0; i < ARRAY_SIZE(key_maps); i++) { | ||
145 | keymap = kbd->key_maps[i]; | ||
146 | if (!keymap) | ||
147 | continue; | ||
148 | for (j = 0; j < NR_KEYS; j++) { | ||
149 | k = ((i & 1) << 7) + j; | ||
150 | keysym = keymap[j]; | ||
151 | if (KTYP(keysym) == (KT_LATIN | 0xf0) || | ||
152 | KTYP(keysym) == (KT_LETTER | 0xf0)) | ||
153 | ascebc[KVAL(keysym)] = k; | ||
154 | else if (KTYP(keysym) == (KT_DEAD | 0xf0)) | ||
155 | ascebc[ret_diacr[KVAL(keysym)]] = k; | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * Generate ebcdic -> ascii translation table from kbd_data. | ||
162 | */ | ||
163 | void | ||
164 | kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc) | ||
165 | { | ||
166 | unsigned short *keymap, keysym; | ||
167 | int i, j, k; | ||
168 | |||
169 | memset(ebcasc, ' ', 256); | ||
170 | for (i = 0; i < ARRAY_SIZE(key_maps); i++) { | ||
171 | keymap = kbd->key_maps[i]; | ||
172 | if (!keymap) | ||
173 | continue; | ||
174 | for (j = 0; j < NR_KEYS; j++) { | ||
175 | keysym = keymap[j]; | ||
176 | k = ((i & 1) << 7) + j; | ||
177 | if (KTYP(keysym) == (KT_LATIN | 0xf0) || | ||
178 | KTYP(keysym) == (KT_LETTER | 0xf0)) | ||
179 | ebcasc[k] = KVAL(keysym); | ||
180 | else if (KTYP(keysym) == (KT_DEAD | 0xf0)) | ||
181 | ebcasc[k] = ret_diacr[KVAL(keysym)]; | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * We have a combining character DIACR here, followed by the character CH. | ||
188 | * If the combination occurs in the table, return the corresponding value. | ||
189 | * Otherwise, if CH is a space or equals DIACR, return DIACR. | ||
190 | * Otherwise, conclude that DIACR was not combining after all, | ||
191 | * queue it and return CH. | ||
192 | */ | ||
193 | static unsigned char | ||
194 | handle_diacr(struct kbd_data *kbd, unsigned char ch) | ||
195 | { | ||
196 | int i, d; | ||
197 | |||
198 | d = kbd->diacr; | ||
199 | kbd->diacr = 0; | ||
200 | |||
201 | for (i = 0; i < kbd->accent_table_size; i++) { | ||
202 | if (kbd->accent_table[i].diacr == d && | ||
203 | kbd->accent_table[i].base == ch) | ||
204 | return kbd->accent_table[i].result; | ||
205 | } | ||
206 | |||
207 | if (ch == ' ' || ch == d) | ||
208 | return d; | ||
209 | |||
210 | kbd_put_queue(kbd->tty, d); | ||
211 | return ch; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * Handle dead key. | ||
216 | */ | ||
217 | static void | ||
218 | k_dead(struct kbd_data *kbd, unsigned char value) | ||
219 | { | ||
220 | value = ret_diacr[value]; | ||
221 | kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value); | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Normal character handler. | ||
226 | */ | ||
227 | static void | ||
228 | k_self(struct kbd_data *kbd, unsigned char value) | ||
229 | { | ||
230 | if (kbd->diacr) | ||
231 | value = handle_diacr(kbd, value); | ||
232 | kbd_put_queue(kbd->tty, value); | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Special key handlers | ||
237 | */ | ||
238 | static void | ||
239 | k_ignore(struct kbd_data *kbd, unsigned char value) | ||
240 | { | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * Function key handler. | ||
245 | */ | ||
246 | static void | ||
247 | k_fn(struct kbd_data *kbd, unsigned char value) | ||
248 | { | ||
249 | if (kbd->func_table[value]) | ||
250 | kbd_puts_queue(kbd->tty, kbd->func_table[value]); | ||
251 | } | ||
252 | |||
253 | static void | ||
254 | k_spec(struct kbd_data *kbd, unsigned char value) | ||
255 | { | ||
256 | if (value >= NR_FN_HANDLER) | ||
257 | return; | ||
258 | if (kbd->fn_handler[value]) | ||
259 | kbd->fn_handler[value](kbd); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * Put utf8 character to tty flip buffer. | ||
264 | * UTF-8 is defined for words of up to 31 bits, | ||
265 | * but we need only 16 bits here | ||
266 | */ | ||
267 | static void | ||
268 | to_utf8(struct tty_struct *tty, ushort c) | ||
269 | { | ||
270 | if (c < 0x80) | ||
271 | /* 0******* */ | ||
272 | kbd_put_queue(tty, c); | ||
273 | else if (c < 0x800) { | ||
274 | /* 110***** 10****** */ | ||
275 | kbd_put_queue(tty, 0xc0 | (c >> 6)); | ||
276 | kbd_put_queue(tty, 0x80 | (c & 0x3f)); | ||
277 | } else { | ||
278 | /* 1110**** 10****** 10****** */ | ||
279 | kbd_put_queue(tty, 0xe0 | (c >> 12)); | ||
280 | kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f)); | ||
281 | kbd_put_queue(tty, 0x80 | (c & 0x3f)); | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * Process keycode. | ||
287 | */ | ||
288 | void | ||
289 | kbd_keycode(struct kbd_data *kbd, unsigned int keycode) | ||
290 | { | ||
291 | unsigned short keysym; | ||
292 | unsigned char type, value; | ||
293 | |||
294 | if (!kbd || !kbd->tty) | ||
295 | return; | ||
296 | |||
297 | if (keycode >= 384) | ||
298 | keysym = kbd->key_maps[5][keycode - 384]; | ||
299 | else if (keycode >= 256) | ||
300 | keysym = kbd->key_maps[4][keycode - 256]; | ||
301 | else if (keycode >= 128) | ||
302 | keysym = kbd->key_maps[1][keycode - 128]; | ||
303 | else | ||
304 | keysym = kbd->key_maps[0][keycode]; | ||
305 | |||
306 | type = KTYP(keysym); | ||
307 | if (type >= 0xf0) { | ||
308 | type -= 0xf0; | ||
309 | if (type == KT_LETTER) | ||
310 | type = KT_LATIN; | ||
311 | value = KVAL(keysym); | ||
312 | #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ | ||
313 | if (kbd->sysrq) { | ||
314 | if (kbd->sysrq == K(KT_LATIN, '-')) { | ||
315 | kbd->sysrq = 0; | ||
316 | handle_sysrq(value, 0, kbd->tty); | ||
317 | return; | ||
318 | } | ||
319 | if (value == '-') { | ||
320 | kbd->sysrq = K(KT_LATIN, '-'); | ||
321 | return; | ||
322 | } | ||
323 | /* Incomplete sysrq sequence. */ | ||
324 | (*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq)); | ||
325 | kbd->sysrq = 0; | ||
326 | } else if ((type == KT_LATIN && value == '^') || | ||
327 | (type == KT_DEAD && ret_diacr[value] == '^')) { | ||
328 | kbd->sysrq = K(type, value); | ||
329 | return; | ||
330 | } | ||
331 | #endif | ||
332 | (*k_handler[type])(kbd, value); | ||
333 | } else | ||
334 | to_utf8(kbd->tty, keysym); | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * Ioctl stuff. | ||
339 | */ | ||
340 | static int | ||
341 | do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe, | ||
342 | int cmd, int perm) | ||
343 | { | ||
344 | struct kbentry tmp; | ||
345 | ushort *key_map, val, ov; | ||
346 | |||
347 | if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) | ||
348 | return -EFAULT; | ||
349 | #if NR_KEYS < 256 | ||
350 | if (tmp.kb_index >= NR_KEYS) | ||
351 | return -EINVAL; | ||
352 | #endif | ||
353 | #if MAX_NR_KEYMAPS < 256 | ||
354 | if (tmp.kb_table >= MAX_NR_KEYMAPS) | ||
355 | return -EINVAL; | ||
356 | #endif | ||
357 | |||
358 | switch (cmd) { | ||
359 | case KDGKBENT: | ||
360 | key_map = kbd->key_maps[tmp.kb_table]; | ||
361 | if (key_map) { | ||
362 | val = U(key_map[tmp.kb_index]); | ||
363 | if (KTYP(val) >= KBD_NR_TYPES) | ||
364 | val = K_HOLE; | ||
365 | } else | ||
366 | val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP); | ||
367 | return put_user(val, &user_kbe->kb_value); | ||
368 | case KDSKBENT: | ||
369 | if (!perm) | ||
370 | return -EPERM; | ||
371 | if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) { | ||
372 | /* disallocate map */ | ||
373 | key_map = kbd->key_maps[tmp.kb_table]; | ||
374 | if (key_map) { | ||
375 | kbd->key_maps[tmp.kb_table] = 0; | ||
376 | kfree(key_map); | ||
377 | } | ||
378 | break; | ||
379 | } | ||
380 | |||
381 | if (KTYP(tmp.kb_value) >= KBD_NR_TYPES) | ||
382 | return -EINVAL; | ||
383 | if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)]) | ||
384 | return -EINVAL; | ||
385 | |||
386 | if (!(key_map = kbd->key_maps[tmp.kb_table])) { | ||
387 | int j; | ||
388 | |||
389 | key_map = (ushort *) kmalloc(sizeof(plain_map), | ||
390 | GFP_KERNEL); | ||
391 | if (!key_map) | ||
392 | return -ENOMEM; | ||
393 | kbd->key_maps[tmp.kb_table] = key_map; | ||
394 | for (j = 0; j < NR_KEYS; j++) | ||
395 | key_map[j] = U(K_HOLE); | ||
396 | } | ||
397 | ov = U(key_map[tmp.kb_index]); | ||
398 | if (tmp.kb_value == ov) | ||
399 | break; /* nothing to do */ | ||
400 | /* | ||
401 | * Attention Key. | ||
402 | */ | ||
403 | if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) && | ||
404 | !capable(CAP_SYS_ADMIN)) | ||
405 | return -EPERM; | ||
406 | key_map[tmp.kb_index] = U(tmp.kb_value); | ||
407 | break; | ||
408 | } | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int | ||
413 | do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs, | ||
414 | int cmd, int perm) | ||
415 | { | ||
416 | unsigned char kb_func; | ||
417 | char *p; | ||
418 | int len; | ||
419 | |||
420 | /* Get u_kbs->kb_func. */ | ||
421 | if (get_user(kb_func, &u_kbs->kb_func)) | ||
422 | return -EFAULT; | ||
423 | #if MAX_NR_FUNC < 256 | ||
424 | if (kb_func >= MAX_NR_FUNC) | ||
425 | return -EINVAL; | ||
426 | #endif | ||
427 | |||
428 | switch (cmd) { | ||
429 | case KDGKBSENT: | ||
430 | p = kbd->func_table[kb_func]; | ||
431 | if (p) { | ||
432 | len = strlen(p); | ||
433 | if (len >= sizeof(u_kbs->kb_string)) | ||
434 | len = sizeof(u_kbs->kb_string) - 1; | ||
435 | if (copy_to_user(u_kbs->kb_string, p, len)) | ||
436 | return -EFAULT; | ||
437 | } else | ||
438 | len = 0; | ||
439 | if (put_user('\0', u_kbs->kb_string + len)) | ||
440 | return -EFAULT; | ||
441 | break; | ||
442 | case KDSKBSENT: | ||
443 | if (!perm) | ||
444 | return -EPERM; | ||
445 | len = strnlen_user(u_kbs->kb_string, | ||
446 | sizeof(u_kbs->kb_string) - 1); | ||
447 | p = kmalloc(len, GFP_KERNEL); | ||
448 | if (!p) | ||
449 | return -ENOMEM; | ||
450 | if (copy_from_user(p, u_kbs->kb_string, len)) { | ||
451 | kfree(p); | ||
452 | return -EFAULT; | ||
453 | } | ||
454 | p[len] = 0; | ||
455 | if (kbd->func_table[kb_func]) | ||
456 | kfree(kbd->func_table[kb_func]); | ||
457 | kbd->func_table[kb_func] = p; | ||
458 | break; | ||
459 | } | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | int | ||
464 | kbd_ioctl(struct kbd_data *kbd, struct file *file, | ||
465 | unsigned int cmd, unsigned long arg) | ||
466 | { | ||
467 | struct kbdiacrs __user *a; | ||
468 | void __user *argp; | ||
469 | int ct, perm; | ||
470 | |||
471 | argp = (void __user *)arg; | ||
472 | |||
473 | /* | ||
474 | * To have permissions to do most of the vt ioctls, we either have | ||
475 | * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. | ||
476 | */ | ||
477 | perm = current->signal->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG); | ||
478 | switch (cmd) { | ||
479 | case KDGKBTYPE: | ||
480 | return put_user(KB_101, (char __user *)argp); | ||
481 | case KDGKBENT: | ||
482 | case KDSKBENT: | ||
483 | return do_kdsk_ioctl(kbd, argp, cmd, perm); | ||
484 | case KDGKBSENT: | ||
485 | case KDSKBSENT: | ||
486 | return do_kdgkb_ioctl(kbd, argp, cmd, perm); | ||
487 | case KDGKBDIACR: | ||
488 | a = argp; | ||
489 | |||
490 | if (put_user(kbd->accent_table_size, &a->kb_cnt)) | ||
491 | return -EFAULT; | ||
492 | ct = kbd->accent_table_size; | ||
493 | if (copy_to_user(a->kbdiacr, kbd->accent_table, | ||
494 | ct * sizeof(struct kbdiacr))) | ||
495 | return -EFAULT; | ||
496 | return 0; | ||
497 | case KDSKBDIACR: | ||
498 | a = argp; | ||
499 | if (!perm) | ||
500 | return -EPERM; | ||
501 | if (get_user(ct, &a->kb_cnt)) | ||
502 | return -EFAULT; | ||
503 | if (ct >= MAX_DIACR) | ||
504 | return -EINVAL; | ||
505 | kbd->accent_table_size = ct; | ||
506 | if (copy_from_user(kbd->accent_table, a->kbdiacr, | ||
507 | ct * sizeof(struct kbdiacr))) | ||
508 | return -EFAULT; | ||
509 | return 0; | ||
510 | default: | ||
511 | return -ENOIOCTLCMD; | ||
512 | } | ||
513 | } | ||
514 | |||
515 | EXPORT_SYMBOL(kbd_ioctl); | ||
516 | EXPORT_SYMBOL(kbd_ascebc); | ||
517 | EXPORT_SYMBOL(kbd_free); | ||
518 | EXPORT_SYMBOL(kbd_alloc); | ||
519 | EXPORT_SYMBOL(kbd_keycode); | ||