overriding keyboard driver in a module

From: Genadz Batsyan
Date: Wed Jul 06 2005 - 20:48:47 EST


I'm writing a little tool to allow intercepting keyboard events and
substituting them with other events / swallowing events / emitting additional
events on a low level before normal processing by kernel.
http://kbd-mangler.sourceforge.net/

Currently to have precedense over the keyboard driver I'm using a patch
against drivers/input/keyboard/atkbd.c

Well, I guess, patching kernel is unthinkable for most linux users, so I
thought, maybe there is a way to do it without the kernel patch.
Is it possible to write a kernel module to intercept keyboard events
and have precedense over the kernel driver?

If anyone is interested, below is my patch.
I know it's damn dirty (especially the last_dev thingy :-)
but take it easy, it's my first kernel undertaking, a product of a few days,
and most importantly - it works.

Any kind of suggestions would be appreciated a lot.

-----------------------

--- linux-2.6.11.6-orig/drivers/input/keyboard/atkbd.c 2005-03-26
04:28:48.000000000 +0100
+++ linux-2.6.11.6-patched/drivers/input/keyboard/atkbd.c 2005-07-01
22:15:52.000000000 +0200
@@ -34,6 +34,10 @@
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

+
+#define CONFIG_KBD_INTERCEPTOR 1
+
+
static int atkbd_set = 2;
module_param_named(set, atkbd_set, int, 0);
MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2
native)");
@@ -229,18 +233,79 @@
ATKBD_DEFINE_ATTR(softraw);


-static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int
code, int value)
+
+
+
+#ifdef CONFIG_KBD_INTERCEPTOR
+
+typedef int (*KBD_INTERCEPTOR)(int *, int *);
+
+static KBD_INTERCEPTOR kbd_interceptor;
+static struct input_dev *last_dev;
+
+#endif
+
+
+
+
+static void _atkbd_report_key(struct input_dev *dev, int code, int value)
{
- input_regs(dev, regs);
if (value == 3) {
input_report_key(dev, code, 1);
input_sync(dev);
input_report_key(dev, code, 0);
} else
input_event(dev, EV_KEY, code, value);
+}
+
+static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int
code, int value)
+{
+ input_regs(dev, regs);
+
+#ifdef CONFIG_KBD_INTERCEPTOR
+ if (kbd_interceptor && kbd_interceptor(&code, &value))
+ {
+ return;
+ }
+#endif
+ _atkbd_report_key(dev, code, value);
input_sync(dev);
}

+#ifdef CONFIG_KBD_INTERCEPTOR
+KBD_INTERCEPTOR kbd_set_interceptor(KBD_INTERCEPTOR interceptor)
+{
+ KBD_INTERCEPTOR old = kbd_interceptor;
+ kbd_interceptor = interceptor;
+ return old;
+}
+
+void kbd_emit_key(int code, int down)
+{
+ if (last_dev)
+ _atkbd_report_key(last_dev, code, down);
+ else
+ printk("kbd_emit_key: input_dev not set\n");
+}
+void kbd_emit_sync()
+{
+ if (last_dev)
+ input_sync(last_dev);
+ else
+ printk("kbd_emit_key: input_dev not set\n");
+}
+
+
+EXPORT_SYMBOL(kbd_set_interceptor);
+EXPORT_SYMBOL(kbd_emit_key);
+EXPORT_SYMBOL(kbd_emit_sync);
+
+#endif
+
+
+
+
+
/*
* atkbd_interrupt(). Here takes place processing of data received from
* the keyboard into events.
@@ -660,6 +725,10 @@
{
struct atkbd *atkbd = serio->private;

+#ifdef CONFIG_KBD_INTERCEPTOR
+ last_dev = NULL;
+#endif
+
atkbd_disable(atkbd);

/* make sure we don't have a command in flight */
@@ -847,6 +916,10 @@

input_register_device(&atkbd->dev);

+#ifdef CONFIG_KBD_INTERCEPTOR
+ last_dev = &atkbd->dev;
+#endif
+
device_create_file(&serio->dev, &atkbd_attr_extra);
device_create_file(&serio->dev, &atkbd_attr_scroll);
device_create_file(&serio->dev, &atkbd_attr_set);
@@ -1097,6 +1170,9 @@

int __init atkbd_init(void)
{
+#ifdef CONFIG_KBD_INTERCEPTOR
+ last_dev = NULL;
+#endif
serio_register_driver(&atkbd_drv);
return 0;
}



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/