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