11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  drivers/s390/char/keyboard.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    ebcdic keycode functions for s390 console drivers
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  S390 version
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
125a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sysrq.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault#include <linux/consolemap.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kbd_kern.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kbd_diacr.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "keyboard.h"
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Handler Tables.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define K_HANDLERS\
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	k_self,		k_fn,		k_spec,		k_ignore,\
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	k_dead,		k_ignore,	k_ignore,	k_ignore,\
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	k_ignore,	k_ignore,	k_ignore,	k_ignore,\
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	k_ignore,	k_ignore,	k_ignore,	k_ignore
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldstypedef void (k_handler_fn)(struct kbd_data *, unsigned char);
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic k_handler_fn K_HANDLERS;
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic k_handler_fn *k_handler[16] = { K_HANDLERS };
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* maximum values each key_handler can handle */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const int kbd_max_vals[] = {
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0,
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char ret_diacr[NR_DEAD] = {
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	'`', '\'', '^', '~', '"', ','
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Alloc/free of kbd_data structures.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct kbd_data *
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_alloc(void) {
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kbd_data *kbd;
52777a5510093a6d6443351160c6969a0e66f3ba8aJulia Lawall	int i;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5488abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn	kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL);
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd)
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
5788abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn	kbd->key_maps = kzalloc(sizeof(key_maps), GFP_KERNEL);
58da074d0ac8ccae1068dc227ef9893c2510d23bd8Peter Oberparleiter	if (!kbd->key_maps)
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_kbd;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (key_maps[i]) {
62939e379e9e183ae6291ac7caa4a5e1dfadae4cccJulia Lawall			kbd->key_maps[i] = kmemdup(key_maps[i],
63939e379e9e183ae6291ac7caa4a5e1dfadae4cccJulia Lawall						   sizeof(u_short) * NR_KEYS,
64939e379e9e183ae6291ac7caa4a5e1dfadae4cccJulia Lawall						   GFP_KERNEL);
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!kbd->key_maps[i])
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out_maps;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6988abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn	kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd->func_table)
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_maps;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(func_table); i++) {
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (func_table[i]) {
74777a5510093a6d6443351160c6969a0e66f3ba8aJulia Lawall			kbd->func_table[i] = kstrdup(func_table[i],
75777a5510093a6d6443351160c6969a0e66f3ba8aJulia Lawall						     GFP_KERNEL);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!kbd->func_table[i])
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out_func;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->fn_handler =
8188abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn		kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL);
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd->fn_handler)
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_func;
84939e379e9e183ae6291ac7caa4a5e1dfadae4cccJulia Lawall	kbd->accent_table = kmemdup(accent_table,
85939e379e9e183ae6291ac7caa4a5e1dfadae4cccJulia Lawall				    sizeof(struct kbdiacruc) * MAX_DIACR,
86939e379e9e183ae6291ac7caa4a5e1dfadae4cccJulia Lawall				    GFP_KERNEL);
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd->accent_table)
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_fn_handler;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->accent_table_size = accent_table_size;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return kbd;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_fn_handler:
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->fn_handler);
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_func:
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(func_table); i++)
9617fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->func_table[i]);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->func_table);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_maps:
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++)
10017fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->key_maps[i]);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->key_maps);
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_kbd:
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd);
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
105d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens	return NULL;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_free(struct kbd_data *kbd)
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->accent_table);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->fn_handler);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(func_table); i++)
11617fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->func_table[i]);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->func_table);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++)
11917fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->key_maps[i]);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->key_maps);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd);
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Generate ascii -> ebcdic translation table from kbd_data.
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short *keymap, keysym;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j, k;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(ascebc, 0x40, 256);
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keymap = kbd->key_maps[i];
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!keymap)
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0; j < NR_KEYS; j++) {
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			k = ((i & 1) << 7) + j;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			keysym = keymap[j];
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    KTYP(keysym) == (KT_LETTER | 0xf0))
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ascebc[KVAL(keysym)] = k;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else if (KTYP(keysym) == (KT_DEAD | 0xf0))
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ascebc[ret_diacr[KVAL(keysym)]] = k;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1502b67fc46061b2171fb8fbb55d1ac717abd533569Heiko Carstens#if 0
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Generate ebcdic -> ascii translation table from kbd_data.
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short *keymap, keysym;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j, k;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(ebcasc, ' ', 256);
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keymap = kbd->key_maps[i];
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!keymap)
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0; j < NR_KEYS; j++) {
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			keysym = keymap[j];
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			k = ((i & 1) << 7) + j;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    KTYP(keysym) == (KT_LETTER | 0xf0))
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ebcasc[k] = KVAL(keysym);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else if (KTYP(keysym) == (KT_DEAD | 0xf0))
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ebcasc[k] = ret_diacr[KVAL(keysym)];
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1762b67fc46061b2171fb8fbb55d1ac717abd533569Heiko Carstens#endif
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We have a combining character DIACR here, followed by the character CH.
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If the combination occurs in the table, return the corresponding value.
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Otherwise, if CH is a space or equals DIACR, return DIACR.
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Otherwise, conclude that DIACR was not combining after all,
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * queue it and return CH.
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
18504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibaultstatic unsigned int
18604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibaulthandle_diacr(struct kbd_data *kbd, unsigned int ch)
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, d;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	d = kbd->diacr;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->diacr = 0;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < kbd->accent_table_size; i++) {
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (kbd->accent_table[i].diacr == d &&
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    kbd->accent_table[i].base == ch)
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return kbd->accent_table[i].result;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ch == ' ' || ch == d)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return d;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd_put_queue(kbd->tty, d);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ch;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Handle dead key.
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_dead(struct kbd_data *kbd, unsigned char value)
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	value = ret_diacr[value];
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Normal character handler.
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_self(struct kbd_data *kbd, unsigned char value)
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kbd->diacr)
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = handle_diacr(kbd, value);
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd_put_queue(kbd->tty, value);
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Special key handlers
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_ignore(struct kbd_data *kbd, unsigned char value)
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Function key handler.
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_fn(struct kbd_data *kbd, unsigned char value)
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kbd->func_table[value])
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_puts_queue(kbd->tty, kbd->func_table[value]);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_spec(struct kbd_data *kbd, unsigned char value)
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (value >= NR_FN_HANDLER)
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kbd->fn_handler[value])
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd->fn_handler[value](kbd);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Put utf8 character to tty flip buffer.
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * UTF-8 is defined for words of up to 31 bits,
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but we need only 16 bits here
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsto_utf8(struct tty_struct *tty, ushort c)
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (c < 0x80)
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*  0******* */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, c);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (c < 0x800) {
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* 110***** 10****** */
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0xc0 | (c >> 6));
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0x80 | (c & 0x3f));
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* 1110**** 10****** 10****** */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0xe0 | (c >> 12));
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f));
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0x80 | (c & 0x3f));
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Process keycode.
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_keycode(struct kbd_data *kbd, unsigned int keycode)
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short keysym;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char type, value;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd || !kbd->tty)
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (keycode >= 384)
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[5][keycode - 384];
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (keycode >= 256)
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[4][keycode - 256];
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (keycode >= 128)
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[1][keycode - 128];
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[0][keycode];
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	type = KTYP(keysym);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (type >= 0xf0) {
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		type -= 0xf0;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (type == KT_LETTER)
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			type = KT_LATIN;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = KVAL(keysym);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MAGIC_SYSRQ	       /* Handle the SysRq Hack */
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (kbd->sysrq) {
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (kbd->sysrq == K(KT_LATIN, '-')) {
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kbd->sysrq = 0;
308f335397d177c906256ee1bba28e8c49e8ec63817Dmitry Torokhov				handle_sysrq(value);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (value == '-') {
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kbd->sysrq = K(KT_LATIN, '-');
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Incomplete sysrq sequence. */
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq));
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->sysrq = 0;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else if ((type == KT_LATIN && value == '^') ||
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   (type == KT_DEAD && ret_diacr[value] == '^')) {
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->sysrq = K(type, value);
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(*k_handler[type])(kbd, value);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		to_utf8(kbd->tty, keysym);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Ioctl stuff.
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdo_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      int cmd, int perm)
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kbentry tmp;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ushort *key_map, val, ov;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if NR_KEYS < 256
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp.kb_index >= NR_KEYS)
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if MAX_NR_KEYMAPS < 256
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp.kb_table >= MAX_NR_KEYMAPS)
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBENT:
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		key_map = kbd->key_maps[tmp.kb_table];
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (key_map) {
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    val = U(key_map[tmp.kb_index]);
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    if (KTYP(val) >= KBD_NR_TYPES)
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			val = K_HOLE;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(val, &user_kbe->kb_value);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBENT:
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!perm)
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) {
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* disallocate map */
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			key_map = kbd->key_maps[tmp.kb_table];
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (key_map) {
367d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens			    kbd->key_maps[tmp.kb_table] = NULL;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    kfree(key_map);
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (KTYP(tmp.kb_value) >= KBD_NR_TYPES)
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)])
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(key_map = kbd->key_maps[tmp.kb_table])) {
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int j;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3815cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day			key_map = kmalloc(sizeof(plain_map),
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						     GFP_KERNEL);
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!key_map)
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -ENOMEM;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->key_maps[tmp.kb_table] = key_map;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (j = 0; j < NR_KEYS; j++)
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				key_map[j] = U(K_HOLE);
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ov = U(key_map[tmp.kb_index]);
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tmp.kb_value == ov)
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;	/* nothing to do */
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Attention Key.
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) &&
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    !capable(CAP_SYS_ADMIN))
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		key_map[tmp.kb_index] = U(tmp.kb_value);
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdo_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       int cmd, int perm)
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char kb_func;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *p;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Get u_kbs->kb_func. */
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (get_user(kb_func, &u_kbs->kb_func))
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if MAX_NR_FUNC < 256
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kb_func >= MAX_NR_FUNC)
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBSENT:
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p = kbd->func_table[kb_func];
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (p) {
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = strlen(p);
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (len >= sizeof(u_kbs->kb_string))
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				len = sizeof(u_kbs->kb_string) - 1;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (copy_to_user(u_kbs->kb_string, p, len))
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -EFAULT;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = 0;
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user('\0', u_kbs->kb_string + len))
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBSENT:
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!perm)
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len = strnlen_user(u_kbs->kb_string,
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   sizeof(u_kbs->kb_string) - 1);
439172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut		if (!len)
440172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut			return -EFAULT;
441172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut		if (len > sizeof(u_kbs->kb_string) - 1)
442172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut			return -EINVAL;
443172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut		p = kmalloc(len + 1, GFP_KERNEL);
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!p)
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOMEM;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_from_user(p, u_kbs->kb_string, len)) {
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(p);
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p[len] = 0;
45117fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->func_table[kb_func]);
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd->func_table[kb_func] = p;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
45865c56e073e4fd10385283561b91189572e33b519Heiko Carstensint kbd_ioctl(struct kbd_data *kbd, unsigned int cmd, unsigned long arg)
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp;
461b652277b09d3d030cb074cc6a98ba80b34244c03Dan Carpenter	unsigned int ct;
462b652277b09d3d030cb074cc6a98ba80b34244c03Dan Carpenter	int perm;
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	argp = (void __user *)arg;
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * To have permissions to do most of the vt ioctls, we either have
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	perm = current->signal->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBTYPE:
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(KB_101, (char __user *)argp);
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBENT:
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBENT:
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return do_kdsk_ioctl(kbd, argp, cmd, perm);
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBSENT:
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBSENT:
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return do_kdgkb_ioctl(kbd, argp, cmd, perm);
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBDIACR:
48104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
48204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrs __user *a = argp;
48304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacr diacr;
48404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		int i;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(kbd->accent_table_size, &a->kb_cnt))
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
48804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		for (i = 0; i < kbd->accent_table_size; i++) {
48904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			diacr.diacr = kbd->accent_table[i].diacr;
49004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			diacr.base = kbd->accent_table[i].base;
49104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			diacr.result = kbd->accent_table[i].result;
49204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
49304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EFAULT;
49404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		}
49504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		return 0;
49604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
49704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	case KDGKBDIACRUC:
49804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
49904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrsuc __user *a = argp;
50004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ct = kbd->accent_table_size;
50204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (put_user(ct, &a->kb_cnt))
50304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EFAULT;
50404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (copy_to_user(a->kbdiacruc, kbd->accent_table,
50504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault				 ct * sizeof(struct kbdiacruc)))
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
50804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBDIACR:
51004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
51104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrs __user *a = argp;
51204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacr diacr;
51304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		int i;
51404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!perm)
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(ct, &a->kb_cnt))
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ct >= MAX_DIACR)
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd->accent_table_size = ct;
52204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		for (i = 0; i < ct; i++) {
52304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
52404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault				return -EFAULT;
52504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			kbd->accent_table[i].diacr = diacr.diacr;
52604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			kbd->accent_table[i].base = diacr.base;
52704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			kbd->accent_table[i].result = diacr.result;
52804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		}
52904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		return 0;
53004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
53104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	case KDSKBDIACRUC:
53204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
53304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrsuc __user *a = argp;
53404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault
53504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (!perm)
53604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EPERM;
53704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (get_user(ct, &a->kb_cnt))
53804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EFAULT;
53904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (ct >= MAX_DIACR)
54004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EINVAL;
54104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		kbd->accent_table_size = ct;
54204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (copy_from_user(kbd->accent_table, a->kbdiacruc,
54304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault				   ct * sizeof(struct kbdiacruc)))
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
54604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOIOCTLCMD;
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_ioctl);
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_ascebc);
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_free);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_alloc);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_keycode);
557