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/