#include <linux/sysrq.h>
 #include <linux/input.h>
 #include <linux/reboot.h>
+#include <linux/notifier.h>
 
 extern void ctrl_alt_del(void);
 
 typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
                            char up_flag);
 static k_handler_fn K_HANDLERS;
-static k_handler_fn *k_handler[16] = { K_HANDLERS };
+k_handler_fn *k_handler[16] = { K_HANDLERS };
+EXPORT_SYMBOL_GPL(k_handler);
 
 #define FN_HANDLERS\
        fn_null,        fn_enter,       fn_show_ptregs, fn_show_mem,\
 #endif
 static int sysrq_alt;
 
+/*
+ * Notifier list for console keyboard events
+ */
+static ATOMIC_NOTIFIER_HEAD(keyboard_notifier_list);
+
+int register_keyboard_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&keyboard_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_keyboard_notifier);
+
+int unregister_keyboard_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&keyboard_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
+
 /*
  * Translation of scancodes to keycodes. We set them on only the first
  * keyboard in the list that accepts the scancode and keycode.
        unsigned char type, raw_mode;
        struct tty_struct *tty;
        int shift_final;
+       struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
 
        tty = vc->vc_tty;
 
                return;
        }
 
-       shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
+       param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
        key_map = key_maps[shift_final];
 
-       if (!key_map) {
+       if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, ¶m) == NOTIFY_STOP || !key_map) {
+               atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNBOUND_KEYCODE, ¶m);
                compute_shiftstate();
                kbd->slockstate = 0;
                return;
        type = KTYP(keysym);
 
        if (type < 0xf0) {
+               param.value = keysym;
+               if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNICODE, ¶m) == NOTIFY_STOP)
+                       return;
                if (down && !raw_mode)
                        to_utf8(vc, keysym);
                return;
 
        type -= 0xf0;
 
-       if (raw_mode && type != KT_SPEC && type != KT_SHIFT)
-               return;
-
        if (type == KT_LETTER) {
                type = KT_LATIN;
                if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
                                keysym = key_map[keycode];
                }
        }
+       param.value = keysym;
+
+       if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYSYM, ¶m) == NOTIFY_STOP)
+               return;
+
+       if (raw_mode && type != KT_SPEC && type != KT_SHIFT)
+               return;
 
        (*k_handler[type])(vc, keysym & 0xff, !down);
 
+       atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, ¶m);
+
        if (type != KT_SLOCK)
                kbd->slockstate = 0;
 }
 
 #define MAX_NR_OF_USER_KEYMAPS 256     /* should be at least 7 */
 
 #ifdef __KERNEL__
+struct notifier_block;
 extern const int NR_TYPES;
 extern const int max_vals[];
 extern unsigned short *key_maps[MAX_NR_KEYMAPS];
 extern unsigned short plain_map[NR_KEYS];
+
+struct keyboard_notifier_param {
+       struct vc_data *vc;     /* VC on which the keyboard press was done */
+       int down;               /* Pressure of the key? */
+       int shift;              /* Current shift mask */
+       unsigned int value;     /* keycode, unicode value or keysym */
+};
+
+extern int register_keyboard_notifier(struct notifier_block *nb);
+extern int unregister_keyboard_notifier(struct notifier_block *nb);
 #endif
 
 #define MAX_NR_FUNC    256     /* max nr of strings assigned to keys */
 
 #define PM_SUSPEND_PREPARE     0x0003 /* Going to suspend the system */
 #define PM_POST_SUSPEND                0x0004 /* Suspend finished */
 
+/* Console keyboard events.
+ * Note: KBD_KEYCODE is always sent before KBD_UNBOUND_KEYCODE, KBD_UNICODE and
+ * KBD_KEYSYM. */
+#define KBD_KEYCODE            0x0001 /* Keyboard keycode, called before any other */
+#define KBD_UNBOUND_KEYCODE    0x0002 /* Keyboard keycode which is not bound to any other */
+#define KBD_UNICODE            0x0003 /* Keyboard unicode */
+#define KBD_KEYSYM             0x0004 /* Keyboard keysym */
+#define KBD_POST_KEYSYM                0x0005 /* Called after keyboard keysym interpretation */
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_NOTIFIER_H */