My Tutorials/Writing Linux kernel modules
From ThorstensHome
Contents |
Keyboard wire watcher
This kernel module watches, after it is loaded, for some seconds what comes from the keyboard and displays this.
keyb.c
/* A kernel module. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/tty.h> #include <asm/io.h> int init_module(void) { printk("This is a kernel module\n"); int retries = 0x100100; int input; while (--retries != 0) { int oldinput=input; input=inb(0x60); if (oldinput!=input) { printk("got %i",input); // print directly to konsole // kernel 2.6.9+ required char *str="hello"; sprintf(str,"%i",input); struct tty_struct *my_tty; my_tty = current->signal->tty; if (my_tty != NULL) { ((my_tty->driver)->write) (my_tty,str,strlen(str)); ((my_tty->driver)->write) (my_tty,"\015\012",2); } } } } void cleanup_module(void) { printk(KERN_ALERT "Au revoir\n"); }
Makefile
obj-m += keyb.o
How to compile Do not change directory and issue:
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
How to use
insmod keyb
Get rid of it
rmmod keyb
Understanding modinfo
tweedleburg:~/mod # modinfo keyb.ko filename: keyb.ko srcversion: 284786FFEC172AAD1E91C48 depends: vermagic: 2.6.25.9-0.2-default SMP mod_unload tweedleburg:~/mod # strings keyb.ko ATSH Z[A\A] <1>Au revoir This is a kernel module got %i hello srcversion=284786FFEC172AAD1E91C48 depends= vermagic=2.6.25.9-0.2-default SMP mod_unload keyb tweedleburg:~/mod #
functions
Inside a kernel module, you can call all functions from /proc/kallsyms, including printk and panic("test").
Interrupts
You will only be able to free irqs that are to be found in /proc/interrupts.
Keyboard driver
Here's the first keyboard driver I wrote.
#include <linux/module.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/workqueue.h> #include <asm/io.h> #define NAME "inter.c" static struct workqueue_struct *mqueue; static void char2(void *scancode) { int iscancode = (int)*((char *) scancode ); printk(KERN_INFO "%i",iscancode); if ( ((int)*((char *)scancode)) == 38 ) panic("pan38"); } irqreturn_t irqhandler(int irq, void *dev_id, struct pt_regs *regs) { static int initialised = 0; static unsigned char scancode; static struct work_struct task; unsigned char state; state = inb(0x64); scancode = inb(0x60); if (initialised == 0) { INIT_WORK(&task, char2, &scancode); initialised = 1; } else { PREPARE_WORK(&task, char2, &scancode); } queue_work(mqueue, &task); return IRQ_HANDLED; } int init_module() { mqueue = create_workqueue(NAME); free_irq(1, NULL); return request_irq(1,irqhandler, SA_SHIRQ, "kbd", (void *)(irqhandler)); } void cleanup_module() { free_irq(1, NULL); } MODULE_LICENSE("GPL");
TimeSetter
#include <linux/module.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/workqueue.h> #include <asm/io.h> #include <linux/reboot.h> #include <linux/syscalls.h> #include <linux/time.h> #define NAME "inter.c" static struct workqueue_struct *mqueue; irqreturn_t irqhandler(int irq, void *dev_id, struct pt_regs *regs) { printk("h"); return IRQ_HANDLED; } int init_module() { struct timespec* t; do_settimeofday(t); } void cleanup_module() { } MODULE_LICENSE("GPL");
Uptime counter
#include <linux/module.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/workqueue.h> #include <asm/io.h> #include <linux/reboot.h> #include <linux/syscalls.h> #include <linux/time.h> #include <linux/sched.h> #define NAME "inter.c" int init_module() { printk("Your kernel HZ values is %i\n", HZ); printk("Since start or overflow, %i ticks have been gone", jiffies); } void cleanup_module() { } MODULE_LICENSE("GPL");
Key simulator
/* * Simulate a keypress */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/module.h> #include <linux/tty.h> #include <linux/version.h> #include <linux/keyboard.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/tty_flip.h> #include <linux/tty.h> #include <linux/string.h> #include <linux/tty_flip.h> #include <linux/mm.h> #include <linux/irq.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/vt_kern.h> #include <linux/sysrq.h> #include <linux/kbd_kern.h> #include <linux/kbd_diacr.h> #include <linux/input.h> #include <linux/reboot.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Thorsten Staerk"); struct tty_struct *tty; int ch; static void println(char *string) { tty = current->signal->tty; (tty->driver)->write (tty, string, strlen(string)); ((tty->driver)->write) (tty, "\015\012", 2); } static int __init print_string_init(void) { println("Simulating the keypress of an A"); ch=65; tty_insert_flip_char(tty, ch, 0); con_schedule_flip(tty); return 0; } static void __exit print_string_exit(void) { println("Hasta la vista"); } module_init(print_string_init); module_exit(print_string_exit);
Memory reader
This reads memory address 0x100 and writes into the dmesg-syslog.
#include <linux/module.h> #define NAME "inter.c" int init_module() { int i=0x100; printk("i is at address %i; ",i); printk("i is %i",&i); return 0; } void cleanup_module() { } MODULE_LICENSE("GPL");
reboot
Sometimes you need to trigger a reboot with your kernel module, e.g. if you want to write a keyboard driver and have a reset-key handy. This module does nothing but trigger a reboot:
#include <linux/module.h> #include <linux/reboot.h> #define NAME "inter4.c" int init_module() { emergency_restart(); return 0; } void cleanup_module() { } MODULE_LICENSE("GPL");
See also
- http://tldp.org/LDP/lkmpg/2.6/html/
- http://www.xml.com/ldd/chapter/book/ch06.html#t1 (jiffies)
- http://kernelnewbies.org/Drivers
- http://ezs.kr.hsnr.de/TreiberBuch/html/ (complete book in german)