vt:tackle kbd_table

Keyboard struct lifetime is easy, but the locking is not and is completely
ignored by the existing code. Tackle this one head on

- Make the kbd_table private so we can run down all direct users
- Hoick the relevant ioctl handlers into the keyboard layer
- Lock them with the keyboard lock so they don't change mid keypress
- Add helpers for things like console stop/start so we isolate the poking
  around properly
- Tweak the braille console so it still builds

There are a couple of FIXME locking cases left for ioctls that are so hideous
they should be addressed in a later patch. After this patch the kbd_table is
private and all the keyboard jiggery pokery is in one place.

This update fixes speakup and also a memory leak in the original.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 898e359..70d0593 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -68,8 +68,6 @@
 
 #define KBD_DEFLOCK 0
 
-void compute_shiftstate(void);
-
 /*
  * Handler Tables.
  */
@@ -100,35 +98,29 @@
  * Variables exported for vt_ioctl.c
  */
 
-/* maximum values each key_handler can handle */
-const int max_vals[] = {
-	255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
-	NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
-	255, NR_LOCK - 1, 255, NR_BRL - 1
-};
-
-const int NR_TYPES = ARRAY_SIZE(max_vals);
-
-struct kbd_struct kbd_table[MAX_NR_CONSOLES];
-EXPORT_SYMBOL_GPL(kbd_table);
-static struct kbd_struct *kbd = kbd_table;
-
 struct vt_spawn_console vt_spawn_con = {
 	.lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock),
 	.pid  = NULL,
 	.sig  = 0,
 };
 
-/*
- * Variables exported for vt.c
- */
-
-int shift_state = 0;
 
 /*
  * Internal Data.
  */
 
+static struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+static struct kbd_struct *kbd = kbd_table;
+
+/* maximum values each key_handler can handle */
+static const int max_vals[] = {
+	255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
+	NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
+	255, NR_LOCK - 1, 255, NR_BRL - 1
+};
+
+static const int NR_TYPES = ARRAY_SIZE(max_vals);
+
 static struct input_handler kbd_handler;
 static DEFINE_SPINLOCK(kbd_event_lock);
 static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)];	/* keyboard key bitmap */
@@ -138,6 +130,8 @@
 static unsigned int diacr;
 static char rep;					/* flag telling character repeat */
 
+static int shift_state = 0;
+
 static unsigned char ledstate = 0xff;			/* undefined */
 static unsigned char ledioctl;
 
@@ -188,7 +182,7 @@
 	return d->error == 0; /* stop as soon as we successfully get one */
 }
 
-int getkeycode(unsigned int scancode)
+static int getkeycode(unsigned int scancode)
 {
 	struct getset_keycode_data d = {
 		.ke	= {
@@ -215,7 +209,7 @@
 	return d->error == 0; /* stop as soon as we successfully set one */
 }
 
-int setkeycode(unsigned int scancode, unsigned int keycode)
+static int setkeycode(unsigned int scancode, unsigned int keycode)
 {
 	struct getset_keycode_data d = {
 		.ke	= {
@@ -383,9 +377,11 @@
 /*
  * Called after returning from RAW mode or when changing consoles - recompute
  * shift_down[] and shift_state from key_down[] maybe called when keymap is
- * undefined, so that shiftkey release is seen
+ * undefined, so that shiftkey release is seen. The caller must hold the
+ * kbd_event_lock.
  */
-void compute_shiftstate(void)
+
+static void do_compute_shiftstate(void)
 {
 	unsigned int i, j, k, sym, val;
 
@@ -418,6 +414,15 @@
 	}
 }
 
+/* We still have to export this method to vt.c */
+void compute_shiftstate(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	do_compute_shiftstate();
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
 /*
  * We have a combining character DIACR here, followed by the character CH.
  * If the combination occurs in the table, return the corresponding value.
@@ -637,7 +642,7 @@
 
 static void fn_null(struct vc_data *vc)
 {
-	compute_shiftstate();
+	do_compute_shiftstate();
 }
 
 /*
@@ -990,6 +995,8 @@
 
 void setledstate(struct kbd_struct *kbd, unsigned int led)
 {
+        unsigned long flags;
+        spin_lock_irqsave(&kbd_event_lock, flags);
 	if (!(led & ~7)) {
 		ledioctl = led;
 		kbd->ledmode = LED_SHOW_IOCTL;
@@ -997,6 +1004,7 @@
 		kbd->ledmode = LED_SHOW_FLAGS;
 
 	set_leds();
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
 }
 
 static inline unsigned char getleds(void)
@@ -1036,6 +1044,75 @@
 	return 0;
 }
 
+/**
+ *	vt_get_leds	-	helper for braille console
+ *	@console: console to read
+ *	@flag: flag we want to check
+ *
+ *	Check the status of a keyboard led flag and report it back
+ */
+int vt_get_leds(int console, int flag)
+{
+	unsigned long flags;
+	struct kbd_struct * kbd = kbd_table + console;
+	int ret;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	ret = vc_kbd_led(kbd, flag);
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vt_get_leds);
+
+/**
+ *	vt_set_led_state	-	set LED state of a console
+ *	@console: console to set
+ *	@leds: LED bits
+ *
+ *	Set the LEDs on a console. This is a wrapper for the VT layer
+ *	so that we can keep kbd knowledge internal
+ */
+void vt_set_led_state(int console, int leds)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	setledstate(kbd, leds);
+}
+
+/**
+ *	vt_kbd_con_start	-	Keyboard side of console start
+ *	@console: console
+ *
+ *	Handle console start. This is a wrapper for the VT layer
+ *	so that we can keep kbd knowledge internal
+ */
+void vt_kbd_con_start(int console)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	unsigned long flags;
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	clr_vc_kbd_led(kbd, VC_SCROLLOCK);
+	set_leds();
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ *	vt_kbd_con_stop		-	Keyboard side of console stop
+ *	@console: console
+ *
+ *	Handle console stop. This is a wrapper for the VT layer
+ *	so that we can keep kbd knowledge internal
+ */
+void vt_kbd_con_stop(int console)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	unsigned long flags;
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	set_vc_kbd_led(kbd, VC_SCROLLOCK);
+	set_leds();
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
 /*
  * This is the tasklet that updates LED state on all keyboards
  * attached to the box. The reason we use tasklet is that we
@@ -1255,7 +1332,7 @@
 	if (rc == NOTIFY_STOP || !key_map) {
 		atomic_notifier_call_chain(&keyboard_notifier_list,
 					   KBD_UNBOUND_KEYCODE, &param);
-		compute_shiftstate();
+		do_compute_shiftstate();
 		kbd->slockstate = 0;
 		return;
 	}
@@ -1615,3 +1692,495 @@
 	}
 	return ret;
 }
+
+/**
+ *	vt_do_kdskbmode		-	set keyboard mode ioctl
+ *	@console: the console to use
+ *	@arg: the requested mode
+ *
+ *	Update the keyboard mode bits while holding the correct locks.
+ *	Return 0 for success or an error code.
+ */
+int vt_do_kdskbmode(int console, unsigned int arg)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	switch(arg) {
+	case K_RAW:
+		kbd->kbdmode = VC_RAW;
+		break;
+	case K_MEDIUMRAW:
+		kbd->kbdmode = VC_MEDIUMRAW;
+		break;
+	case K_XLATE:
+		kbd->kbdmode = VC_XLATE;
+		do_compute_shiftstate();
+		break;
+	case K_UNICODE:
+		kbd->kbdmode = VC_UNICODE;
+		do_compute_shiftstate();
+		break;
+	case K_OFF:
+		kbd->kbdmode = VC_OFF;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+	return ret;
+}
+
+/**
+ *	vt_do_kdskbmeta		-	set keyboard meta state
+ *	@console: the console to use
+ *	@arg: the requested meta state
+ *
+ *	Update the keyboard meta bits while holding the correct locks.
+ *	Return 0 for success or an error code.
+ */
+int vt_do_kdskbmeta(int console, unsigned int arg)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	switch(arg) {
+	case K_METABIT:
+		clr_vc_kbd_mode(kbd, VC_META);
+		break;
+	case K_ESCPREFIX:
+		set_vc_kbd_mode(kbd, VC_META);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+	return ret;
+}
+
+int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
+								int perm)
+{
+	struct kbkeycode tmp;
+	int kc = 0;
+
+	if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
+		return -EFAULT;
+	switch (cmd) {
+	case KDGETKEYCODE:
+		kc = getkeycode(tmp.scancode);
+		if (kc >= 0)
+			kc = put_user(kc, &user_kbkc->keycode);
+		break;
+	case KDSETKEYCODE:
+		if (!perm)
+			return -EPERM;
+		kc = setkeycode(tmp.scancode, tmp.keycode);
+		break;
+	}
+	return kc;
+}
+
+#define i (tmp.kb_index)
+#define s (tmp.kb_table)
+#define v (tmp.kb_value)
+
+int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
+						int console)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	struct kbentry tmp;
+	ushort *key_map, *new_map, val, ov;
+	unsigned long flags;
+
+	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+		return -EFAULT;
+
+	if (!capable(CAP_SYS_TTY_CONFIG))
+		perm = 0;
+
+	switch (cmd) {
+	case KDGKBENT:
+		/* Ensure another thread doesn't free it under us */
+		spin_lock_irqsave(&kbd_event_lock, flags);
+		key_map = key_maps[s];
+		if (key_map) {
+		    val = U(key_map[i]);
+		    if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+			val = K_HOLE;
+		} else
+		    val = (i ? K_HOLE : K_NOSUCHMAP);
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+		return put_user(val, &user_kbe->kb_value);
+	case KDSKBENT:
+		if (!perm)
+			return -EPERM;
+		if (!i && v == K_NOSUCHMAP) {
+			spin_lock_irqsave(&kbd_event_lock, flags);
+			/* deallocate map */
+			key_map = key_maps[s];
+			if (s && key_map) {
+			    key_maps[s] = NULL;
+			    if (key_map[0] == U(K_ALLOCATED)) {
+					kfree(key_map);
+					keymap_count--;
+			    }
+			}
+			spin_unlock_irqrestore(&kbd_event_lock, flags);
+			break;
+		}
+
+		if (KTYP(v) < NR_TYPES) {
+		    if (KVAL(v) > max_vals[KTYP(v)])
+				return -EINVAL;
+		} else
+		    if (kbd->kbdmode != VC_UNICODE)
+				return -EINVAL;
+
+		/* ++Geert: non-PC keyboards may generate keycode zero */
+#if !defined(__mc68000__) && !defined(__powerpc__)
+		/* assignment to entry 0 only tests validity of args */
+		if (!i)
+			break;
+#endif
+
+		new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
+		if (!new_map)
+			return -ENOMEM;
+		spin_lock_irqsave(&kbd_event_lock, flags);
+		key_map = key_maps[s];
+		if (key_map == NULL) {
+			int j;
+
+			if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
+			    !capable(CAP_SYS_RESOURCE)) {
+				spin_unlock_irqrestore(&kbd_event_lock, flags);
+				kfree(new_map);
+				return -EPERM;
+			}
+			key_maps[s] = new_map;
+			key_map[0] = U(K_ALLOCATED);
+			for (j = 1; j < NR_KEYS; j++)
+				key_map[j] = U(K_HOLE);
+			keymap_count++;
+		} else
+			kfree(new_map);
+
+		ov = U(key_map[i]);
+		if (v == ov)
+			goto out;
+		/*
+		 * Attention Key.
+		 */
+		if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) {
+			spin_unlock_irqrestore(&kbd_event_lock, flags);
+			return -EPERM;
+		}
+		key_map[i] = U(v);
+		if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+			do_compute_shiftstate();
+out:
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+		break;
+	}
+	return 0;
+}
+#undef i
+#undef s
+#undef v
+
+/* FIXME: This one needs untangling and locking */
+int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
+{
+	struct kbsentry *kbs;
+	char *p;
+	u_char *q;
+	u_char __user *up;
+	int sz;
+	int delta;
+	char *first_free, *fj, *fnw;
+	int i, j, k;
+	int ret;
+
+	if (!capable(CAP_SYS_TTY_CONFIG))
+		perm = 0;
+
+	kbs = kmalloc(sizeof(*kbs), GFP_KERNEL);
+	if (!kbs) {
+		ret = -ENOMEM;
+		goto reterr;
+	}
+
+	/* we mostly copy too much here (512bytes), but who cares ;) */
+	if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) {
+		ret = -EFAULT;
+		goto reterr;
+	}
+	kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0';
+	i = kbs->kb_func;
+
+	switch (cmd) {
+	case KDGKBSENT:
+		sz = sizeof(kbs->kb_string) - 1; /* sz should have been
+						  a struct member */
+		up = user_kdgkb->kb_string;
+		p = func_table[i];
+		if(p)
+			for ( ; *p && sz; p++, sz--)
+				if (put_user(*p, up++)) {
+					ret = -EFAULT;
+					goto reterr;
+				}
+		if (put_user('\0', up)) {
+			ret = -EFAULT;
+			goto reterr;
+		}
+		kfree(kbs);
+		return ((p && *p) ? -EOVERFLOW : 0);
+	case KDSKBSENT:
+		if (!perm) {
+			ret = -EPERM;
+			goto reterr;
+		}
+
+		q = func_table[i];
+		first_free = funcbufptr + (funcbufsize - funcbufleft);
+		for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
+			;
+		if (j < MAX_NR_FUNC)
+			fj = func_table[j];
+		else
+			fj = first_free;
+
+		delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
+		if (delta <= funcbufleft) { 	/* it fits in current buf */
+		    if (j < MAX_NR_FUNC) {
+			memmove(fj + delta, fj, first_free - fj);
+			for (k = j; k < MAX_NR_FUNC; k++)
+			    if (func_table[k])
+				func_table[k] += delta;
+		    }
+		    if (!q)
+		      func_table[i] = fj;
+		    funcbufleft -= delta;
+		} else {			/* allocate a larger buffer */
+		    sz = 256;
+		    while (sz < funcbufsize - funcbufleft + delta)
+		      sz <<= 1;
+		    fnw = kmalloc(sz, GFP_KERNEL);
+		    if(!fnw) {
+		      ret = -ENOMEM;
+		      goto reterr;
+		    }
+
+		    if (!q)
+		      func_table[i] = fj;
+		    if (fj > funcbufptr)
+			memmove(fnw, funcbufptr, fj - funcbufptr);
+		    for (k = 0; k < j; k++)
+		      if (func_table[k])
+			func_table[k] = fnw + (func_table[k] - funcbufptr);
+
+		    if (first_free > fj) {
+			memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
+			for (k = j; k < MAX_NR_FUNC; k++)
+			  if (func_table[k])
+			    func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
+		    }
+		    if (funcbufptr != func_buf)
+		      kfree(funcbufptr);
+		    funcbufptr = fnw;
+		    funcbufleft = funcbufleft - delta + sz - funcbufsize;
+		    funcbufsize = sz;
+		}
+		strcpy(func_table[i], kbs->kb_string);
+		break;
+	}
+	ret = 0;
+reterr:
+	kfree(kbs);
+	return ret;
+}
+
+int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+        unsigned long flags;
+	unsigned char ucval;
+
+        switch(cmd) {
+	/* the ioctls below read/set the flags usually shown in the leds */
+	/* don't use them - they will go away without warning */
+	case KDGKBLED:
+                spin_lock_irqsave(&kbd_event_lock, flags);
+		ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
+                spin_unlock_irqrestore(&kbd_event_lock, flags);
+		return put_user(ucval, (char __user *)arg);
+
+	case KDSKBLED:
+		if (!perm)
+			return -EPERM;
+		if (arg & ~0x77)
+			return -EINVAL;
+                spin_lock_irqsave(&kbd_event_lock, flags);
+		kbd->ledflagstate = (arg & 7);
+		kbd->default_ledflagstate = ((arg >> 4) & 7);
+		set_leds();
+                spin_unlock_irqrestore(&kbd_event_lock, flags);
+		break;
+
+	/* the ioctls below only set the lights, not the functions */
+	/* for those, see KDGKBLED and KDSKBLED above */
+	case KDGETLED:
+		ucval = getledstate();
+		return put_user(ucval, (char __user *)arg);
+
+	case KDSETLED:
+		if (!perm)
+			return -EPERM;
+		setledstate(kbd, arg);
+		return 0;
+        }
+        return -ENOIOCTLCMD;
+}
+
+int vt_do_kdgkbmode(int console)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	/* This is a spot read so needs no locking */
+	switch (kbd->kbdmode) {
+	case VC_RAW:
+		return K_RAW;
+	case VC_MEDIUMRAW:
+		return K_MEDIUMRAW;
+	case VC_UNICODE:
+		return K_UNICODE;
+	case VC_OFF:
+		return K_OFF;
+	default:
+		return K_XLATE;
+	}
+}
+
+/**
+ *	vt_do_kdgkbmeta		-	report meta status
+ *	@console: console to report
+ *
+ *	Report the meta flag status of this console
+ */
+int vt_do_kdgkbmeta(int console)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+        /* Again a spot read so no locking */
+	return vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT;
+}
+
+/**
+ *	vt_reset_unicode	-	reset the unicode status
+ *	@console: console being reset
+ *
+ *	Restore the unicode console state to its default
+ */
+void vt_reset_unicode(int console)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	kbd_table[console].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ *	vt_get_shiftstate	-	shift bit state
+ *
+ *	Report the shift bits from the keyboard state. We have to export
+ *	this to support some oddities in the vt layer.
+ */
+int vt_get_shift_state(void)
+{
+        /* Don't lock as this is a transient report */
+        return shift_state;
+}
+
+/**
+ *	vt_reset_keyboard	-	reset keyboard state
+ *	@console: console to reset
+ *
+ *	Reset the keyboard bits for a console as part of a general console
+ *	reset event
+ */
+void vt_reset_keyboard(int console)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	set_vc_kbd_mode(kbd, VC_REPEAT);
+	clr_vc_kbd_mode(kbd, VC_CKMODE);
+	clr_vc_kbd_mode(kbd, VC_APPLIC);
+	clr_vc_kbd_mode(kbd, VC_CRLF);
+	kbd->lockstate = 0;
+	kbd->slockstate = 0;
+	kbd->ledmode = LED_SHOW_FLAGS;
+	kbd->ledflagstate = kbd->default_ledflagstate;
+	/* do not do set_leds here because this causes an endless tasklet loop
+	   when the keyboard hasn't been initialized yet */
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ *	vt_get_kbd_mode_bit	-	read keyboard status bits
+ *	@console: console to read from
+ *	@bit: mode bit to read
+ *
+ *	Report back a vt mode bit. We do this without locking so the
+ *	caller must be sure that there are no synchronization needs
+ */
+
+int vt_get_kbd_mode_bit(int console, int bit)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	return vc_kbd_mode(kbd, bit);
+}
+
+/**
+ *	vt_set_kbd_mode_bit	-	read keyboard status bits
+ *	@console: console to read from
+ *	@bit: mode bit to read
+ *
+ *	Set a vt mode bit. We do this without locking so the
+ *	caller must be sure that there are no synchronization needs
+ */
+
+void vt_set_kbd_mode_bit(int console, int bit)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	set_vc_kbd_mode(kbd, bit);
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ *	vt_clr_kbd_mode_bit	-	read keyboard status bits
+ *	@console: console to read from
+ *	@bit: mode bit to read
+ *
+ *	Report back a vt mode bit. We do this without locking so the
+ *	caller must be sure that there are no synchronization needs
+ */
+
+void vt_clr_kbd_mode_bit(int console, int bit)
+{
+	struct kbd_struct * kbd = kbd_table + console;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	clr_vc_kbd_mode(kbd, bit);
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+}