keyboard.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
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;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, len;
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]) {
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->key_maps[i] =
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kmalloc(sizeof(u_short)*NR_KEYS, GFP_KERNEL);
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!kbd->key_maps[i])
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out_maps;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(kbd->key_maps[i], key_maps[i],
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       sizeof(u_short)*NR_KEYS);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7088abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn	kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL);
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd->func_table)
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_maps;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(func_table); i++) {
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (func_table[i]) {
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = strlen(func_table[i]) + 1;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->func_table[i] = kmalloc(len, GFP_KERNEL);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!kbd->func_table[i])
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out_func;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(kbd->func_table[i], func_table[i], len);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->fn_handler =
8388abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn		kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL);
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd->fn_handler)
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_func;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->accent_table =
8704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		kmalloc(sizeof(struct kbdiacruc)*MAX_DIACR, GFP_KERNEL);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd->accent_table)
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_fn_handler;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(kbd->accent_table, accent_table,
9104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	       sizeof(struct kbdiacruc)*MAX_DIACR);
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->accent_table_size = accent_table_size;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return kbd;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_fn_handler:
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->fn_handler);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_func:
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(func_table); i++)
9917fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->func_table[i]);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->func_table);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_maps:
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++)
10317fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->key_maps[i]);
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->key_maps);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_kbd:
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd);
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
108d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens	return NULL;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_free(struct kbd_data *kbd)
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->accent_table);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->fn_handler);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(func_table); i++)
11917fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->func_table[i]);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->func_table);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++)
12217fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->key_maps[i]);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd->key_maps);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(kbd);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Generate ascii -> ebcdic translation table from kbd_data.
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short *keymap, keysym;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j, k;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(ascebc, 0x40, 256);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keymap = kbd->key_maps[i];
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!keymap)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0; j < NR_KEYS; j++) {
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			k = ((i & 1) << 7) + j;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			keysym = keymap[j];
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    KTYP(keysym) == (KT_LETTER | 0xf0))
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ascebc[KVAL(keysym)] = k;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else if (KTYP(keysym) == (KT_DEAD | 0xf0))
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ascebc[ret_diacr[KVAL(keysym)]] = k;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1532b67fc46061b2171fb8fbb55d1ac717abd533569Heiko Carstens#if 0
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Generate ebcdic -> ascii translation table from kbd_data.
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short *keymap, keysym;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j, k;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(ebcasc, ' ', 256);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keymap = kbd->key_maps[i];
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!keymap)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0; j < NR_KEYS; j++) {
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			keysym = keymap[j];
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			k = ((i & 1) << 7) + j;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (KTYP(keysym) == (KT_LATIN | 0xf0) ||
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    KTYP(keysym) == (KT_LETTER | 0xf0))
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ebcasc[k] = KVAL(keysym);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else if (KTYP(keysym) == (KT_DEAD | 0xf0))
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ebcasc[k] = ret_diacr[KVAL(keysym)];
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1792b67fc46061b2171fb8fbb55d1ac717abd533569Heiko Carstens#endif
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We have a combining character DIACR here, followed by the character CH.
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If the combination occurs in the table, return the corresponding value.
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Otherwise, if CH is a space or equals DIACR, return DIACR.
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Otherwise, conclude that DIACR was not combining after all,
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * queue it and return CH.
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
18804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibaultstatic unsigned int
18904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibaulthandle_diacr(struct kbd_data *kbd, unsigned int ch)
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, d;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	d = kbd->diacr;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->diacr = 0;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < kbd->accent_table_size; i++) {
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (kbd->accent_table[i].diacr == d &&
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    kbd->accent_table[i].base == ch)
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return kbd->accent_table[i].result;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ch == ' ' || ch == d)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return d;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd_put_queue(kbd->tty, d);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ch;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Handle dead key.
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_dead(struct kbd_data *kbd, unsigned char value)
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	value = ret_diacr[value];
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Normal character handler.
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_self(struct kbd_data *kbd, unsigned char value)
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kbd->diacr)
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = handle_diacr(kbd, value);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kbd_put_queue(kbd->tty, value);
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Special key handlers
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_ignore(struct kbd_data *kbd, unsigned char value)
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Function key handler.
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_fn(struct kbd_data *kbd, unsigned char value)
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kbd->func_table[value])
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_puts_queue(kbd->tty, kbd->func_table[value]);
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsk_spec(struct kbd_data *kbd, unsigned char value)
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (value >= NR_FN_HANDLER)
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kbd->fn_handler[value])
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd->fn_handler[value](kbd);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Put utf8 character to tty flip buffer.
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * UTF-8 is defined for words of up to 31 bits,
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but we need only 16 bits here
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsto_utf8(struct tty_struct *tty, ushort c)
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (c < 0x80)
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*  0******* */
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, c);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (c < 0x800) {
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* 110***** 10****** */
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0xc0 | (c >> 6));
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0x80 | (c & 0x3f));
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* 1110**** 10****** 10****** */
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0xe0 | (c >> 12));
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f));
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd_put_queue(tty, 0x80 | (c & 0x3f));
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Process keycode.
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_keycode(struct kbd_data *kbd, unsigned int keycode)
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short keysym;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char type, value;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!kbd || !kbd->tty)
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (keycode >= 384)
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[5][keycode - 384];
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (keycode >= 256)
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[4][keycode - 256];
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (keycode >= 128)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[1][keycode - 128];
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		keysym = kbd->key_maps[0][keycode];
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	type = KTYP(keysym);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (type >= 0xf0) {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		type -= 0xf0;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (type == KT_LETTER)
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			type = KT_LATIN;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = KVAL(keysym);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MAGIC_SYSRQ	       /* Handle the SysRq Hack */
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (kbd->sysrq) {
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (kbd->sysrq == K(KT_LATIN, '-')) {
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kbd->sysrq = 0;
3115a489b9846f688db7e69aa7ccb23c53459a9c20eHeiko Carstens				handle_sysrq(value, kbd->tty);
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (value == '-') {
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kbd->sysrq = K(KT_LATIN, '-');
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Incomplete sysrq sequence. */
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq));
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->sysrq = 0;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else if ((type == KT_LATIN && value == '^') ||
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   (type == KT_DEAD && ret_diacr[value] == '^')) {
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->sysrq = K(type, value);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(*k_handler[type])(kbd, value);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		to_utf8(kbd->tty, keysym);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Ioctl stuff.
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdo_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      int cmd, int perm)
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kbentry tmp;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ushort *key_map, val, ov;
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if NR_KEYS < 256
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp.kb_index >= NR_KEYS)
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if MAX_NR_KEYMAPS < 256
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp.kb_table >= MAX_NR_KEYMAPS)
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBENT:
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		key_map = kbd->key_maps[tmp.kb_table];
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (key_map) {
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    val = U(key_map[tmp.kb_index]);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    if (KTYP(val) >= KBD_NR_TYPES)
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			val = K_HOLE;
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    val = (tmp.kb_index ? K_HOLE : K_NOSUCHMAP);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(val, &user_kbe->kb_value);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBENT:
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!perm)
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!tmp.kb_index && tmp.kb_value == K_NOSUCHMAP) {
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* disallocate map */
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			key_map = kbd->key_maps[tmp.kb_table];
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (key_map) {
370d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens			    kbd->key_maps[tmp.kb_table] = NULL;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    kfree(key_map);
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (KTYP(tmp.kb_value) >= KBD_NR_TYPES)
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)])
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(key_map = kbd->key_maps[tmp.kb_table])) {
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int j;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3845cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day			key_map = kmalloc(sizeof(plain_map),
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						     GFP_KERNEL);
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!key_map)
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -ENOMEM;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kbd->key_maps[tmp.kb_table] = key_map;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (j = 0; j < NR_KEYS; j++)
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				key_map[j] = U(K_HOLE);
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ov = U(key_map[tmp.kb_index]);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tmp.kb_value == ov)
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;	/* nothing to do */
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Attention Key.
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) &&
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    !capable(CAP_SYS_ADMIN))
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		key_map[tmp.kb_index] = U(tmp.kb_value);
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdo_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       int cmd, int perm)
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char kb_func;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *p;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Get u_kbs->kb_func. */
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (get_user(kb_func, &u_kbs->kb_func))
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if MAX_NR_FUNC < 256
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (kb_func >= MAX_NR_FUNC)
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBSENT:
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p = kbd->func_table[kb_func];
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (p) {
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = strlen(p);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (len >= sizeof(u_kbs->kb_string))
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				len = sizeof(u_kbs->kb_string) - 1;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (copy_to_user(u_kbs->kb_string, p, len))
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -EFAULT;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = 0;
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user('\0', u_kbs->kb_string + len))
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBSENT:
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!perm)
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len = strnlen_user(u_kbs->kb_string,
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   sizeof(u_kbs->kb_string) - 1);
442172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut		if (!len)
443172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut			return -EFAULT;
444172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut		if (len > sizeof(u_kbs->kb_string) - 1)
445172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut			return -EINVAL;
446172411f10c25bbd81b19f67566af6a7f549f46a9Davi Arnaut		p = kmalloc(len + 1, GFP_KERNEL);
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!p)
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOMEM;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_from_user(p, u_kbs->kb_string, len)) {
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(p);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p[len] = 0;
45417fd682e544556a2a829e94383239c029bb21c5eJesper Juhl		kfree(kbd->func_table[kb_func]);
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd->func_table[kb_func] = p;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldskbd_ioctl(struct kbd_data *kbd, struct file *file,
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  unsigned int cmd, unsigned long arg)
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp;
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ct, perm;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	argp = (void __user *)arg;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * To have permissions to do most of the vt ioctls, we either have
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	perm = current->signal->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBTYPE:
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(KB_101, (char __user *)argp);
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBENT:
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBENT:
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return do_kdsk_ioctl(kbd, argp, cmd, perm);
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBSENT:
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBSENT:
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return do_kdgkb_ioctl(kbd, argp, cmd, perm);
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDGKBDIACR:
48504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
48604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrs __user *a = argp;
48704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacr diacr;
48804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		int i;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(kbd->accent_table_size, &a->kb_cnt))
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
49204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		for (i = 0; i < kbd->accent_table_size; i++) {
49304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			diacr.diacr = kbd->accent_table[i].diacr;
49404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			diacr.base = kbd->accent_table[i].base;
49504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			diacr.result = kbd->accent_table[i].result;
49604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr)))
49704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EFAULT;
49804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		}
49904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		return 0;
50004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
50104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	case KDGKBDIACRUC:
50204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
50304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrsuc __user *a = argp;
50404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ct = kbd->accent_table_size;
50604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (put_user(ct, &a->kb_cnt))
50704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EFAULT;
50804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (copy_to_user(a->kbdiacruc, kbd->accent_table,
50904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault				 ct * sizeof(struct kbdiacruc)))
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
51204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case KDSKBDIACR:
51404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
51504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrs __user *a = argp;
51604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacr diacr;
51704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		int i;
51804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!perm)
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(ct, &a->kb_cnt))
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ct >= MAX_DIACR)
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kbd->accent_table_size = ct;
52604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		for (i = 0; i < ct; i++) {
52704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr)))
52804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault				return -EFAULT;
52904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			kbd->accent_table[i].diacr = diacr.diacr;
53004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			kbd->accent_table[i].base = diacr.base;
53104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			kbd->accent_table[i].result = diacr.result;
53204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		}
53304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		return 0;
53404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
53504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	case KDSKBDIACRUC:
53604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	{
53704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		struct kbdiacrsuc __user *a = argp;
53804c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault
53904c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (!perm)
54004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EPERM;
54104c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (get_user(ct, &a->kb_cnt))
54204c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EFAULT;
54304c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (ct >= MAX_DIACR)
54404c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault			return -EINVAL;
54504c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		kbd->accent_table_size = ct;
54604c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault		if (copy_from_user(kbd->accent_table, a->kbdiacruc,
54704c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault				   ct * sizeof(struct kbdiacruc)))
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
55004c71976500352d02f60616d2b960267d8c5fe24Samuel Thibault	}
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOIOCTLCMD;
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_ioctl);
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_ascebc);
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_free);
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_alloc);
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(kbd_keycode);
561