Friday, April 18, 2014

Interrupts in Linux

What is an interrupt in linux?
       An interrupt is simply a signal that the hardware can send when it wants the processor’s attention.
Why we need interrupt?
        We need interrupt to save the CPU cycle. For an instance, we have a keyboard, when we press any key, we need response, CPU can’t wait for our input all the time. So the keyboard driver registers an interrupt handler, so that we press a key, the interrupt will signal to CPU.
How to register interrupt hanlder?
Interupt handler must be register in kernel module. we can register the handler in init or open. I would suggest to put it in open().
Here is the example:
int request_irq (
unsigned int  
irq,

irq_handler_t  
handler,

unsigned long  
irqflags,

const char *  
devname,

void *  
dev_id);



void my_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    printk("Interrupt should be handled there\n");
}

static int __init
my_init(void)
{
    unsigned int irq;
    unsigned int irqflags;
    int ret;

    irq=68;
    irqflags=IRQF_SHARED | IRQF_NO_SUSPEND;
    ret = request_irq(irq, my_interrupt,
            irqflags, "my_dev", NULL);
    if (ret!=0) {
            printk("ERROR: Cannot request IRQ %d", irq);
           
    }
}
static void __exit
my_exit(void)
{
    unsigned int irq;
    irq=68;
    free_irq(irq, NULL);
    printk("CLCDINT_EXIT\n");
}


Parameters for request_irq:

unsigned int irq
          The interrupt number being requested.

irq_hander_t
          The pointer to the handling function being installed.

unsigned long flags
          The bit mask, to manage the interrupt

const char *dev_name
          The string passed to request_irq is used in /proc/interrupts to show the owner of the interrupt

void *dev_id
          The set to NULL, if the interrupt line is not shared, if interrupt line is shared then, it has the pointer to provate data to identify which device is interrupting.

unsigned long flags: SA_INTERRUPT
          When set, this indicates a “fast” interrupt handler. Fast handlers are executed with interrupts disabled on the current processor.

unsigned long flags: SA_SHIRQ
          This bit signals that the interrupt can be shared between devices.

unsigned long flags: SA_SAMPLE_RANDOM
          This bit indicates that the generated interrupts can contribute to the entropy pool used by /dev/random and /dev/urandom. These devices return truly random numbers when read and are designed to help application software choose secure keys for encryption. Such random numbers are extracted from an entropy pool that is contributed by various random events. If your device generates interrupts at truly random times, you should set this flag. 



Wednesday, April 9, 2014

Simple char driver and user space application

Is there any different between kernel 2.4 and kernel 2.6 for writing the kernel module?

       Yes, there are differents. In kernel 2.4 we use register_chrdev() to allocate and register the file operations structure. But this method is history now. Below are the type and functions introduced in kernel 2.6

dev_t, register_chrdev_region, alloc_chrdev_region.


Why we don’t use register_chrdev()?

register_chrdev() still works in kernel 2.6, but the problem in kernel 2.4 was, we have limited major number allocation. We had only 8bit for both major and minor number. So total we could have only 255 major or minor numbers. Where as in new methods, we have 32 bit for major and minor numbers.(12 bits for major, 20 bits for minor).

What is the different between register_chrdev_region and alloc_chrdev_region?

        register_chrdev() basically allocates major number and registers the file operation structure. But register_chrdev_region and alloc_chrdev_region only allocates major number. (register_chrdev_region allocates static major number. Alloc_chrdev_region allocates dynamic major number).


What is MAJOR and MINOR numbers?

        In unix everything is file, the kernel device is also consider as file aka special file. These special files are located in /dev.
You can do open, read, write, these devices, by doing this you can really access the devices.

        Ok, Now consider you have five devices say simple timer, each timer has one register, by reading that you can see the different time zone.

        We need only one driver, but five devices./dev/time1, /dev/time2, so on. So when you open /dev/time1 to read the india time. Your kernel module open should call, because there must be many kernel module running in our kernel, and each has open call.

        In our kernel module we register our fops structure with our own major number. And by doing mknod /dev/time1 c 249 1 we map the node to major number.

        249 1- /dev/time1 - our fops

         
1 is minor number, because we five devices so minor would be 1, 2, 3, 4, 5.
Main purpose of major number is, we can have many devices with the same major number.

Simple driver with static major no allocation.
#include <linux/module.h>
#include <linux/kernel.h>       //for printk
#include <linux/init.h>         //for __init __exit
#include <linux/fs.h>           //for file_operations

dev_t dev;

static int __init mymodule_init(void)
{
        int ret_val;
        dev = MKDEV(248, 0);
        ret_val = register_chrdev_region(dev, 1, "mydev");
        if(ret_val < 0){
                printk("\n Error: register_chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");

output:

make
insmod test_module.ko
dmesg | tail -20

Major No: 248 minor_no : 0


What is dev_t?

       dev_t is the data type introduced in kerne 2.6. its 32 bit, first 12 bits holds the major number and remaining 20 bits holds the minor number.
Example:
        dev_t dev;
        dev = 0x 0F800001
        MAJOR(dev) = 0x0F800001 >> 20 = 0x0F8 = 248
        MINOR(dev) = 0x0F800001  & 0x000FFFFF = 0x1 = 1
What is the maximum major number?

       dev_t can hold 12 bits for major number so we can have 4095 major number, if we give more than that, then kernel will allocate random number.

What are the parameter for register_chrdev_region()?

dev_t, number of consecutive major numbers, dev name.


Can I give any number to for major number allocation, I mean less than 4095?

No, you can see /proc/devices to know the major number which was already allocated, so you should not give the same number which was already allocated. You can choose any number which was not allocated.

How do I cross compile this module for powerpc?

Cross compile is bit different, then user space program. We need to have changes in makefile

Makefile:

obj-m := test_module.o
all:
        #make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
        make -C /vobs/linux_2_6_34/ M=$(PWD) modules

clean:
        #make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
        make -C /vobs /linux_2_6_34/ M=$(PWD) clean


the lines commented out is for x86. Basically we need to give a kernel path which was compiled for powerpc.

Compiling also bit different, refer below.


make CROSS_COMPILE=/vobs/toolchains/ppc/gcc-3.4.6-glibc-2.3.6/powerpc-linux/bin/powerpc-linux- ARCH=powerpc 


How can I allocate the major number dynamically?

        You can allocate the major number and minor number dynamically during the execution. Refer below code.

#include <linux/module.h>
#include <linux/kernel.h>       //for printk
#include <linux/init.h>         //for __init __exit
#include <linux/fs.h>           //for file_operations

dev_t dev;

static int __init mymodule_init(void)
{
        int ret_val;
        ret_val = alloc_chrdev_region(&dev, 0, 1, "mydev_dynamic");
        if(ret_val < 0){
                printk("\n Error: alloc__chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");

output
make
insmod test_module.ko
dmesg | tail -1
 Major No: 226 Minor_no : 0


alloc_chrdev_region() allocates dynamic major number. It accepts dev_t, starting minor number, no.of minors, device name.


Does alloc_chrdev_region() or register_chrdev_region() register the file operation structure?

        No, these two function just allocates major number and registers the device, whereas the old register_chrdev() allocates major number and registers the file operations structure and the device.

What is file operations structure?

        File operations structure is available in linux/fs.h, this structure has the field for open, read, write callbacks ponters. By registering this structure in your kernel, you can access the devices from the user space program.

How to register file operations structure with the kernel?

        Below is code.

#include  linux/module.h
#include linux/kernel.h       //for printk
#include linux/init.h        //for __init __exit
#include linux/fs.h           //for file_operations
#include linux/cdev.h         //for cdev struct

dev_t dev;
struct cdev *my_cdev;

static int my_open(struct inode *inode, struct file  *file)
{
        printk("\n my_open \n");
        return 0;
}


static struct file_operations fops = {
        .open = my_open
};

static int __init mymodule_init(void)
{
        int ret_val;
        ret_val = alloc_chrdev_region(&dev, 0, 1, "mydev_dynamic");
        if(ret_val < 0){
                printk("\n Error: alloc__chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));

        my_cdev = cdev_alloc();
        my_cdev->owner = THIS_MODULE;
        my_cdev-&gtops = &fops;

        cdev_add(my_cdev, dev, 1);
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        cdev_del(my_cdev);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");


What is cdev?

        cdev is newly introduced in kernel 2.6. This cdev structure is an internal representation of a char device. It has a member field as struct file operations pointer. You have to create and populate your file operations structure and assign the address of that structure to this member.

# include
struct cdev
{
    struct kobject kobj;
    struct module* owner;
    const struct file_operations* ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};







What is cdev_init and cdev_alloc?

cdev_alloc() allocates cdev object and return to you. Then you need to give fops pointer
cdev_init() will be used, if you manually allocate using kmalloc, in this way you don’t need to assign fops manualy.
Example:
1)
        struct cdev *cdev;
        cdev = cdev_alloc();
        cdev->fops = &fops
               
2)
                struct foo {
                struc cdev cdev;
                int  flag;
        };

        foo f ;
                f= kmalloc(100);
                cdev_init(f.cdev, &fops);          
   
        Here is the complete kernel module and the user application.

#include <linux/module.h>
#include <linux/kernel.h>       //for printk
#include <linux/init.h>         //for __init __exit
#include <linux/fs.h>           //for file_operations
#include <linux/cdev.h>         //for cdev struct
#include <linux/types.h>        //for ssize_t
#include <linux/slab.h>         //for kmalloc
#include <asm/uaccess.h>        //for copy_to_user
#define ADD 0x1
#define SUB 0x2
#define MUL 0x3

dev_t dev;
struct cdev *my_cdev;
char *ptr;
struct foo {
        int a;
        int b;
        int c;
};

static int my_open(struct inode *inode, struct file  *file)
{
        printk("\n Device Major : %d \n", imajor(inode));
        printk("\n Device Minor : %d \n", iminor(inode));
        ptr = kmalloc(10, GFP_KERNEL);
        return 0;
}
static int my_release(struct inode *inode, struct file *file)
{
        kfree(ptr);
        printk("\n Device Released and freed memory \n");
        return 0;
}
static ssize_t my_read(struct file *filp,char *buffer,
                                size_t length,loff_t *offset)
{
        copy_to_user(buffer, ptr, length);
        return 0;
}
static ssize_t my_write(struct file *filp, const char *buffer,
                                size_t length, loff_t *offset)
{
        printk("\n Receiced String :%s \n", buffer);
        copy_from_user(ptr, buffer, length);
        return length;
}
static long my_ioctl(struct file *filp, unsigned int cmd,
                                        unsigned long args)
{
        struct foo f;
        switch(cmd){
                case ADD:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = f.a + f.b;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;
                case SUB:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = f.a - f.b;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;
                case MUL:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = f.a * f.b;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;
                default:
                        copy_from_user(&f, (struct foo *)args, sizeof(struct foo));
                        f.c = 0;
                        copy_to_user((struct foo *)args, &f, sizeof(struct foo));
                break;

        }
        return 0;
}

static struct file_operations fops = {
        .open = my_open,
        .read = my_read,
        .write = my_write,
        .release = my_release,
        .unlocked_ioctl = my_ioctl
};

static int __init mymodule_init(void)
{
        int ret_val;
        ret_val = alloc_chrdev_region(&dev, 0, 1, "mydev_dynamic");
        if(ret_val < 0){
                printk("\n Error: alloc__chrdev_region \n");
                return -1;
        }
        printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev), MINOR(dev));

        my_cdev = cdev_alloc();
        my_cdev->owner = THIS_MODULE;
        my_cdev->ops = &fops;

        cdev_add(my_cdev, dev, 1);
        return 0;
}

static void __exit mymodule_exit(void)
{
        unregister_chrdev_region(dev, 1);
        cdev_del(my_cdev);
        printk ("Unloading my module.\n");
        return;
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");


User Application:


#include<stdio.h>
#include<fcntl.h>
#define ADD 0x1
#define SUB 0x2
#define MUL 0x3
#define UNKNOWN 0x4

struct foo{
        int a;
        int b;
        int c;
};
int main()
{
struct foo f;
int fd;
char buff[15];
fd = open("/dev/mydev_dynamic", O_RDWR);
write(fd, "Hello world", 12);
read(fd, buff, 12);
printf("%s", buff);
f.a = 15;
f.b = 10;
ioctl(fd, ADD, &f);
printf("\n Add : %d \n", f.c);
ioctl(fd, SUB, &f);
printf("\n Sub : %d \n", f.c);
ioctl(fd, MUL, &f);
printf("\n mul : %d \n", f.c);
ioctl(fd, UNKNOWN, &f);
printf("\n Unknown : %d \n", f.c);
close(fd);
return 0;
}


Output:

./a.out
Hello world
 Add : 25

 Sub : 5

 mul : 150

 Unknown : 0

dmesg | tail -10

 Major No: 226 Minor_no : 0

 Device Major : 226

 Device Minor : 0

 Receiced String :Hello world


 Device Released and freed memory


How does the open call in user context move to kernel context?

Basically, the open call triggers the soft interrupt we say that 0x80, this interrupt is for system call(). This is initialized in the interrupt vector table during set_system_gate (0x80, &system_call. This system call interrupt forces the context switch.


Why copy_to_user or copy_from_user? Can we use memcpy?

Hmm, I guess the memcpy also works fine, but the main reason for copy_to_user or copy_from_user is mainly for protection. We don’t want a user space program to make a panic in kernel. copy_to_user or copy_from_user checks the memory before read or write.