// SPDX-License-Identifier: GPL-2.0 // SPDX-FileCopyrightText: Copyright 2025 Collabora ltd. //! This module provides types like [`Registration`] and //! [`ThreadedRegistration`], which allow users to register handlers for a given //! IRQ line. use core::marker::PhantomPinned; use crate::alloc::Allocator; use crate::device::{Bound, Device}; use crate::devres::Devres; use crate::error::to_result; use crate::irq::flags::Flags; use crate::prelude::*; use crate::str::CStr; use crate::sync::Arc; /// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`]. #[repr(u32)] pub enum IrqReturn { /// The interrupt was not from this device or was not handled. None = bindings::irqreturn_IRQ_NONE, /// The interrupt was handled by this device. Handled = bindings::irqreturn_IRQ_HANDLED, } /// Callbacks for an IRQ handler. pub trait Handler: Sync { /// The hard IRQ handler. /// /// This is executed in interrupt context, hence all corresponding /// limitations do apply. /// /// All work that does not necessarily need to be executed from /// interrupt context, should be deferred to a threaded handler. /// See also [`ThreadedRegistration`]. fn handle(&self, device: &Device) -> IrqReturn; } impl Handler for Arc { fn handle(&self, device: &Device) -> IrqReturn { T::handle(self, device) } } impl Handler for Box { fn handle(&self, device: &Device) -> IrqReturn { T::handle(self, device) } } /// # Invariants /// /// - `self.irq` is the same as the one passed to `request_{threaded}_irq`. /// - `cookie` was passed to `request_{threaded}_irq` as the cookie. It is guaranteed to be unique /// by the type system, since each call to `new` will return a different instance of /// `Registration`. #[pin_data(PinnedDrop)] struct RegistrationInner { irq: u32, cookie: *mut c_void, } impl RegistrationInner { fn synchronize(&self) { // SAFETY: safe as per the invariants of `RegistrationInner` unsafe { bindings::synchronize_irq(self.irq) }; } } #[pinned_drop] impl PinnedDrop for RegistrationInner { fn drop(self: Pin<&mut Self>) { // SAFETY: // // Safe as per the invariants of `RegistrationInner` and: // // - The containing struct is `!Unpin` and was initialized using // pin-init, so it occupied the same memory location for the entirety of // its lifetime. // // Notice that this will block until all handlers finish executing, // i.e.: at no point will &self be invalid while the handler is running. unsafe { bindings::free_irq(self.irq, self.cookie) }; } } // SAFETY: We only use `inner` on drop, which called at most once with no // concurrent access. unsafe impl Sync for RegistrationInner {} // SAFETY: It is safe to send `RegistrationInner` across threads. unsafe impl Send for RegistrationInner {} /// A request for an IRQ line for a given device. /// /// # Invariants /// /// - `ìrq` is the number of an interrupt source of `dev`. /// - `irq` has not been registered yet. pub struct IrqRequest<'a> { dev: &'a Device, irq: u32, } impl<'a> IrqRequest<'a> { /// Creates a new IRQ request for the given device and IRQ number. /// /// # Safety /// /// - `irq` should be a valid IRQ number for `dev`. pub(crate) unsafe fn new(dev: &'a Device, irq: u32) -> Self { // INVARIANT: `irq` is a valid IRQ number for `dev`. IrqRequest { dev, irq } } /// Returns the IRQ number of an [`IrqRequest`]. pub fn irq(&self) -> u32 { self.irq } } /// A registration of an IRQ handler for a given IRQ line. /// /// # Examples /// /// The following is an example of using `Registration`. It uses a /// [`Completion`] to coordinate between the IRQ /// handler and process context. [`Completion`] uses interior mutability, so the /// handler can signal with [`Completion::complete_all()`] and the process /// context can wait with [`Completion::wait_for_completion()`] even though /// there is no way to get a mutable reference to the any of the fields in /// `Data`. /// /// [`Completion`]: kernel::sync::Completion /// [`Completion::complete_all()`]: kernel::sync::Completion::complete_all /// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion /// /// ``` /// use kernel::c_str; /// use kernel::device::{Bound, Device}; /// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration}; /// use kernel::prelude::*; /// use kernel::sync::{Arc, Completion}; /// /// // Data shared between process and IRQ context. /// #[pin_data] /// struct Data { /// #[pin] /// completion: Completion, /// } /// /// impl irq::Handler for Data { /// // Executed in IRQ context. /// fn handle(&self, _dev: &Device) -> IrqReturn { /// self.completion.complete_all(); /// IrqReturn::Handled /// } /// } /// /// // Registers an IRQ handler for the given IrqRequest. /// // /// // This runs in process context and assumes `request` was previously acquired from a device. /// fn register_irq( /// handler: impl PinInit, /// request: IrqRequest<'_>, /// ) -> Result>> { /// let registration = Registration::new(request, Flags::SHARED, c_str!("my_device"), handler); /// /// let registration = Arc::pin_init(registration, GFP_KERNEL)?; /// /// registration.handler().completion.wait_for_completion(); /// /// Ok(registration) /// } /// # Ok::<(), Error>(()) /// ``` /// /// # Invariants /// /// * We own an irq handler whose cookie is a pointer to `Self`. #[pin_data] pub struct Registration { #[pin] inner: Devres, #[pin] handler: T, /// Pinned because we need address stability so that we can pass a pointer /// to the callback. #[pin] _pin: PhantomPinned, } impl Registration { /// Registers the IRQ handler with the system for the given IRQ number. pub fn new<'a>( request: IrqRequest<'a>, flags: Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> impl PinInit + 'a { try_pin_init!(&this in Self { handler <- handler, inner <- Devres::new( request.dev, try_pin_init!(RegistrationInner { // INVARIANT: `this` is a valid pointer to the `Registration` instance cookie: this.as_ptr().cast::(), irq: { // SAFETY: // - The callbacks are valid for use with request_irq. // - If this succeeds, the slot is guaranteed to be valid until the // destructor of Self runs, which will deregister the callbacks // before the memory location becomes invalid. // - When request_irq is called, everything that handle_irq_callback will // touch has already been initialized, so it's safe for the callback to // be called immediately. to_result(unsafe { bindings::request_irq( request.irq, Some(handle_irq_callback::), flags.into_inner(), name.as_char_ptr(), this.as_ptr().cast::(), ) })?; request.irq } }) ), _pin: PhantomPinned, }) } /// Returns a reference to the handler that was registered with the system. pub fn handler(&self) -> &T { &self.handler } /// Wait for pending IRQ handlers on other CPUs. /// /// This will attempt to access the inner [`Devres`] container. pub fn try_synchronize(&self) -> Result { let inner = self.inner.try_access().ok_or(ENODEV)?; inner.synchronize(); Ok(()) } /// Wait for pending IRQ handlers on other CPUs. pub fn synchronize(&self, dev: &Device) -> Result { let inner = self.inner.access(dev)?; inner.synchronize(); Ok(()) } } /// # Safety /// /// This function should be only used as the callback in `request_irq`. unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void) -> c_uint { // SAFETY: `ptr` is a pointer to `Registration` set in `Registration::new` let registration = unsafe { &*(ptr as *const Registration) }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq // callback is running implies that the device has not yet been unbound. let device = unsafe { registration.inner.device().as_bound() }; T::handle(®istration.handler, device) as c_uint } /// The value that can be returned from [`ThreadedHandler::handle`]. #[repr(u32)] pub enum ThreadedIrqReturn { /// The interrupt was not from this device or was not handled. None = bindings::irqreturn_IRQ_NONE, /// The interrupt was handled by this device. Handled = bindings::irqreturn_IRQ_HANDLED, /// The handler wants the handler thread to wake up. WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD, } /// Callbacks for a threaded IRQ handler. pub trait ThreadedHandler: Sync { /// The hard IRQ handler. /// /// This is executed in interrupt context, hence all corresponding /// limitations do apply. All work that does not necessarily need to be /// executed from interrupt context, should be deferred to the threaded /// handler, i.e. [`ThreadedHandler::handle_threaded`]. /// /// The default implementation returns [`ThreadedIrqReturn::WakeThread`]. #[expect(unused_variables)] fn handle(&self, device: &Device) -> ThreadedIrqReturn { ThreadedIrqReturn::WakeThread } /// The threaded IRQ handler. /// /// This is executed in process context. The kernel creates a dedicated /// `kthread` for this purpose. fn handle_threaded(&self, device: &Device) -> IrqReturn; } impl ThreadedHandler for Arc { fn handle(&self, device: &Device) -> ThreadedIrqReturn { T::handle(self, device) } fn handle_threaded(&self, device: &Device) -> IrqReturn { T::handle_threaded(self, device) } } impl ThreadedHandler for Box { fn handle(&self, device: &Device) -> ThreadedIrqReturn { T::handle(self, device) } fn handle_threaded(&self, device: &Device) -> IrqReturn { T::handle_threaded(self, device) } } /// A registration of a threaded IRQ handler for a given IRQ line. /// /// Two callbacks are required: one to handle the IRQ, and one to handle any /// other work in a separate thread. /// /// The thread handler is only called if the IRQ handler returns /// [`ThreadedIrqReturn::WakeThread`]. /// /// # Examples /// /// The following is an example of using [`ThreadedRegistration`]. It uses a /// [`Mutex`](kernel::sync::Mutex) to provide interior mutability. /// /// ``` /// use kernel::c_str; /// use kernel::device::{Bound, Device}; /// use kernel::irq::{ /// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn, /// ThreadedRegistration, /// }; /// use kernel::prelude::*; /// use kernel::sync::{Arc, Mutex}; /// /// // Declare a struct that will be passed in when the interrupt fires. The u32 /// // merely serves as an example of some internal data. /// // /// // [`irq::ThreadedHandler::handle`] takes `&self`. This example /// // illustrates how interior mutability can be used when sharing the data /// // between process context and IRQ context. /// #[pin_data] /// struct Data { /// #[pin] /// value: Mutex, /// } /// /// impl ThreadedHandler for Data { /// // This will run (in a separate kthread) if and only if /// // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by /// // default. /// fn handle_threaded(&self, _dev: &Device) -> IrqReturn { /// let mut data = self.value.lock(); /// *data += 1; /// IrqReturn::Handled /// } /// } /// /// // Registers a threaded IRQ handler for the given [`IrqRequest`]. /// // /// // This is executing in process context and assumes that `request` was /// // previously acquired from a device. /// fn register_threaded_irq( /// handler: impl PinInit, /// request: IrqRequest<'_>, /// ) -> Result>> { /// let registration = /// ThreadedRegistration::new(request, Flags::SHARED, c_str!("my_device"), handler); /// /// let registration = Arc::pin_init(registration, GFP_KERNEL)?; /// /// { /// // The data can be accessed from process context too. /// let mut data = registration.handler().value.lock(); /// *data += 1; /// } /// /// Ok(registration) /// } /// # Ok::<(), Error>(()) /// ``` /// /// # Invariants /// /// * We own an irq handler whose cookie is a pointer to `Self`. #[pin_data] pub struct ThreadedRegistration { #[pin] inner: Devres, #[pin] handler: T, /// Pinned because we need address stability so that we can pass a pointer /// to the callback. #[pin] _pin: PhantomPinned, } impl ThreadedRegistration { /// Registers the IRQ handler with the system for the given IRQ number. pub fn new<'a>( request: IrqRequest<'a>, flags: Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> impl PinInit + 'a { try_pin_init!(&this in Self { handler <- handler, inner <- Devres::new( request.dev, try_pin_init!(RegistrationInner { // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance. cookie: this.as_ptr().cast::(), irq: { // SAFETY: // - The callbacks are valid for use with request_threaded_irq. // - If this succeeds, the slot is guaranteed to be valid until the // destructor of Self runs, which will deregister the callbacks // before the memory location becomes invalid. // - When request_threaded_irq is called, everything that the two callbacks // will touch has already been initialized, so it's safe for the // callbacks to be called immediately. to_result(unsafe { bindings::request_threaded_irq( request.irq, Some(handle_threaded_irq_callback::), Some(thread_fn_callback::), flags.into_inner(), name.as_char_ptr(), this.as_ptr().cast::(), ) })?; request.irq } }) ), _pin: PhantomPinned, }) } /// Returns a reference to the handler that was registered with the system. pub fn handler(&self) -> &T { &self.handler } /// Wait for pending IRQ handlers on other CPUs. /// /// This will attempt to access the inner [`Devres`] container. pub fn try_synchronize(&self) -> Result { let inner = self.inner.try_access().ok_or(ENODEV)?; inner.synchronize(); Ok(()) } /// Wait for pending IRQ handlers on other CPUs. pub fn synchronize(&self, dev: &Device) -> Result { let inner = self.inner.access(dev)?; inner.synchronize(); Ok(()) } } /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. unsafe extern "C" fn handle_threaded_irq_callback( _irq: i32, ptr: *mut c_void, ) -> c_uint { // SAFETY: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq // callback is running implies that the device has not yet been unbound. let device = unsafe { registration.inner.device().as_bound() }; T::handle(®istration.handler, device) as c_uint } /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. unsafe extern "C" fn thread_fn_callback(_irq: i32, ptr: *mut c_void) -> c_uint { // SAFETY: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq // callback is running implies that the device has not yet been unbound. let device = unsafe { registration.inner.device().as_bound() }; T::handle_threaded(®istration.handler, device) as c_uint }