Allocate an interrupt line using request_irq and Implementation of IRQ / Interrupt Handler in Linux kernel device 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 ” ?

#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 choosen 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

READ  USB - Physical

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

Leave a Reply

Your email address will not be published. Required fields are marked *