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