What is Requesting and Freeing IRQ Lines in Linux Device Drivers (2026 Guide)-Free Linux Kernel Development Course

Requesting and Freeing IRQ Lines in Linux Device Drivers (2026 Guide)
Free Linux Kernel Programming Course — Interrupt Handling Module, Lecture 1
Kernel Version: 6.x
Reading Time: 12 min
Level: Beginner to Intermediate

← Previous Lecture  |  Next Lecture →

If you are learning Linux device drivers, one of the very first “real” hardware concepts you run into is the interrupt request line, usually shortened to IRQ. In this free Linux kernel programming course lecture, you will learn exactly how a modern Linux driver asks the kernel for an interrupt line, how it hands that line back, and why doing this correctly matters for stability. Every example here is written fresh for current 6.x kernels — no outdated APIs, no deprecated patterns.

Topics covered in this free Linux device drivers course lecture:
request_irq() devm_request_irq() free_irq() Interrupt Handler Function Linux Device Model probe() and remove()

What You Will Learn

  • What an IRQ line is and why a driver must “request” it before use
  • The exact signature and arguments of request_irq()
  • Why devm_request_irq() is the preferred, modern way to allocate an interrupt
  • Where in a driver’s lifecycle you should request and free an interrupt
  • How to correctly release an interrupt line with free_irq()
  • A complete, working, minimal interrupt-driven platform driver example

Prerequisites

Before this lecture, you should already be comfortable with:

  • Writing and building a basic Linux Kernel Module (LKM)
  • The idea of the Linux Device Model (LDM) and what probe()/remove() do
  • Basic C pointers and function pointers

If you haven’t covered these yet, go through our earlier free Linux kernel development course lectures on kernel modules first.

Why a Driver Must Request an IRQ Line

A CPU has a limited number of interrupt lines. Multiple peripherals — a touchscreen controller, a UART, a GPIO expander — may all want to signal the CPU when something happens. The kernel keeps a table mapping IRQ numbers to the handler function that should run when that line fires. Before your driver’s handler can be called, it has to register itself against a specific IRQ number. This registration step is exactly what request_irq() (or its safer modern cousin, devm_request_irq()) does.

Interrupt Registration Flow
Peripheral Hardware → Interrupt Controller → Registered Driver Handler
The driver only receives step 3 after a successful call to request_irq() / devm_request_irq()

The request_irq() API Explained

The classic function signature looks like this:

int request_irq(unsigned int irq,
                 irq_handler_t handler,
                 unsigned long flags,
                 const char *name,
                 void *dev_id);
Parameter Meaning
irqThe IRQ number to hook, usually obtained from the device tree or a resource lookup
handlerPointer to your interrupt handler function
flagsA bitmask controlling sharing and threading behavior (covered in the next lecture)
nameA short identifying string shown in /proc/interrupts
dev_idA unique token passed back to your handler; required when the line is shared

On success it returns 0; on failure it returns a negative error code such as -EBUSY if the line is already taken by another driver that didn’t allow sharing.

Why Modern Drivers Prefer devm_request_irq()

Older training material (and many older textbooks) teach plain request_irq() paired with a matching free_irq() call in the driver’s remove() function. That pattern still works, but it is easy to get wrong: if your probe() function fails after the interrupt has been requested, you must remember to unwind it manually, in the correct order, along with every other resource you allocated. Miss one path and you leak an IRQ line.

Modern Linux driver code (this has been the recommended style for a long time now, and remains so on current 6.x kernels) uses the device-managed variant instead:

int devm_request_irq(struct device *dev,
                      unsigned int irq,
                      irq_handler_t handler,
                      unsigned long flags,
                      const char *name,
                      void *dev_id);

It behaves identically to request_irq(), except the kernel automatically calls free_irq() for you when the associated struct device is removed. This eliminates an entire category of cleanup bugs and is the style you should default to for any platform, I2C, SPI, or USB driver you write today.

Where to Call It: probe() Is the Right Place

For any driver written against the modern Linux Device Model, the interrupt should be requested inside the probe() callback — the function the kernel calls once your driver is matched to a device. This is true whether it’s a platform driver, an I2C client driver, or an SPI driver.

Freeing the IRQ Line with free_irq()

If you used plain request_irq(), you are responsible for calling its counterpart when the driver is unloaded or the device is disconnected:

void free_irq(unsigned int irq, void *dev_id);

Two rules matter here:

  • The dev_id you pass to free_irq() must be the exact same pointer you originally passed to request_irq() — this is how the kernel identifies which registration to remove, especially on a shared line.
  • free_irq() blocks until any interrupt handler currently executing on that line has finished, so never call it while holding a lock your handler also needs.

If you used devm_request_irq(), you generally don’t need to call free_irq() at all — the device-managed core does it for you automatically in the correct order relative to your other devm_* resources.

Complete Minimal Example

Below is a small, original, self-contained platform driver skeleton showing the modern pattern end to end. This is written fresh for this lecture and targets current 6.x kernel APIs.

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>

struct mydev_priv {
    int irq;
    struct device *dev;
};

static irqreturn_t mydev_irq_handler(int irq, void *dev_id)
{
    struct mydev_priv *priv = dev_id;

    dev_info(priv->dev, "interrupt fired on IRQ %d\n", irq);
    return IRQ_HANDLED;
}

static int mydev_probe(struct platform_device *pdev)
{
    struct mydev_priv *priv;
    int ret;

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->dev = &pdev->dev;
    priv->irq = platform_get_irq(pdev, 0);
    if (priv->irq < 0)
        return priv->irq;

    ret = devm_request_irq(&pdev->dev, priv->irq,
                            mydev_irq_handler, 0,
                            "mydev-irq", priv);
    if (ret) {
        dev_err(priv->dev, "failed to request IRQ: %d\n", ret);
        return ret;
    }

    platform_set_drvdata(pdev, priv);
    dev_info(priv->dev, "probed successfully, IRQ %d ready\n", priv->irq);
    return 0;
}

static int mydev_remove(struct platform_device *pdev)
{
    /* devm_request_irq() cleans up automatically - nothing to do here */
    return 0;
}

static const struct of_device_id mydev_of_match[] = {
    { .compatible = "epathashala,mydev" },
    { }
};
MODULE_DEVICE_TABLE(of, mydev_of_match);

static struct platform_driver mydev_driver = {
    .probe  = mydev_probe,
    .remove = mydev_remove,
    .driver = {
        .name = "mydev",
        .of_match_table = mydev_of_match,
    },
};
module_platform_driver(mydev_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Minimal interrupt driven platform driver example");

Common Mistakes and Troubleshooting Tips

  • Mismatched dev_id: Passing a different pointer to free_irq() than the one used in request_irq() causes the kernel to fail to locate the registration, especially on shared lines.
  • Calling free_irq() from interrupt context: This function can sleep while waiting for a running handler to finish, so it must only be called from process context.
  • Forgetting IRQF_SHARED on a shared line: If two devices share a physical IRQ line, both drivers must set this flag or the second request_irq() call will fail with -EBUSY.
  • Checking /proc/interrupts: Run cat /proc/interrupts after loading your module to confirm the IRQ count is incrementing when the hardware event occurs.

Best Practices

  • Default to devm_request_irq() unless you have a specific reason to manage the interrupt lifetime manually.
  • Keep the handler function short; do the minimal work needed to acknowledge the hardware and defer heavier processing (covered in later lectures on threaded interrupts and workqueues).
  • Always check the return value of request_irq() / devm_request_irq() and fail probe() gracefully.
  • Give your interrupt a descriptive name string so it’s easy to identify in /proc/interrupts on a busy embedded system.

Key Takeaways

  • An IRQ line must be requested before the kernel will call your handler.
  • request_irq() is the classic API; devm_request_irq() is the modern, self-cleaning equivalent used in almost all current drivers.
  • Request the interrupt in probe(); if you used the non-devm variant, release it with free_irq() in remove().
  • free_irq() must be called from process context and blocks until any in-flight handler completes.

Frequently Asked Questions

Q1. What is the difference between request_irq() and devm_request_irq()?
Both register an interrupt handler for a given IRQ number. The devm_ variant additionally ties the interrupt’s lifetime to the driver’s struct device, so the kernel frees it automatically, removing the need for a manual free_irq() call.

Q2. Do I still need free_irq() if I use devm_request_irq()?
No, not in the normal case. The device-managed core calls it for you when the device is removed or probe fails partway through.

Q3. What happens if request_irq() is called twice for the same non-shared IRQ?
The second call fails and returns -EBUSY because the line is already owned by the first caller.

Q4. Can I call free_irq() from inside an interrupt handler?
No. It must be called from process context because it can block while waiting for a running handler on that line to finish.

Q5. How do I check whether my IRQ handler is actually being triggered?
Use cat /proc/interrupts before and after triggering the hardware event and confirm the counter for your IRQ number increases.

Q6. What does dev_id actually get used for?
It’s an opaque pointer the kernel hands back to your handler unchanged. On a shared IRQ line it’s also how the kernel tells apart the handlers registered by different drivers.

Continue the Free Linux Kernel Programming Course

Next up: understanding every interrupt flag in linux/interrupt.h and what each one actually changes about your driver’s behavior.

← Previous Lecture  |  Next Lecture →

2 Comments

Leave a Reply

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