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

From ThorstensHome
Jump to: navigation, search
(Keyboard wire watcher)
(How to use)
Line 34: Line 34:
  
 
== How to use ==
 
== How to use ==
  insmod keyb
+
  insmod keyb.ko
  
 
== Get rid of it ==
 
== Get rid of it ==

Revision as of 21:38, 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.ko

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. You will need a PS/2 keyboard, not a USB one. It works with Linux 2.6 kernels.

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