diff mbox

[23/28] keyboard, input: Add hook to input to allow low level event clear

Message ID 4B8BF01D.7070507@windriver.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jason Wessel March 1, 2010, 4:49 p.m. UTC
None
diff mbox

Patch

--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -379,6 +379,32 @@  static void to_utf8(struct vc_data *vc, 
 	}
 }
 
+static int kbd_dbg_clear_keys_helper(struct input_handle *handle, void *data)
+{
+	unsigned int *keycode = data;
+	input_inject_event(handle, EV_KEY, *keycode, 0);
+	return 0;
+}
+
+/* Called to clear the any key presses after resuming the kernel. */
+void kbd_dbg_clear_keys(void) {
+	unsigned int i, j, k;
+
+	for (i = 0; i < ARRAY_SIZE(key_down); i++) {
+		if (!key_down[i])
+			continue;
+
+		k = i * BITS_PER_LONG;
+
+		for (j = 0; j < BITS_PER_LONG; j++, k++) {
+			if (!test_bit(k, key_down))
+				continue;
+			input_handler_for_each_handle(&kbd_handler, &k,
+				      kbd_dbg_clear_keys_helper);
+		}
+	}
+}
+
 /*
  * Called after returning from RAW mode or when changing consoles - recompute
  * shift_down[] and shift_state from key_down[] maybe called when keymap is
@@ -1206,6 +1232,7 @@  static void kbd_keycode(unsigned int key
 	if (sysrq_down && !down && keycode == sysrq_alt_use)
 		sysrq_down = 0;
 	if (sysrq_down && down && !rep) {
+		set_bit(keycode, key_down);
 		handle_sysrq(kbd_sysrq_xlate[keycode], tty);
 		return;
 	}
--- a/drivers/serial/kgdboc.c
+++ b/drivers/serial/kgdboc.c
@@ -17,6 +17,7 @@ 
 #include <linux/kdb.h>
 #include <linux/tty.h>
 #include <linux/console.h>
+#include <linux/kbd_kern.h>
 
 #define MAX_CONFIG_LEN		40
 
@@ -35,12 +36,16 @@  static struct tty_driver	*kgdb_tty_drive
 static int			kgdb_tty_line;
 
 #ifdef CONFIG_KDB_KEYBOARD
+static bool			kgdboc_use_kbd;
+
 static int kgdboc_register_kbd(char **cptr)
 {
+	kgdboc_use_kbd = false;
 	if (strncmp(*cptr, "kbd", 3) == 0) {
 		if (kdb_poll_idx < KDB_POLL_FUNC_MAX) {
 			kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char;
 			kdb_poll_idx++;
+			kgdboc_use_kbd = true;
 			if (cptr[0][3] == ',')
 				*cptr += 4;
 			else
@@ -63,9 +68,16 @@  static void kgdboc_unregister_kbd(void)
 		}
 	}
 }
+
+static inline void kgdboc_clear_kbd(void)
+{
+	if (kgdboc_use_kbd)
+		kdb_clear_keys(); /* Release all pressed keys */
+}
 #else /* ! CONFIG_KDB_KEYBOARD */
 #define kgdboc_register_kbd(x) 0
 #define kgdboc_unregister_kbd()
+#define kgdboc_clear_kbd()
 #endif /* ! CONFIG_KDB_KEYBOARD */
 
 static int kgdboc_option_setup(char *opt)
@@ -213,6 +225,7 @@  static void kgdboc_post_exp_handler(void
 	/* decrement the module count when the debugger detaches */
 	if (!kgdb_connected)
 		module_put(THIS_MODULE);
+	kgdboc_clear_kbd();
 }
 
 static struct kgdb_io kgdboc_io_ops = {
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -144,6 +144,7 @@  struct console;
 int getkeycode(unsigned int scancode);
 int setkeycode(unsigned int scancode, unsigned int keycode);
 void compute_shiftstate(void);
+void kbd_dbg_clear_keys(void);
 
 /* defkeymap.c */
 
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -93,6 +93,9 @@  typedef int (*get_char_func)(void);
 extern get_char_func kdb_poll_funcs[];
 extern int kdb_get_kbd_char(void);
 
+/* KDB keyboard hooks */
+extern void kdb_clear_keys(void);
+
 static inline
 int kdb_process_cpu(const struct task_struct *p)
 {
--- a/kernel/debug/kdb/kdb_keyboard.c
+++ b/kernel/debug/kdb/kdb_keyboard.c
@@ -13,6 +13,7 @@ 
 #include <linux/ctype.h>
 #include <linux/module.h>
 #include <linux/io.h>
+#include <linux/kbd_kern.h>
 
 /* Keyboard Controller Registers on normal PCs. */
 
@@ -210,3 +211,27 @@  int kdb_get_kbd_char(void)
 	return keychar & 0xff;
 }
 EXPORT_SYMBOL_GPL(kdb_get_kbd_char);
+
+static bool dbg_keys_cleared;
+/*
+ * input_dbg_clear_keys - Clear any keyboards if they have a call back,
+ * after returning from the kernel debugger
+ */
+static void kdb_keys_task(unsigned long not_used)
+{
+	if (!dbg_keys_cleared)
+		return;
+	kbd_dbg_clear_keys();
+	dbg_keys_cleared = false;
+}
+
+static DECLARE_TASKLET(kdb_keys_tasklet, kdb_keys_task, 0);
+
+void kdb_clear_keys(void)
+{
+	if (dbg_keys_cleared)
+		return;
+	dbg_keys_cleared = true;
+	tasklet_schedule(&kdb_keys_tasklet);
+}
+EXPORT_SYMBOL_GPL(kdb_clear_keys);