name: driver-interrupt-impl-cpp description: > Implement IRQ / interrupt handling in a C++ DFv2 driver. Use when a C++ driver must acquire a zx::interrupt (from pdev GetInterrupt or fuchsia.hardware.gpio/Gpio.GetInterrupt), listen with async::IrqMethod on its dispatcher, and acknowledge with interrupt_.ack() (using fit::defer to re-arm) -- including when interrupts stop firing after the first because ack was missed. For writing interrupt tests use the C++ interrupt testing skill; for Rust drivers use the Rust interrupt implementation skill.

Driver Interrupt Implementation (C++) (DFv2)

Dependencies

To use async::IrqMethod, zx::interrupt, and fit::defer, add the following to your build file (though some may be included transitively by other driver dependencies):

GN:

deps = [
  "//sdk/lib/async:async-cpp",  # For `async::IrqMethod`
  "//sdk/lib/fit",              # For `fit::defer`
  "//zircon/system/ulib/zx",    # For `zx::interrupt`
]

Bazel:

deps = [
    "@fuchsia_sdk//pkg/async-cpp",  # For `async::IrqMethod`
    "@fuchsia_sdk//pkg/fit",        # For `fit::defer`
    "@fuchsia_sdk//pkg/zx",         # For `zx::interrupt`
]

Acquire an Interrupt

Drivers typically acquire an interrupt object from a FIDL service.

If the interrupt is acquired from a standard FIDL service (e.g., GPIO):

A driver might request a GPIO interrupt via fuchsia.hardware.gpio/Gpio.GetInterrupt.

Otherwise (If the interrupt is defined via devicetree for a platform device):

The driver typically uses the Platform Device service to acquire it via methods like GetInterruptById or GetInterruptByName on the fuchsia.hardware.platform.device/Device protocol.

Lifecycle and Cleanup: No manual cleanup of the interrupt handle is necessary. The zx::interrupt wrapper manages the handle's lifecycle; its destructor automatically closes the handle when the object goes out of scope.

Listen to an Interrupt

  • Use async::IrqMethod to listen to interrupts.
  • It executes a callback on a dispatcher (usually the driver's dispatcher).
  • Ensure the callback does not block the dispatcher excessively if it's synchronized.
  • Use fit::defer to ensure the interrupt is acknowledged (interrupt_.ack()) even if errors occur, to re-arm the interrupt.

Implement an Interrupt Handler

// Contains `driver_base2.h`.
#include <lib/driver/component/cpp/driver_base2.h>

// Contains `async::IrqMethod`.
#include <lib/async/cpp/irq.h>

// Contains `fit::defer`.
#include <lib/fit/defer.h>

// Contains `zx::interrupt`.
#include <lib/zx/interrupt.h>

class MyDriver : public fdf::DriverBase2 {
 public:
  zx::result<> Start(fdf::DriverContext context) override {
    // ... Connect to FIDL service and get interrupt handle ...
    // interrupt_ = std::move(interrupt->value()->interrupt);

    interrupt_handler_.set_object(interrupt_.get());
    zx_status_t status = interrupt_handler_.Begin(dispatcher());
    if (status != ZX_OK) {
      return zx::error(status);
    }

    return zx::ok();
  }

 private:
  void HandleInterrupt(async_dispatcher_t* dispatcher, async::IrqBase* irq,
                       zx_status_t status, const zx_packet_interrupt_t* interrupt_packet) {
    if (status != ZX_OK) {
      return;
    }

    // Use defer to ensure the interrupt is acknowledged on all exit paths.
    // Failing to ack will prevent future interrupts from firing.
    auto ack_interrupt = fit::defer([this] {
      interrupt_.ack();
    });

    // Perform work in response to triggered interrupt.
  }

  zx::interrupt interrupt_;
  async::IrqMethod<MyDriver, &MyDriver::HandleInterrupt> interrupt_handler_{this};
};

Common Pitfalls

  • Forgetting to acknowledge the interrupt: Failing to call interrupt_.ack() will prevent the interrupt from triggering again. Use fit::defer as shown in the example to avoid this.
  • Blocking the dispatcher: The interrupt handler runs on the dispatcher passed to Begin(). If the handler blocks or performs heavy computation, it will starve other tasks on that dispatcher. Offload heavy work to a separate thread or use asynchronous primitives if necessary.

Further Reading