Documentation:NGW/KernelModule

From AVRFreaks Wiki

Jump to: navigation, search

Contents

[edit] Kernel Module

I am writting this as I go as this could take 2 to 3 month. Need to get a GPS track system up and running. 90 percent done, but it always the last 10 percent of debuging with takes most of the time

[edit] Why do I need to write a Kernel Module

If you are coming from an AVR 8-bit world then it will seem very strange that you cannot just directly access internal registers that control peripherals like a UART. When running Linux, a user program CANNOT write to the sections of memory that control such peripherals. The Kernel has exclusive control over such areas of memory.

You may be asking why not. Well the Linux system that is running on the AVR32 is a real linux kernel. Which means it comes from the same code base as that running your full Linux server. As you can imagine with a critical server, you do not what a user's program messing up the Linux kernel controlling the server because it has access to the internal registers which the kernel is trying to control.

I could go on with why but it better if you go to the following web site and read it all. Note that some of the code examples have errors, which I have fixed, I hope and put here. Note that some of the code is out of date. i.e. the /proc code looks like it been updated in the kernel. Which means the example code on the website will not work.

http://www.faqs.org/docs/kernel/index.html

[edit] What you need

  • A PC running Linux with buildroot on it and known to be working i.e. you are able to compile your own Kernel (this takes some time).
  • A NGW100. (I like to have TFTP and NFS set-up so I can change the code in the server and then reboot the NGW100 and see what happens or just move the file onto the nfs location and run it on the NGW100 with no FTP or messing about with progaming Flash).
  • A Makefile and code see below

[edit] Make File

This is bassed of the code I found at the following web site. http://pastebin.ca/804215


Note that buildroot is always change so the paths to the CROSS_COMPLIE and KDIR may have moved. So if the makefile below does a find -name avr32-linux-gcc for the CROSS_COMPLIE and find -name linux-2.6* and look for the kernel you are using. This should be done from the buildroot directory.

You need the full path in the makefile. So you will need to edit it before using it. You do not have to be root to make the modules.


Makefile

# Makefile:
ARCH := avr32
CROSS_COMPILE := /home/user/buildroot/build_avr32/staging_dir/usr/bin/avr32-linux-
KDIR := /home/user/buildroot/project_build_avr32/atngw100/linux-2.6.24
PWD := $(shell pwd)
 
obj-m := testmodule.o

default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
 
clean:
        rm -rf testmodule.ko testmodule.o* testmodule.mod* .testmodule* .tmp* Module*

Note that you need to put a tab and NOT 8 space after default: and clean:. The make will tell you this if you just cut and passed as I can not put tab in wiki.

[edit] Hello World

You need to use the make file above and this file to make hello world module.

testmodule.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>
 
int init_module(void)
{
       printk("hello\n");
       return 0;
}
 
void cleanup_module(void)
{
       printk("world\n");
}


[edit] How to Make

I just use the command

make default

# make default
make -C /home/user/buildroot/project_build_avr32/atngw100/linux-2.6.24   
SUBDIRS=/home/user/ATNGW100 ARCH=avr32 CROSS_COMPILE=/home/user/buildroot/
build_avr32/staging_dir/usr/bin/avr32-linux- modules
make[1]: Entering directory `/home/user/buildroot/project_build_avr32/atngw100/linux- 2.6.24'
 CC [M]  /home/user/ATNGW100/testmodule.o
 Building modules, stage 2.
 MODPOST 1 modules
 CC      /home/user/ATNGW100/testmodule.mod.o
 LD [M]  /home/user/ATNGW100/testmodule.ko
make[1]: Leaving directory `/home/user/buildroot/project_build_avr32/atngw100/linux-2.6.24'

you can do a make clean to clean up the directory when you are done. This will delete the output file as well.

Note that make will give you the following message and will NOT do any thing. So do a make default

make: Nothing to be done for `Makefile'.

[edit] Run Time

You need to FTP or move the testmodule.ko to the NGW100. It can go anywhere for now. I like putting it in /

Note you should be on the console and be login as root. I have had problem with the insmon command from a ssh console, but I think it fix now with 2.6.24.4.


You need to use the following commands lsmod, insmod and rmmod.


NOTE: The logs may only come out on the concole so it best to run it from there in the NGW100 case it the RS232 port.

Note that the kernel needs to match or you will get the following

~ # insmod  ./testmodule.ko
testmodule: version magic '2.6.24 mod_unload AVR32v1' should be '2.6.24.4 mod_unload  AVR32v1'
insmod: cannot insert './testmodule.ko': invalid module format


~ # lsmod
Module                  Size  Used by    Not tainted
xt_state                1856  1
iptable_filter          2112  1
ipt_MASQUERADE          2368  1
iptable_nat             4900  1
nf_nat                 12278  2 ipt_MASQUERADE,iptable_nat
nf_conntrack_ipv4      11048  3 iptable_nat
nf_conntrack           42652  5   xt_state,ipt_MASQUERADE,iptable_nat,nf_nat,nf_conntrack_ipv4
ip_tables               8040  2 iptable_filter,iptable_nat
configfs               17552  1


~ # insmod ./testmodule.ko


~ # lsmod
Module                  Size  Used by    Tainted: P
testmodule               928  0 
xt_state                1856  1
iptable_filter          2112  1
ipt_MASQUERADE          2368  1
iptable_nat             4900  1
nf_nat                 12278  2 ipt_MASQUERADE,iptable_nat
nf_conntrack_ipv4      11048  3 iptable_nat
nf_conntrack           42652  5 xt_state,ipt_MASQUERADE,iptable_nat,nf_nat,nf_conntrack_ipv4
ip_tables               8040  2 iptable_filter,iptable_nat
configfs               17552  1


~ # rmmod testmodule.ko
~ # lsmod
Module                  Size  Used by    Not tainted
xt_state                1856  1
iptable_filter          2112  1
ipt_MASQUERADE          2368  1
iptable_nat             4900  1
nf_nat                 12278  2 ipt_MASQUERADE,iptable_nat
nf_conntrack_ipv4      11048  3 iptable_nat
nf_conntrack           42652  5   xt_state,ipt_MASQUERADE,iptable_nat,nf_nat,nf_conntrack_ipv4
ip_tables               8040  2 iptable_filter,iptable_nat
configfs               17552  1


# tail /var/log/messages
Jul 10 19:12:24 ngw user.warn kernel: hello
Jul 10 19:49:49 ngw user.warn kernel: world

[edit] Making a Device

OK time to make a device. You can use the same makefile just change the the code to testmodule.c file

testmodule.c

/*  chardev.c: Creates a read-only char device that says how many times
 *  you've read from the dev file
 */
 
       #if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
          #include <linux/modversions.h>
          #define MODVERSIONS
       #endif
       #include <linux/kernel.h>
       #include <linux/module.h>
       #include <linux/fs.h>
       #include <asm/uaccess.h>  /* for put_user */
 
       /*  Prototypes - this would normally go in a .h file
        */
       int init_module(void);
       void cleanup_module(void);
       static int device_open(struct inode *, struct file *);
       static int device_release(struct inode *, struct file *);
       static ssize_t device_read(struct file *, char *, size_t, loff_t *);
       static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
 
       #define SUCCESS 0
       #define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices   */
       #define BUF_LEN 80            /* Max length of the message from the device */
 
       /* Global variables are declared as static, so are global within the file. */
 
       static int Major;            /* Major number assigned to our device driver */
       static int Device_Open = 0;  /* Is device open?  Used to prevent multiple  */
                                    /*   access to the device                       */
       static char msg[BUF_LEN];    /* The msg the device will give when asked    */
       static char *msg_Ptr;

       static struct file_operations fops = {
         .read = device_read,
         .write = device_write,
         .open = device_open,
         .release = device_release
       };
 
       /*                   Functions
        */
 
       int init_module(void)
       {
          Major = register_chrdev(0, DEVICE_NAME, &fops);
 
          if (Major < 0) {
            printk ("Registering the character device failed with %d\n", Major);
            return Major;
          }
 
          printk("<1>I was assigned major number %d.  To talk to\n", Major);
          printk("<1>the driver, create a dev file with\n");
                printk("<1>'mknod /dev/hello c %d 0'.\n", Major);
          printk("<1>Try various minor numbers.  Try to cat and echo to\n");
                printk("<1>the device file.\n");
          printk("<1>Remove the device file and module when done.\n");
 
          return 0;
       }
 
       void cleanup_module(void)
       {
           printk("Cleanup time\n");

          /* Unregister the device */
          unregister_chrdev(Major, DEVICE_NAME);
          //int ret = unregister_chrdev(Major, DEVICE_NAME);
          //if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret);
       }
 
       /*                   Methods
        */
 
       /* Called when a process tries to open the device file, like
        * "cat /dev/mycharfile"
        */
       static int device_open(struct inode *inode, struct file *file)
       {
          static int counter = 0;
          if (Device_Open) return -EBUSY;
          Device_Open++;
          sprintf(msg,"I already told you %d times Hello world!\n", counter++);
          msg_Ptr = msg;
          //MOD_INC_USE_COUNT;
 
         return SUCCESS;
       }
 
       /* Called when a process closes the device file.
        */
       static int device_release(struct inode *inode, struct file *file)
       {
          Device_Open --;     /* We're now ready for our next caller */
 
          /* Decrement the usage count, or else once you opened the file, you'll
                   never get get rid of the module. */
          //MOD_DEC_USE_COUNT;
 
          return 0;
       }
 
       /* Called when a process, which already opened the dev file, attempts to
          read from it.
       */
       static ssize_t device_read(struct file *filp,
          char *buffer,    /* The buffer to fill with data */
          size_t length,   /* The length of the buffer     */
          loff_t *offset)  /* Our offset in the file       */
       {
          /* Number of bytes actually written to the buffer */
          int bytes_read = 0;
 
          /* If we're at the end of the message, return 0 signifying end of file */
          if (*msg_Ptr == 0) return 0;
 
          /* Actually put the data into the buffer */
          while (length && *msg_Ptr)  {
 
               /* The buffer is in the user data segment, not the kernel segment;
                * assignment won't work.  We have to use put_user which copies data from
                * the kernel data segment to the user data segment. */
             put_user(*(msg_Ptr++), buffer++);
 
             length--;
             bytes_read++;
          }
 
          /* Most read functions return the number of bytes put into the buffer */
          return bytes_read;
       }
 
       /*  Called when a process writes to dev file: echo "hi" > /dev/hello */
       static ssize_t device_write(struct file *filp,
          const char *buff,
                size_t len,
                loff_t *off)
       {
          printk ("<1>Sorry, this operation isn't supported.\n");
          return -EINVAL;
       }

When you do a insmod testmodule.ko and look at the messages you will see it tells you do a mknod. This will make the device in the /dev directory


~ # insmod testmodule.ko
I was assigned major number 253.  To talk to
the driver, create a dev file with
'mknod /dev/hello c 253 0'.
Try various minor numbers.  Try to cat and echo to
the device file.
Remove the device file and module when done.


Note that this time it printed out on the console as well as going to the message file. This is triggered by the <1> at the start of the printk. i.e. printk("<1>I was assigned major number %d. To talk to\n", Major);


from the message logs

~ # tail /var/log/messages
Jul 10 19:50:06 ngw user.alert kernel: I was assigned major number 253.  To talk to
Jul 10 19:50:06 ngw user.alert kernel: the driver, create a dev file with
Jul 10 19:50:06 ngw user.warn kernel: 'mknod /dev/hello c 253 0'.
Jul 10 19:50:06 ngw user.alert kernel: Try various minor numbers.  Try to cat and echo to
Jul 10 19:50:06 ngw user.warn kernel: the device file.
Jul 10 19:50:06 ngw user.alert kernel: Remove the device file and module when done.

So make the nod

mknod /dev/hello c 253 0
~ # ls /dev/
console     log         mtd2        mtdblock2   rtc0        ttyS0
core        mem         mtd2ro      mtdblock3   shm         ttygserial
fd          mtd0        mtd3        null        stderr      urandom
full        mtd0ro      mtd3ro      ptmx        stdin       watchdog
kmem        mtd1        mtdblock0   pts         stdout      zero
kmsg        mtd1ro      mtdblock1   random      tty
 
~ # mknod /dev/hello c 253 0  

~ # ls /dev/
console     kmsg        mtd1ro      mtdblock1   random      tty
core        log         mtd2        mtdblock2   rtc0        ttyS0
fd          mem         mtd2ro      mtdblock3   shm         ttygserial
full        mtd0        mtd3        null        stderr      urandom
hello       mtd0ro      mtd3ro      ptmx        stdin       watchdog
kmem        mtd1        mtdblock0   pts         stdout      zero


[edit] Time to try the device

Now to time to see if the device works

~ # cd /dev
/dev # cat hello
I already told you 0 times Hello world!
/dev # cat hello
I already told you 1 times Hello world!
/dev # cat hello
I already told you 2 times Hello world!
/dev # cat hello
I already told you 3 times Hello world!


And it works.

Note That is a read only deivce when you s a echo "hi"> /dev/hello you will get alart in the message file of

~ # echo "hi"> /dev/hello 
~ # tail /var/log/messages
Jul 10 19:55:57 ngw user.alert kernel: Sorry, this operation isn't supported.
Jul 10 19:55:57 ngw user.alert kernel: Sorry, this operation isn't supported.
Jul 10 19:55:57 ngw user.alert kernel: Sorry, this operation isn't supported.

Note you will get 3 lines, one for each char you sent. i.e. 'h', 'i' and '\n'

[edit] I will add the code for the write. It is coming. Need to test it.

Misc notes



As usual, /etc/init.d holds scripts which are executed during booting. These scripts have to be invoked explicitly from a file called rcS. Here is my init script:


insmod /media/mmcblk0p1/robodriver.ko
major=`grep freebird /proc/devices | awk '{print $1}'`
echo $major
mknod /tmp/freebird c $major 0
/media/mmcblk0p1/drive &

got this from http://avr32linux.org/twiki/bin/view/Main/PramodeCE

[edit] buildroot Rev

This Document is based on buildroot Rev 22403 (2.6.24.3) svn

$ svn info
Path: .
URL: svn://uclibc.org/trunk/buildroot
Repository Root: svn://uclibc.org
Repository UUID: 69ca8d6d-28ef-0310-b511-8ec308f3f277
Revision: 22403
Node Kind: directory
Schedule: normal
Last Changed Author: jacmet
Last Changed Rev: 22402
Last Changed Date: 2008-06-17 09:33:02 -0400 (Tue, 17 Jun 2008)
Personal tools