Linux PCI Driver Registration Guide-Free Linux Device Drivers Training Online

PREV_LEC  |  NEXT_LEC

Linux PCI Driver Registration Guide

Part of EmbeddedPathashala’s free Linux kernel development course — from matched device to a running, DMA-capable PCI driver

If you’ve followed this free Linux device drivers course so far, your struct pci_driver already knows how to match a device using struct pci_device_id. But matching a device is not the same as running against it. This lecture covers Linux PCI driver registration end to end: how pci_register_driver() hooks your driver into the PCI core, how a matched device is safely enabled, and how bus mastering is turned on so your device can perform DMA. We’ll build everything against the latest mainline kernel APIs, including the devres-managed enable path that most modern drivers now use, and finish with an original driver you can build and load on QEMU’s edu test device.

What You Will Learn

  • How pci_register_driver() and module_pci_driver() connect your driver to the PCI core
  • The correct, safe order of operations from probe to remove
  • The difference between pci_enable_device(), pci_enable_device_mem(), and pci_enable_device_io()
  • Why current kernels prefer the devres-managed pcim_enable_device()
  • How and when to enable bus mastering with pci_set_master()
  • How to build, load, and verify an original PCI driver on QEMU

Prerequisites

  • The previous lecture on struct pci_device_id and struct pci_driver matching (see PREV_LEC)
  • Comfort writing and loading basic Linux kernel modules
  • QEMU with the edu educational PCI device enabled, as used earlier in this free embedded Linux course

From Matching to Running: The PCI Driver Lifecycle

Once the PCI core matches your pci_device_id table against a physical or virtual device, it calls your driver’s probe() function with a pointer to the corresponding struct pci_dev. Everything from that point forward — enabling the device, mapping its BARs, requesting an IRQ, enabling DMA — happens inside that single function call. Getting the order wrong is one of the most common sources of PCI driver bugs, so it’s worth internalizing the sequence before writing a single line of code.

PCI Driver Lifecycle
module_pci_driver() registers pci_driver | v PCI core matches pci_device_id –> calls probe(pdev) | v pcim_enable_device(pdev) [enable BARs, wake from D3 if needed] | v pci_set_master(pdev) [only if the device performs DMA] | v driver requests IRQ, maps BARs, registers with subsystem | v … device is operational … | v remove(pdev) called on unbind/module unload | v pci_clear_master() (if set) –> device disabled automatically | v module_pci_driver() unregisters pci_driver

Registering the Driver with pci_register_driver()

The PCI core doesn’t know your driver exists until you register it. pci_register_driver() takes a pointer to your populated struct pci_driver and adds it to the core’s internal driver list, immediately triggering a matching pass against every currently enumerated PCI device.

static int __init ep_pci_init(void)
{
    int ret;

    ret = pci_register_driver(&ep_pci_driver);
    if (ret)
        pr_err("ep_pci: registration failed (%d)\n", ret);

    return ret;
}

static void __exit ep_pci_exit(void)
{
    pci_unregister_driver(&ep_pci_driver);
}

module_init(ep_pci_init);
module_exit(ep_pci_exit);

pci_register_driver() returns 0 on success or a negative error code on failure — always check it, since a silent registration failure means your probe() will simply never be called, with no other symptom. On unload, pci_unregister_driver() must be called with the same struct pci_driver pointer, or the kernel may later try to use a driver whose module no longer exists.

Reducing Boilerplate with module_pci_driver()

The register/unregister pair above is so common that the kernel provides a single macro, module_pci_driver(), which generates both the module_init() and module_exit() wrappers for you. Almost every modern PCI driver in the mainline kernel tree uses this instead of writing the pair out by hand.

module_pci_driver(ep_pci_driver);

This one line replaces the entire ep_pci_init() / ep_pci_exit() pair shown above. It is safer in practice because it removes the possibility of a developer adding a registration call and forgetting the matching unregister call during a later refactor.

ApproachLines of boilerplateRisk
Manual init/exit~10Register/unregister can drift out of sync
module_pci_driver()1Always paired correctly by construction

Enabling the PCI Device

Before your driver can touch a PCI device — even just to read its configuration registers for anything beyond identification — the device must be explicitly enabled. This step asks the platform’s low-level PCI code to turn on I/O and memory decoding for the device’s Base Address Registers (BARs), and it also wakes the device if it was left in a low-power PCI power state.

static int ep_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    int ret;

    ret = pci_enable_device(pdev);
    if (ret) {
        dev_err(&pdev->dev, "cannot enable PCI device: %d\n", ret);
        return ret;
    }

    /* ... rest of probe ... */
    return 0;
}

Two narrower variants exist for cases where you don’t want to touch both BAR types:

  • pci_enable_device_mem() — enables only memory-mapped BARs, leaving I/O port BARs untouched
  • pci_enable_device_io() — enables only I/O port BARs

Internally, every enable call increments an enable_cnt reference counter on the struct pci_dev. Calling any enable variant multiple times is safe — only the first call actually touches the hardware, and the device stays enabled until every caller has issued a matching disable.

The Modern Way: Managed Enable with pcim_enable_device()

Plain pci_enable_device() has one recurring problem: if a later step in probe() fails, the driver must remember to call pci_disable_device() on every error path, or the device stays enabled after a failed probe. Current kernels solve this with the devres (device resource management) framework. pcim_enable_device() enables the device exactly like pci_enable_device(), but registers the corresponding disable call with the device’s managed-resource list, so it is automatically undone when the device is unbound — even if probe() returns an error partway through.

static int ep_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    int ret;

    ret = pcim_enable_device(pdev);
    if (ret) {
        dev_err(&pdev->dev, "pcim_enable_device failed: %d\n", ret);
        return ret;
    }

    /* No matching pci_disable_device() needed on error paths or in remove() */
    return 0;
}

Because of this automatic cleanup, most new mainline PCI drivers use pcim_enable_device() in place of the raw pci_enable_device(). It’s worth knowing both: the managed variant for new code, and the plain variant because you’ll still see it throughout older, still-maintained drivers.

APICleanup on error/removeTypical use
pci_enable_device()Manual — you must call pci_disable_device()Older drivers, fine-grained control
pcim_enable_device()Automatic via devresNew drivers (recommended default)

Turning the Device Off Safely: pci_disable_device()

If you used the plain (non-managed) enable call, your remove() function is responsible for disabling the device with pci_disable_device(). This also clears bus mastering if it was active. Just like enabling, disabling is reference counted — the device is not actually turned off in hardware until every matching enable call has been balanced by a disable call.

static void ep_pci_remove(struct pci_dev *pdev)
{
    pci_clear_master(pdev);
    pci_disable_device(pdev);
}

Bus Mastering and DMA Capability

By PCI’s design, a device can initiate its own transactions on the bus — but only once it becomes a “bus master.” This is controlled by a single bit in the device’s configuration space command register. If your driver intends to use DMA (letting the device read or write system memory directly, rather than the CPU shuttling every byte), that bit must be set explicitly.

pci_set_master(pdev);   /* enable bus mastering / DMA */
...
pci_clear_master(pdev); /* disable it again, e.g. in remove() */

pci_set_master() also triggers any architecture-specific setup needed to allow the device to master the bus. If your device never performs DMA, skip this call entirely — enabling bus mastering unnecessarily widens the device’s ability to write to system memory, which matters for both stability and security, especially on systems without IOMMU isolation for that device.

Original Demo: ep_pci_lifecycle_demo Driver

The following original driver ties every API from this lecture together. It targets QEMU’s edu educational PCI device (vendor 0x1234, device 0x11e8), which is safe to enable and bus-master against in a virtual machine without touching real hardware.

#include <linux/module.h>
#include <linux/pci.h>

#define EP_EDU_VENDOR_ID 0x1234
#define EP_EDU_DEVICE_ID 0x11e8

static int ep_pci_lifecycle_probe(struct pci_dev *pdev,
                                   const struct pci_device_id *id)
{
    int ret;

    ret = pcim_enable_device(pdev);
    if (ret) {
        dev_err(&pdev->dev, "enable failed: %d\n", ret);
        return ret;
    }

    pci_set_master(pdev);

    dev_info(&pdev->dev,
             "ep_pci_lifecycle_demo: bound to %04x:%04x, bus mastering ON\n",
             pdev->vendor, pdev->device);

    return 0;
}

static void ep_pci_lifecycle_remove(struct pci_dev *pdev)
{
    pci_clear_master(pdev);
    dev_info(&pdev->dev, "ep_pci_lifecycle_demo: removed, bus mastering OFF\n");
}

static const struct pci_device_id ep_pci_lifecycle_ids[] = {
    { PCI_DEVICE(EP_EDU_VENDOR_ID, EP_EDU_DEVICE_ID) },
    { }
};
MODULE_DEVICE_TABLE(pci, ep_pci_lifecycle_ids);

static struct pci_driver ep_pci_lifecycle_driver = {
    .name     = "ep_pci_lifecycle_demo",
    .id_table = ep_pci_lifecycle_ids,
    .probe    = ep_pci_lifecycle_probe,
    .remove   = ep_pci_lifecycle_remove,
};

module_pci_driver(ep_pci_lifecycle_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala PCI driver lifecycle demo");

Build and Run Walkthrough

# Makefile
obj-m += ep_pci_lifecycle_demo.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
$ make
$ sudo insmod ep_pci_lifecycle_demo.ko
$ dmesg | tail -n 3
[  102.552013] ep_pci_lifecycle_demo 0000:00:04.0: ep_pci_lifecycle_demo: bound to 1234:11e8, bus mastering ON

$ sudo rmmod ep_pci_lifecycle_demo
$ dmesg | tail -n 1
[  118.771200] ep_pci_lifecycle_demo 0000:00:04.0: ep_pci_lifecycle_demo: removed, bus mastering OFF

If insmod reports no matching device, confirm QEMU was started with -device edu and that lspci -nn | grep 1234:11e8 shows the device before loading the module.

Common Mistakes and Troubleshooting

  • Forgetting to check the return value of pci_enable_device(). A failed enable followed by BAR access will typically hang or fault.
  • Calling pci_set_master() when the device never does DMA. This needlessly widens the device’s memory access and can mask real bugs elsewhere.
  • Mixing managed and unmanaged calls. Don’t call plain pci_disable_device() against a device enabled with pcim_enable_device() unless you deliberately understand the devres interaction — let the managed cleanup do its job.
  • Unbalanced enable/disable counts across multiple code paths, leaving enable_cnt non-zero and the device stuck “enabled” after what looks like a clean unload.

Best Practices and Security Considerations

  • Prefer pcim_enable_device() in new drivers so error paths can’t leak an enabled-but-abandoned device.
  • Only call pci_set_master() for devices that genuinely need DMA — pair it with proper DMA masks and, where available, IOMMU-backed isolation.
  • Always pair module_pci_driver() style registration rather than hand-rolled init/exit unless you need custom logic at load time.
  • Log enable/disable and bus-master transitions with dev_info() during development — they’re invaluable when debugging probe-order bugs.
Interview Questions
  • What does pci_register_driver() actually do internally?
  • Why does the kernel provide module_pci_driver() instead of requiring manual init/exit?
  • What is the difference between pci_enable_device() and pci_enable_device_mem()?
  • How does pcim_enable_device() avoid resource leaks on a failed probe?
  • What hardware-level effect does pci_set_master() have?
  • Why is enable_cnt reference counted rather than a simple boolean?

Summary and Key Takeaways

Registering a Linux PCI driver is a small, well-defined sequence: pci_register_driver() (or the module_pci_driver() shortcut) wires your driver into the core, pcim_enable_device() safely turns the device on with automatic cleanup, and pci_set_master() is reserved strictly for devices that need DMA. Getting this order right — and preferring the managed devres APIs — removes an entire category of probe-error and resource-leak bugs from your driver. In the next lecture of this free Linux kernel development course, we move from lifecycle management into reading and writing the device’s configuration space directly.

FAQ

What happens if I forget to call pci_enable_device()?

Accessing BARs or performing I/O against a device that was never enabled leads to undefined behavior — commonly a hang, a bus error, or silently reading garbage, because the platform never turned on memory/I/O decoding for that device.

Is pcim_enable_device() a drop-in replacement for pci_enable_device()?

Functionally yes for enabling the device, but it also registers automatic disable-on-detach cleanup through devres, so you should generally stop calling pci_disable_device() manually once you switch to it.

Do I always need module_pci_driver()?

No — if your module needs extra setup or teardown logic beyond registering the PCI driver, write explicit init/exit functions. module_pci_driver() is a convenience for the common case of pure PCI driver registration.

When should I call pci_set_master()?

Only when your device performs DMA — that is, when the device itself needs to read or write system memory directly rather than being driven purely through programmed I/O or MMIO register access.

What does the enable_cnt field actually track?

It’s an internal reference count on struct pci_dev incremented by every pci_enable_device() variant and decremented by every pci_disable_device() call, ensuring the device stays enabled as long as any caller still needs it.

Can pci_enable_device() fail on real hardware?

Yes — for example if BAR resource assignment failed earlier during boot, or if the device cannot be woken from a low-power state. Always check and propagate the return value.

Continue the Free Linux Kernel Development Course

Next up: reading and writing PCI configuration space registers directly.

Next Lecture Browse All Lectures

PREV_LEC  |  NEXT_LEC

Leave a Reply

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