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 ” ?

[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 &amp;amp;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"); [/bash]

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

[c] 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 [/c]

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