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