Difference between revisions of "My Tutorials/Writing Linux kernel modules"

From ThorstensHome
Jump to: navigation, search
(http://www.linuxquestions.org/questions/programming-9/no-rule-to-make-target-%60missing-syscalls-stop-812911/)
(Keyboard wire watcher)
Line 62: Line 62:
 
= Keyboard wire watcher =
 
= Keyboard wire watcher =
  
This kernel module watches, after it is loaded, for some seconds what comes from the keyboard and displays this.
+
This kernel module watches, after it is loaded, for some seconds what comes from the keyboard and displays this. It works with Linux 2.6 kernels, not with newer ones.
  
 
'''keyb.c'''
 
'''keyb.c'''

Revision as of 09:50, 19 November 2012


Contents

wait a second

This kernel module waits a second when it is initialized. You can modify it and use it e.g. to output something for one second just to test if that works.

the source code

driver.c

#include <linux/module.h>

int init_module()
{
  printk("Starting driver...");
  unsigned long j;
  j=jiffies;
  while (jiffies-j<=HZ)
  {}
  printk("done\n");
  return 0;
}
 
void cleanup_module()
{
}
MODULE_LICENSE("GPL");

Makefile

obj-m += driver.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

# modinfo driver.ko
filename:       driver.ko
license:        GPL
srcversion:     CEF3E0AB0FE04A24F00B7A6
depends:        
vermagic:       3.1.10-1.16-desktop SMP preempt mod_unload modversions 
# strings driver.ko
Starting driver...
done
license=GPL
srcversion=CEF3E0AB0FE04A24F00B7A6
depends=
vermagic=3.1.10-1.16-desktop SMP preempt mod_unload modversions 
module_layout
jiffies
printk
driver

Keyboard wire watcher

This kernel module watches, after it is loaded, for some seconds what comes from the keyboard and displays this. It works with Linux 2.6 kernels, not with newer ones.

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)->ops->write) (my_tty,str,strlen(str));
        ((my_tty->driver)->ops->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