Implementation of Interrupt Handler (IRQ) in Linux kernel driver

Following driver implements, how we can allocate an interrupt line using request_irq kernel API and how to implement an interrupt handler ( e.g. my_interrupt_handlerin this example ) and how to pass a structure with some values from module to IRQ handler.

This code is almost self explainatory with added comments inline. If you want to know ” What is the difference between setup_irq and request_irq in Linux kernel interrupts ” ?

[bash]
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
 
// execute it as
// sudo insmod ./interrupt.ko interupt_name="some_name" irq=1
// for testing use irq numbers from cat /proc/interrupts
 
// remove module as
// sudo rmmod interrupt
 
static int irq;
static char *interrupt_name;
 
module_param(interrupt_name, charp,0);
MODULE_PARM_DESC(interrupt_name, "Name for the Interrupt");
module_param(irq, int, 0);
MODULE_PARM_DESC(irq, "The IRQ of the network interface");
 
struct info_to_pass {
    int *some_int;
    char *some_string;
}dev_info;
 
static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
    static int mycount = 0;
    // info is what we passed as &dev_info to request_irq
    // this variable is just a argument carrier from module_init to IRQ handler.
    struct info_to_pass *info = dev_id;
 
    // static counter to just print debug message 10 times,
    // saying we received interrupt, after 10, we don't print anything
    // but interrupt might have came.
    if (mycount < 10) {
        printk("We received from module init : %s\n", info->some_string);
        printk("Interrupt! = %d\n", mycount);
        mycount++;
    }
 
    return IRQ_HANDLED;
}
 
static int __init mymodule_init(void) {
    printk ("My module initialised!\n");
 
/* request_irq: allocate a given interrupt line */
// int request_irq(unsigned int irq,
//                irq_handler_t handler,
//                unsigned long flags,
//                const char *name,
//                void *dev)
    dev_info.some_string = "helloworld";
 
    if (request_irq(irq, &my_interrupt_handler, IRQF_SHARED, interrupt_name, &dev_info)) {
        printk(KERN_ERR "myirqtest: cannot register IRQ %d\n", irq);
        return -EIO;
    }
 
//The second parameter, handler, is a function pointer to the actual interrupt handler that services this interrupt. This function is invoked whenever the operating system receives the interrupt.
 
// IRQF_SHARED. This flag specifies that the interrupt line can be shared among multiple interrupt handlers. Each handler registered on a given line must specify this flag; otherwise, only one handler can exist per line.
 
    printk("Request on IRQ %d succeeded\n", irq);
    return 0;
}
 
static void __exit mymodule_exit(void) {
    free_irq(irq, &dev_info);
    printk("Freeing IRQ %d\n", irq);
 
    printk ("Unloading my module.\n");
    return;
}
 
module_init(mymodule_init);
module_exit(mymodule_exit);
 
MODULE_LICENSE("GPL");

The Simple makefile to compile this driver on Linux laptop is as follows,

obj-m += interrupt.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

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

Compile the driver as,

 $ make 
 $ sudo insmod ./interrupt.ko interrupt_name=somename irq=30 

Currently we have chosen random shared IRQ number 30 for testing to make sure we receive actual interrupt ( As in our driver, we dont consider real hardware interrupt with unique IRQ pin ) from /proc/interrupt

More Reference – https://notes.shichao.io/lkd/ch7/

Leave a Comment